Import cartridge .sav files with verified structure, checksums and clock (Closes #44)
Savegames → Import backup now reads an English Crystal .sav (from an emulator, a cartridge dump or the 3DS Virtual Console's sav.dat) and converts it into a native save after a preview. - world/cartridge-save.ts: picks the copy TryLoadSaveFile would load (check values and checksums of the main copy, else the backup), reads the saved WRAM block, the numbered boxes, mail backups, Hall of Fame and Battle Tower by PRET labels (SRAM, SAVE_BLOCK in the profile, checked by check:reference with the SAV constants), engine flags via EngineFlags, scenes via MapScenes, and the map's objects and masks. - Clock: an emulator MBC3 footer plus the time since it was written, FixDays/FixTime with wStart*, mapped to the minute and weekday offsets; without one, the device's time and the saved wCurDay. Day countdowns move with the day difference. - Refused with the reason: unported maps, the Bug-Catching Contest, a Battle Tower challenge, characters outside the naming screen. - Names keep <PK>/<MN> as ᴾᴷ/ᴹᴺ (one character, one tile); trainer names accept the naming screen's alphabet; boxed Eggs validate; native states up to 256 kB load (a full PC exceeded 64 kB). - Tests: the committed debug saves, layout offsets against the symbols, backup fallback, clock footers, day counters, refusals, slot session and the Savegames flow in Chromium and WebKit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
87b05b45f4
commit
592ca43a40
20 files changed
+1821
-40
No files matched your search
@@ -39,7 +39,7 @@ flowchart LR
|
||||
|
||||
Saves made during an unfinished walk or jump retain its source tile and completed step count, so loading cannot skip a landing event or map transition. Directional mats wait for exit input; doors, stairs and caves force an animated downward step after arrival.
|
||||
|
||||
Native and reference sessions use different IndexedDB databases and Web Locks. The native player keeps save slots (`storage/slots.ts`, `app/slot-session.ts`): each slot keeps its newest two records with checksums, written in one transaction, and loading falls back to the previous record when the newest is damaged. The singleton save of database version 1 becomes the first slot inside the version-2 upgrade. Imports are previewed in a scratch world and reject incompatible schemas and invalid positions before changing the world or any slot. A failed import, switch or new game restores the prior state. Original cartridge saves and emulator snapshots cannot be used as native saves.
|
||||
Native and reference sessions use different IndexedDB databases and Web Locks. The native player keeps save slots (`storage/slots.ts`, `app/slot-session.ts`): each slot keeps its newest two records with checksums, written in one transaction, and loading falls back to the previous record when the newest is damaged. The singleton save of database version 1 becomes the first slot inside the version-2 upgrade. Imports are previewed in a scratch world and reject incompatible schemas and invalid positions before changing the world or any slot. A failed import, switch or new game restores the prior state. Emulator snapshots cannot be used as native saves. Cartridge saves are converted, not loaded: `world/cartridge-save.ts` checks the size, the check values and checksums of both copies (taking the backup when the main copy fails, as `TryLoadSaveFile` does), reads the saved WRAM block, the numbered PC boxes, the mail backups, the Hall of Fame and the Battle Tower by PRET addresses (`SAVE_BLOCK`, `SRAM` in the profile), translates engine flags through the ROM's `EngineFlags` and scenes through `MapScenes`, and turns the cartridge clock (an emulator's MBC3 footer plus `wStart*`, as `FixDays`/`FixTime` do) into the port's minute and weekday offsets, moving stored day counters by the same amount. The result goes through the same preview and `restore` checks as a native backup.
|
||||
|
||||
`GameWorld` advances in overworld ticks of two video frames, as the original loop does. The runtime converts elapsed time to ticks. Tile events follow `CheckTileEvent`: a completed step checks connections, then warps, then coordinate events for the current scene. Idle ticks run the current scene script. Map entry runs the NEWMAP, TILES and OBJECTS callbacks on a copy first, so an unsupported callback refuses the step instead of entering a half-initialized map. Objects spawn from event flags at map load, and later flag changes leave them in place until `appear`/`disappear` or the next load.
|
||||
|
||||
|
||||
Reference in new issue
Block a user