mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-02 15:50:39 -04:00
158 lines
6.3 KiB
Lua
158 lines
6.3 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
love = love or require("tests.love_stub")
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
local K = require("tests.save_compat._codec")
|
|
local G3 = require("tests.fixtures.save.gen3_build")
|
|
local B = require("tests.fixtures.save.bytes")
|
|
local Gen3Save = require("src.save_convert.Gen3Save")
|
|
local SaveConvert = require("src.save_convert.SaveConvert")
|
|
local SaveFileIO = require("src.import.SaveFileIO")
|
|
local SaveData = require("src.core.SaveData")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
|
|
local fixtures = {}
|
|
for _, c in ipairs(G3.cases()) do fixtures[c.id] = c end
|
|
|
|
local function memfs(files)
|
|
return {
|
|
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
|
|
local prefix = path .. "/"
|
|
for key in pairs(files) do
|
|
if key:sub(1, #prefix) == prefix then return { type = "directory" } end
|
|
end
|
|
return nil
|
|
end,
|
|
createDirectory = function() return true end,
|
|
getSaveDirectory = function() return "/fake/save" end,
|
|
}
|
|
end
|
|
|
|
local realFs = love.filesystem
|
|
local function freshFs(version)
|
|
local files = {}
|
|
love.filesystem = memfs(files)
|
|
SaveData.resetSlotState()
|
|
GameVersion.set(version)
|
|
return files
|
|
end
|
|
|
|
local function rsImage(version)
|
|
local w = G3.base("emerald")
|
|
for i = 0x890, w.sb2.size - 1 do w.sb2[i] = 0 end
|
|
for i = 0x3AC0, w.sb1.size - 1 do w.sb1[i] = 0 end
|
|
return G3.emit(w)
|
|
end
|
|
|
|
local function withMons(version, metGame, matchOwner)
|
|
local w = G3.base(version)
|
|
local tid, sid = B.getLE(w.sb2, 0x0A, 2), B.getLE(w.sb2, 0x0C, 2)
|
|
local list = {}
|
|
for i = 1, 3 do
|
|
list[i] = G3.mon({
|
|
pid = 0x01010101 * i, tid = matchOwner and tid or (tid + 1) % 65536, sid = matchOwner and sid or (sid + 1) % 65536,
|
|
origins = 5 + metGame * 128 + 4 * 2048, species = 4 + i, nick = "M" .. i,
|
|
})
|
|
end
|
|
B.fill(w.storage, 4, 14 * 30 * 80, 0)
|
|
G3.setParty(w, list)
|
|
return G3.emit(w)
|
|
end
|
|
|
|
local function importAs(bytes, version)
|
|
return SaveConvert.importSav(bytes, version, version)
|
|
end
|
|
|
|
for _, cfg in ipairs({
|
|
{ version = "firered", met = 4, want = "firered" },
|
|
{ version = "firered", met = 5, want = "leafgreen" },
|
|
{ version = "leafgreen", met = 4, want = "firered" },
|
|
{ version = "leafgreen", met = 5, want = "leafgreen" },
|
|
}) do
|
|
local bytes = withMons(cfg.version, cfg.met, true)
|
|
local s = assert(importAs(bytes, cfg.version))
|
|
eq(s.modData.cartGame, cfg.want, ("%s slot of a cart whose own mons were met in game %d is marked %s"):format(cfg.version, cfg.met, cfg.want))
|
|
local traded = withMons(cfg.version, cfg.met == 4 and 5 or 4, false)
|
|
local s2 = assert(importAs(traded, cfg.version))
|
|
eq(s2.modData.cartGame, cfg.version, cfg.version .. ": mons the player did not catch never decide the marking")
|
|
local fresh = assert(importAs(fixtures["g3." .. cfg.version .. ".basic"].bytes, cfg.version))
|
|
check(fresh.modData.cartGame == "firered" or fresh.modData.cartGame == "leafgreen",
|
|
cfg.version .. ": a cart without own mons is still marked FireRed or LeafGreen (" .. tostring(fresh.modData.cartGame) .. ")")
|
|
end
|
|
do
|
|
local s = assert(importAs(fixtures["g3.emerald.basic"].bytes, "emerald"))
|
|
eq(s.modData.cartGame, "emerald", "an Emerald slot is marked Emerald")
|
|
end
|
|
|
|
local variants = { { "synthetic RS-shaped image", rsImage() } }
|
|
local f = io.open(".claude/skills/pygba-headless/assets/pokemon-ruby.sav", "rb")
|
|
if f then
|
|
variants[#variants + 1] = { "real Ruby save", f:read("*a") }
|
|
f:close()
|
|
else
|
|
print("[skip] real Ruby save not present")
|
|
end
|
|
|
|
for _, var in ipairs(variants) do
|
|
local label, bytes = var[1], var[2]
|
|
eq(Gen3Save.sniff(bytes), "rs", label .. " sniffs as Ruby/Sapphire")
|
|
for _, v in ipairs({ "firered", "leafgreen", "emerald" }) do
|
|
local s, err = SaveConvert.importSav(bytes, v, v)
|
|
eq(s, nil, ("%s refused by importSav for %s"):format(label, v))
|
|
check(type(err) == "string" and err:find("Ruby/Sapphire", 1, true) ~= nil,
|
|
("%s: importSav for %s names Ruby/Sapphire (%s)"):format(label, v, tostring(err)))
|
|
end
|
|
for _, v in ipairs({ "red", "blue", "yellow", "gold", "silver", "crystal" }) do
|
|
local ok, why = SaveConvert.mainChecksumValid(bytes, v)
|
|
check(ok == nil and type(why) == "string" and why:find("Ruby/Sapphire", 1, true) ~= nil,
|
|
("%s: the %s launcher check names Ruby/Sapphire (%s)"):format(label, v, tostring(why)))
|
|
end
|
|
for _, v in ipairs({ "firered", "leafgreen", "emerald", "red", "gold" }) do
|
|
local files = freshFs(v)
|
|
files["picked.sav"] = bytes
|
|
local ok, slotOrErr = SaveFileIO.importToSlot("picked.sav", v)
|
|
eq(ok, false, ("%s: SaveFileIO refuses it for %s"):format(label, v))
|
|
check(type(slotOrErr) == "string" and slotOrErr:find("Ruby/Sapphire", 1, true) ~= nil,
|
|
("%s: SaveFileIO message for %s names Ruby/Sapphire (%s)"):format(label, v, tostring(slotOrErr)))
|
|
local created = false
|
|
for path in pairs(files) do if path:find("^saves/") then created = true end end
|
|
check(not created, ("%s: SaveFileIO leaves no slot behind for %s"):format(label, v))
|
|
end
|
|
end
|
|
|
|
do
|
|
local dir = os.getenv("TMPDIR") or "/tmp"
|
|
local src = ("%s/pokeport-entry-paths-%d.sav"):format(dir:gsub("/$", ""), os.time())
|
|
local out = src .. ".lua"
|
|
local fh = assert(io.open(src, "wb"))
|
|
fh:write(variants[1][2])
|
|
fh:close()
|
|
local cmd = ("luajit tools/save_convert/convert.lua import '%s' '%s' red >/dev/null 2>&1"):format(src, out)
|
|
local rc = os.execute(cmd)
|
|
check(rc ~= 0 and rc ~= true, "tools/save_convert/convert.lua refuses a Ruby/Sapphire image")
|
|
local produced = io.open(out, "rb")
|
|
check(produced == nil, "the CLI writes nothing for a Ruby/Sapphire image")
|
|
if produced then produced:close() end
|
|
os.remove(src)
|
|
os.remove(out)
|
|
end
|
|
|
|
do
|
|
local files = freshFs("firered")
|
|
files["picked.sav"] = withMons("firered", 5, true)
|
|
local ok, slot = SaveFileIO.importToSlot("picked.sav", "firered")
|
|
check(ok == true, "SaveFileIO imports a LeafGreen-origin cart into the FireRed game")
|
|
local save = ok and SaveData.load("firered")
|
|
eq(save and save.modData.cartGame, "leafgreen", "the stored slot keeps the cart's LeafGreen marking")
|
|
eq(save and save.version, "firered", "the slot belongs to the game it was imported into")
|
|
end
|
|
|
|
love.filesystem = realFs
|
|
T.finish()
|