Files
CONSOLGAMES/src/sim/save.ts
T

53 lines
1.9 KiB
TypeScript

import { GameState } from './state';
export interface SaveStorage {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
}
export type SaveSlot = 'current' | 'monthly';
export const SAVE_KEYS: Record<SaveSlot, string> = {
current: 'consolgames.save',
monthly: 'consolgames.save.monthly',
};
export function serialize(state: GameState): string {
return JSON.stringify(state);
}
const OBJECT_FIELDS = ['date', 'inventory', 'loyalty', 'day', 'prices', 'pendingOrders'] as const;
const NUMBER_FIELDS = ['cash', 'minutes', 'reputation', 'rngState', 'nextCustomerId', 'customerAccumulator', 'minuteAccumulator', 'negativeMonthEnds'] as const;
export function deserialize(json: string): GameState {
let data: unknown;
try {
data = JSON.parse(json);
} catch {
throw new Error('Corrupted save');
}
if (typeof data !== 'object' || data === null || Array.isArray(data)) throw new Error('Corrupted save');
const record = data as Record<string, unknown>;
if (record.saveVersion !== 1) throw new Error(`Unsupported save version: ${record.saveVersion}`);
for (const field of OBJECT_FIELDS) {
if (typeof record[field] !== 'object' || record[field] === null) throw new Error('Corrupted save');
}
for (const field of NUMBER_FIELDS) {
if (typeof record[field] !== 'number') throw new Error('Corrupted save');
}
if (typeof record.bankrupt !== 'boolean') throw new Error('Corrupted save');
return data as GameState;
}
/** Always writes the current save; also writes the monthly one when asked. */
export function saveGame(storage: SaveStorage, state: GameState, options: { monthly: boolean }): void {
const json = serialize(state);
storage.setItem(SAVE_KEYS.current, json);
if (options.monthly) storage.setItem(SAVE_KEYS.monthly, json);
}
export function loadGame(storage: SaveStorage, slot: SaveSlot): GameState | null {
const json = storage.getItem(SAVE_KEYS[slot]);
return json ? deserialize(json) : null;
}