Files
gen1recomp/tests/engine/union_export_safety.lua
T
2026-10-07 14:31:38 -04:00

150 lines
6.5 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
love = love or require("tests.love_stub")
local GenSave = require("src.save_convert.GenSave")
local Gen2Save = require("src.save_convert.Gen2Save")
local SaveConvert = require("src.save_convert.SaveConvert")
local SaveData = require("src.core.SaveData")
local Origin = require("src.online.union.Origin")
local function versionRoot(version)
local home = os.getenv("HOME")
if not home or home == "" then return nil end
local ids = {}
local env = os.getenv("POKEPORT_IDENTITY")
if env and env ~= "" then ids[#ids + 1] = env end
ids[#ids + 1] = "g1r-" .. version
ids[#ids + 1] = "pokeport-test-caches"
for _, base in ipairs({ home .. "/Library/Application Support/LOVE", home .. "/.local/share/love" }) do
for _, id in ipairs(ids) do
local root = base .. "/" .. id .. "/" .. version
local f = io.open(root .. "/data/generated/maps.lua", "rb")
if f then f:close() return root end
end
end
return nil
end
local function loadTable(root, name)
local chunk = loadfile(root .. "/data/generated/" .. name .. ".lua")
local ok, mod = pcall(chunk or error)
return ok and type(mod) == "table" and mod or nil
end
local function captured(module, fn)
local seen
local real = module.encode
module.encode = function(save, ...)
seen = save
return real(save, ...)
end
local bytes, err = fn()
module.encode = real
return seen, bytes, err
end
local ran = 0
do
local root = versionRoot("red")
local stub = root and SaveConvert.gen1DataFromDir(root)
if stub and stub.maps.MT_MOON_POKECENTER then
ran = ran + 1
SaveConvert.setGen1DataStub(stub, "red")
local function onMap(map, x, y, origin)
local s = SaveData.newGame({ playerName = "RED", rivalName = "BLUE" })
s.player.map, s.player.x, s.player.y, s.player.facing = map, x, y, "left"
s.lastOutdoor = { id = "PALLET_TOWN", x = 5, y = 6 }
if origin then Origin.record(s, origin) end
return s
end
local mtMoon = { gen = 1, version = "red", map = "MT_MOON_POKECENTER", warp = 3, x = 13, y = 1 }
local cases = {
{ "2F", onMap("POKECENTER_2F", 13, 2, mtMoon), "MT_MOON_POKECENTER", "ROUTE_4" },
{ "Union Room", onMap("UNION_ROOM", 12, 20, mtMoon), "MT_MOON_POKECENTER", "ROUTE_4" },
{ "1F stairs alcove", onMap("VIRIDIAN_POKECENTER", 12, 1), "VIRIDIAN_POKECENTER", "VIRIDIAN_CITY" },
}
for _, c in ipairs(cases) do
local label, live, center, town = c[1], c[2], c[3], c[4]
local livePlayer = live.player
local seen, bytes, err = captured(GenSave, function() return SaveConvert.exportSav(live, "red") end)
check(seen ~= nil, "red " .. label .. ": the encoder ran")
eq(seen and seen.player.map, center, "red " .. label .. ": the cart save stands in the origin center")
eq(seen and seen.player.facing, "up", "red " .. label .. ": facing the nurse")
eq(seen and seen.lastOutdoor and seen.lastOutdoor.id, town, "red " .. label .. ": LAST_MAP is the center's town")
check(live.player == livePlayer, "red " .. label .. ": the live save is not moved")
check(bytes ~= nil, "red " .. label .. ": exported -- " .. tostring(err))
if bytes then
local back = SaveConvert.importSav(bytes, "red", "red")
eq(back and back.player.map, center, "red " .. label .. ": the .sav reads back in the center")
end
end
local plain = onMap("VIRIDIAN_POKECENTER", 11, 3)
local seen = captured(GenSave, function() return SaveConvert.exportSav(plain, "red") end)
check(seen == plain, "red: a vanilla cell encodes the save untouched")
SaveConvert.setGen1DataStub(nil)
end
end
do
local root = versionRoot("gold")
local stub = {}
for _, name in ipairs({ "pokemon", "moves", "items", "maps", "scripts", "sprites", "constants" }) do
stub[name] = root and loadTable(root, name)
end
local center = "CHERRYGROVE_POKECENTER_1F"
if stub.maps and stub.maps.POKECENTER_2F and stub.maps[center] then
ran = ran + 1
stub.tilesets, stub.landmarks = loadTable(root, "tilesets"), loadTable(root, "landmarks")
SaveConvert.setGen2DataStub(stub)
local width = stub.maps.POKECENTER_2F.width * 2
local function at(map, x, y)
local s = {
generation = 2,
player = { name = "GOLD", id = 12345, money = 3000, gender = "male" },
rival = { name = "SILVER" }, mom = { name = "MOM" },
position = { map = map, x = x, y = y, facing = "left" },
party = {}, boxes = {}, boxNames = {}, currentBox = 1, spawn = 2,
}
Origin.record(s, { gen = 2, version = "gold", map = center, warp = 3, x = 0, y = 7, facing = "left" })
return s
end
for _, c in ipairs({ { "Union Room", at("UNION_ROOM", 4, 4) },
{ "2F added column", at("POKECENTER_2F", width + 1, 3) } }) do
local label, live = c[1], c[2]
local livePos = live.position
local seen, bytes, err = captured(Gen2Save, function() return SaveConvert.exportSav(live, "gold") end)
check(seen ~= nil, "gold " .. label .. ": the encoder ran")
eq(seen and seen.position.map, center, "gold " .. label .. ": the cart save stands in the origin 1F")
eq(seen and seen.position.facing, "up", "gold " .. label .. ": facing the nurse")
eq(seen and seen.backupWarp and seen.backupWarp.map, center, "gold " .. label .. ": the backup warp is the 1F")
check(live.position == livePos, "gold " .. label .. ": the live save is not moved")
check(bytes ~= nil, "gold " .. label .. ": exported -- " .. tostring(err))
end
local plain = at("POKECENTER_2F", 2, 3)
local seen = captured(Gen2Save, function() return SaveConvert.exportSav(plain, "gold") end)
check(seen == plain, "gold: a vanilla 2F cell encodes the save untouched")
SaveConvert.setGen2DataStub(nil)
end
end
do
SaveConvert.setGen1DataStub({ pokemon = {}, moves = {}, items = {}, maps = {}, encounters = {} }, "red")
local s = { player = { map = "UNION_ROOM", x = 4, y = 4, facing = "up" } }
local seen, bytes, why = captured(GenSave, function() return SaveConvert.exportSav(s, "red") end)
check(seen == nil and bytes == nil, "red: a Union Room save is never encoded without the text pointer table")
check(type(why) == "string" and why:find("text_pointers", 1, true) ~= nil, "and the refusal names it: " .. tostring(why))
SaveConvert.setGen1DataStub(nil)
ran = ran + 1
end
if ran == 0 then
print("[skip] union_export_safety: no red or gold cache")
os.exit(0)
end
T.finish("union_export_safety")