mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 16:31:05 +02:00
153 lines
6.2 KiB
Lua
153 lines
6.2 KiB
Lua
-- The launcher's Export on a Gold save slot.
|
|
--
|
|
-- GOLD_CACHE="..." luajit tests/gen2_save_export_test.lua
|
|
--
|
|
-- src/save_convert/GenSave.lua is a Gen 1 codec: its whole offset table is
|
|
-- pokered's SRAM window, and a Gold save lives in a different one entirely
|
|
-- (pokegold ram/sram.asm sOptions/sCheckValue1/sPlayerData/sBox1-sBox14),
|
|
-- so no cart .sav can come out of a Gold slot yet. Export used to reach
|
|
-- the codec anyway and die inside GenSave.crosswalks: the Gen 2 generated
|
|
-- tables carry top-level provenance scalars (`generation`, `source` --
|
|
-- src/import/RomExtractorGen2.lua) beside the def rows, and indexing the
|
|
-- scalar raised "attempt to index local 'def' (a number value)", which the
|
|
-- launcher then printed as a red "encode failed:" traceback.
|
|
--
|
|
-- Two guarantees pinned here:
|
|
-- * the REAL launcher path (slot registry -> gen2 Save.save ->
|
|
-- SaveFileIO.exportActiveSlot) answers a plain "not supported yet"
|
|
-- message for Gold, in both directions, and writes no export file;
|
|
-- * GenSave.crosswalks skips non-table rows, so a data set that carries
|
|
-- provenance scalars can never crash the codec again.
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local S = require("tests.harness").suite("gen2 save export")
|
|
local check, eq = S.check, S.eq
|
|
|
|
love = love or require("tests.love_stub")
|
|
|
|
-- An isolated in-memory love.filesystem, the same shape
|
|
-- tests/engine/save_slots.lua isolates SaveData with: full-path keys, no
|
|
-- directory support (both writers guard their createDirectory calls).
|
|
local files = {}
|
|
love.filesystem = {
|
|
files = files,
|
|
write = function(path, content) files[path] = content return true end,
|
|
read = function(path) return files[path] end,
|
|
remove = function(path) files[path] = nil return true end,
|
|
getInfo = function(path)
|
|
if files[path] then return { type = "file" } end
|
|
return nil
|
|
end,
|
|
}
|
|
|
|
local SaveData = require("src.core.SaveData")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
local GoldSave = require("src.core.gen2.Save")
|
|
local SaveFileIO = require("src.import.SaveFileIO")
|
|
local SaveConvert = require("src.save_convert.SaveConvert")
|
|
local GenSave = require("src.save_convert.GenSave")
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- The real launcher path: a registered Gold slot holding a save the game's
|
|
-- own writer produced, exported through the same call the Export chip makes.
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
SaveData.resetSlotState()
|
|
GameVersion.set("gold")
|
|
|
|
local slotId = SaveData.createSlot("gold")
|
|
check(slotId ~= nil, "a gold slot registers")
|
|
SaveData.setActiveSlot("gold", slotId)
|
|
local save = GoldSave.newGame({ playerName = "BLAKE", rivalName = "SILVER" })
|
|
eq(GoldSave.save(save), true, "the gen2 writer fills the slot")
|
|
check(files["saves/gold/" .. tostring(slotId) .. ".lua"] ~= nil,
|
|
"the slot file the launcher lists is on disk")
|
|
|
|
local ok, res = SaveFileIO.exportActiveSlot("gold")
|
|
eq(ok, false, "Export on a Gold slot is refused, not crashed")
|
|
check(type(res) == "string" and res:find("not supported yet", 1, true),
|
|
"the refusal is the plain launcher message: " .. tostring(res))
|
|
check(not tostring(res):find("GenSave", 1, true)
|
|
and not tostring(res):find("attempt to index", 1, true),
|
|
"no codec traceback leaks into the notice line")
|
|
local exported = false
|
|
for path in pairs(files) do
|
|
if path:find("^exports/") then exported = true end
|
|
end
|
|
eq(exported, false, "no export file is written for a Gold slot")
|
|
|
|
-- The import direction through the same seam: a 32 KB image aimed at Gold
|
|
-- must be refused by version, before any Gen 1 decoding is attempted.
|
|
local iok, ierr = SaveConvert.importSav(string.rep("\0", 32768), "gold", "gold")
|
|
eq(iok, nil, "importing a cart .sav for Gold is refused")
|
|
check(type(ierr) == "string" and ierr:find("not supported yet", 1, true),
|
|
"the import refusal is the plain launcher message: " .. tostring(ierr))
|
|
|
|
-- Gen 1 versions still pass the gate: red reaches the codec proper and
|
|
-- fails on its own terms (an all-zero image is not a table), never on the
|
|
-- version.
|
|
local rok, rerr = SaveConvert.exportSav({}, "red")
|
|
check(rok ~= nil or not tostring(rerr):find("not supported yet", 1, true),
|
|
"a red export is never refused by the version gate")
|
|
|
|
GameVersion.set("red")
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- GenSave.crosswalks over def tables that carry provenance scalars.
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
-- Synthetic set, ROM-free: real rows resolve, scalar rows are skipped in
|
|
-- all three builders (index, dex, machine).
|
|
local defs = {
|
|
pokemon = {
|
|
generation = 2,
|
|
source = "ROM:BaseStats",
|
|
MEW = { index = 21, source = "ROM:BaseStats[151]", name = "MEW" },
|
|
},
|
|
moves = {
|
|
generation = 2,
|
|
POUND = { index = 1 },
|
|
},
|
|
items = {
|
|
generation = 2,
|
|
source = "ROM:ItemNames",
|
|
POKE_BALL = { index = 4 },
|
|
TM01 = { machine = { kind = "TM", number = 1 } },
|
|
},
|
|
maps = {
|
|
generation = 2,
|
|
PALLET_TOWN = { index = 0 },
|
|
},
|
|
}
|
|
local cok, cw = pcall(GenSave.crosswalks, defs)
|
|
eq(cok, true, "crosswalks survive scalar rows: " .. tostring(cw))
|
|
if cok then
|
|
eq(cw.pokemonIndex.MEW, 21, "a def row beside scalars still resolves")
|
|
eq(cw.pokemonByDex[151], "MEW", "the dex builder skips scalar rows")
|
|
eq(cw.itemsIndex.TM01, 201, "the machine builder skips scalar rows")
|
|
eq(cw.pokemonIndex.generation, nil, "a scalar row never becomes an id")
|
|
end
|
|
|
|
-- The real Gold tables, when a cache is around: the exact shape that
|
|
-- raised at GenSave.crosswalks before the guard.
|
|
local cache = os.getenv("GOLD_CACHE")
|
|
if cache then
|
|
local function loadGen(name)
|
|
local chunk = assert(loadfile(cache .. "/data/generated/" .. name .. ".lua"))
|
|
return chunk()
|
|
end
|
|
local gold = {
|
|
pokemon = loadGen("pokemon"), moves = loadGen("moves"),
|
|
items = loadGen("items"), maps = loadGen("maps"),
|
|
}
|
|
eq(type(gold.items.generation), "number",
|
|
"the gold items table really does carry a scalar row (the regression stays armed)")
|
|
local gok, gerr = pcall(GenSave.crosswalks, gold)
|
|
eq(gok, true, "crosswalks over the extracted Gold tables: " .. tostring(gerr))
|
|
else
|
|
check(true, "no GOLD_CACHE: the extracted Gold tables are not checked (SKIP)")
|
|
end
|
|
|
|
S.finish()
|