53 lines
1.9 KiB
TypeScript
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;
|
|
}
|