diff --git a/src/save.ts b/src/save.ts index c147759..a0e80d8 100644 --- a/src/save.ts +++ b/src/save.ts @@ -1,7 +1,7 @@ import { getPlayerProgress } from "./playerprogress"; import { getStateManager } from "./statemanager"; import { getThralls } from "./thralls"; -import { SaveFileV1, StatCounterV1 } from "./saveformat"; +import { SaveFileV1, mustBeSaveFileV1 } from "./saveformat"; export interface SaveFile { version: string; @@ -10,159 +10,6 @@ export interface SaveFile { type SaveSlot = "FLEDGLING_SLOT_1" | "FLEDGLING_SLOT_2"; -/// Checks whether obj is a valid save file, as far as we can tell, and returns -/// it unchanged if it is, or throws an error if it's not valid. -export function mustBeSaveFileV1(obj: unknown): SaveFileV1 { - if (obj === undefined || obj === null) { - throw new Error("nonexistent"); - } - if (typeof obj !== "object") { - throw new Error(`not an object; was ${typeof obj}`); - } - - if (!("version" in obj)) { - throw new Error("no magic number"); - } - if (obj.version !== "fledgling_save_v1") { - throw new Error(`bad magic number: ${obj.version}`); - } - - return { - version: "fledgling_save_v1", - revision: mustGetNumber(obj, "revision"), - turn: mustGetNumber(obj, "turn"), - name: mustGetString(obj, "name"), - thrallTemplateId: mustGetNumber(obj, "thrallTemplateId"), - nImprovements: mustGetNumber(obj, "nImprovements"), - stats: mustGetStatCounterV1(obj, "stats"), - talents: mustGetStatCounterV1(obj, "talents"), - isInPenance: mustGetBoolean(obj, "isInPenance"), - wishId: mustGetNumber(obj, "wishId"), - exp: mustGetNumber(obj, "exp"), - blood: mustGetNumber(obj, "blood"), - itemsPurloined: mustGetNumber(obj, "itemsPurloined"), - skillsLearned: mustGetNumberArray(obj, "skillsLearned"), - untrimmedSkillsAvailableIds: mustGetNumberArray( - obj, - "untrimmedSkillsAvailableIds", - ), - thrallsUnlocked: mustGetNumberArray(obj, "thrallsUnlocked"), - thrallDamage: mustGetNumberArray(obj, "thrallDamage"), - thrallsObtainedItem: mustGetNumberArray(obj, "thrallsObtainedItem"), - thrallsDeliveredItem: mustGetNumberArray(obj, "thrallsDeliveredItem"), - }; -} - -function mustGetNumber(obj: object, key: string): number { - if (obj === null || obj === undefined) { - throw new Error("container absent"); - } - if (typeof obj !== "object") { - throw new Error(`container was not an object; was ${typeof obj}`); - } - if (!(key in obj)) { - throw new Error(`missing number: ${key}`); - } - const dict = obj as { [key: string]: any }; - const val = dict[key]; - if (typeof val !== "number") { - throw new Error(`not a number: ${key}: ${val}`); - } - return val; -} - -function mustGetString(obj: object, key: string): string { - if (obj === null || obj === undefined) { - throw new Error("container absent"); - } - if (typeof obj !== "object") { - throw new Error(`container was not an object; was ${typeof obj}`); - } - if (!(key in obj)) { - throw new Error(`missing number: ${key}`); - } - const dict = obj as { [key: string]: any }; - const val = dict[key]; - if (typeof val !== "string") { - throw new Error(`not a string: ${key}: ${val}`); - } - return val; -} - -function mustGetStatCounterV1(obj: object, key: string): StatCounterV1 { - if (obj === null || obj === undefined) { - throw new Error("container absent"); - } - if (typeof obj !== "object") { - throw new Error(`container was not an object; was ${typeof obj}`); - } - if (!(key in obj)) { - throw new Error(`missing number: ${key}`); - } - const dict = obj as { [key: string]: any }; - const val = dict[key]; - if (typeof val !== "object") { - throw new Error(`not an object: ${key}: ${val}`); - } - - try { - return { - agi: mustGetNumber(val, "agi"), - int: mustGetNumber(val, "int"), - cha: mustGetNumber(val, "cha"), - psi: mustGetNumber(val, "psi"), - }; - } catch (e) { - let message = "unrecognizable error"; - if (e instanceof Error) { - message = e.message; - } - throw new Error(`reading ${key}: ${message}`); - } -} - -function mustGetBoolean(obj: object, key: string): boolean { - if (obj === null || obj === undefined) { - throw new Error("container absent"); - } - if (typeof obj !== "object") { - throw new Error(`container was not an object; was ${typeof obj}`); - } - if (!(key in obj)) { - throw new Error(`missing number: ${key}`); - } - const dict = obj as { [key: string]: any }; - const val = dict[key]; - if (typeof val !== "boolean") { - throw new Error(`not boolean: ${key}: ${val}`); - } - return val; -} - -function mustGetNumberArray(obj: object, key: string): number[] { - if (obj === null || obj === undefined) { - throw new Error("container absent"); - } - if (typeof obj !== "object") { - throw new Error(`container was not an object; was ${typeof obj}`); - } - if (!(key in obj)) { - throw new Error(`missing number: ${key}`); - } - const dict = obj as { [key: string]: any }; - const val = dict[key]; - if (typeof val !== "object") { - throw new Error(`not an object: ${key}: ${val}`); - } - - for (const x of val) { - if (typeof x !== "number") { - throw new Error(`contained non-number item in ${key}: ${val}`); - } - } - return val; -} - /// The result of attempting to load a V1 save file. interface SaveFileV1LoadResult { // If present and valid, the loaded file.