keep imported save cache on export

Red restores the current-map cache from SRAM before Continue. Dropping the import template made a later export boot with zeroed map data.
This commit is contained in:
johnjohto
2026-07-29 14:01:19 -04:00
parent 73d81a72b8
commit bbe894fb4f
3 changed files with 18 additions and 9 deletions
+4 -5
View File
@@ -116,13 +116,12 @@ local function defaultsSave()
end
-- Merge a GenSave.decode() result over the new-game defaults, exactly the
-- way convert.lua did, then stamp the requested version. The 32768-byte
-- import template GenSave stashes as `rawImport` and the decode `warnings`
-- are dropped here: neither belongs in a serialized slot file (a fresh
-- export always starts zero-filled -- see GenSave.lua's header).
-- way convert.lua did, then stamp the requested version. Keep the imported
-- SRAM image with the slot: Pokémon Red restores its saved current-map cache
-- before Continue, and an export needs that unmodeled data to remain bootable.
-- Decode warnings are only import diagnostics and do not belong in the slot.
local function mergeDefaults(decoded, version)
decoded.warnings = nil
decoded.rawImport = nil
local save = defaultsSave()
for k, v in pairs(decoded) do save[k] = v end
save.lastHeal = { map = save.player.map, x = save.player.x, y = save.player.y }
+10 -1
View File
@@ -96,7 +96,14 @@ local function syntheticSave(name)
moves = { { id = "TACKLE", pp = 35, ppUps = 0 } },
nickname = "SQ", ot = name, otId = seed.player.id, catchRate = 45,
} }
return GenSave.encode(seed, data, nil)
-- The current-map view pointer is part of wMainData's map cache, not a
-- modeled save field. Pokémon Red restores that cache before Continue,
-- so it is a useful canary for the import -> slot -> export path.
local raw = GenSave.encode(seed, data, nil)
local cacheOff = OFF.mainData + 104
local cacheTemplate = raw:sub(1, cacheOff) .. string.char(0xA5)
.. raw:sub(cacheOff + 2)
return GenSave.encode(seed, data, cacheTemplate)
end
-- ---------------------------------------------- importToSlot -> listSlots
@@ -149,6 +156,8 @@ do
eq(outBytes and #outBytes, GenSave.SAVE_SIZE, "the export is exactly 32768 bytes")
check(outBytes and mainChecksumValid(outBytes),
"the export carries a valid main-data checksum")
eq(outBytes and outBytes:byte(OFF.mainData + 105), 0xA5,
"the export keeps the saved current-map cache")
-- the export re-imports to an equivalent save
local re = SaveConvert.importSav(outBytes, "red")
+4 -3
View File
@@ -297,9 +297,10 @@ check(scSave and scSave.lastHeal and scSave.lastHeal.map == scSave.player.map,
"SaveConvert.importSav: lastHeal derives from the decoded position")
check(scSave and scSave.lastOutdoor and scSave.lastOutdoor.id ~= nil,
"SaveConvert.importSav: lastOutdoor is set")
-- the import template + decode warnings never leak into the slot table
check(scSave and scSave.rawImport == nil and scSave.warnings == nil,
"SaveConvert.importSav: rawImport/warnings stripped from the returned table")
-- The original SRAM image carries the current-map cache that Red restores on
-- Continue, while decode warnings are only import diagnostics.
check(scSave and type(scSave.rawImport) == "string" and scSave.warnings == nil,
"SaveConvert.importSav: keeps the SRAM template but drops warnings")
-- size / type validation
local badSize, badSizeErr = SaveConvert.importSav("too short", 2)