mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-28 23:00:21 -04:00
162 lines
6.2 KiB
Lua
162 lines
6.2 KiB
Lua
local U = require("tests.drivers.util")
|
|
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_sav_import"
|
|
local SAV = os.getenv("POKEPORT_EM_SAV") or ".claude/skills/pygba-headless/assets/pokemon-emerald-all-shiny-fixed.sav"
|
|
|
|
local failures = 0
|
|
local function check(ok, label)
|
|
print((ok and "PASS " or "FAIL ") .. label)
|
|
if not ok then failures = failures + 1 end
|
|
return ok
|
|
end
|
|
|
|
local function finish()
|
|
print((failures == 0 and "PASS" or "FAIL") .. " em_sav_import failures=" .. failures)
|
|
love.event.quit(failures == 0 and 0 or 1)
|
|
U.wait(10)
|
|
end
|
|
|
|
local function waitFor(pred, limit)
|
|
for _ = 1, limit or 600 do
|
|
if pred() then return true end
|
|
U.wait(1)
|
|
end
|
|
return pred() and true or false
|
|
end
|
|
|
|
local function shot(game, name)
|
|
U.wait(4)
|
|
U.still(game, DIR .. "/" .. name .. ".png")
|
|
end
|
|
|
|
local function topId()
|
|
local t = require("src.ui.game3.stack").top()
|
|
return t and t.id
|
|
end
|
|
|
|
local function toMenu(game)
|
|
local custom = game.boot and game.boot.custom
|
|
if not custom then return nil end
|
|
for _ = 1, 400 do
|
|
if custom.title or custom.menu then break end
|
|
U.tap(game, "a")
|
|
U.wait(8)
|
|
end
|
|
for _ = 1, 600 do
|
|
if custom.menu then break end
|
|
U.tap(game, "start")
|
|
U.wait(10)
|
|
end
|
|
return custom.menu
|
|
end
|
|
|
|
return function(game)
|
|
if not check(waitFor(function() return game.phase == "boot" and game.boot ~= nil end, 900), "boot reached") then
|
|
return finish()
|
|
end
|
|
local f = io.open(SAV, "rb")
|
|
if not check(f ~= nil, "bundled Emerald .sav at " .. SAV) then return finish() end
|
|
local original = f:read("*a")
|
|
f:close()
|
|
|
|
local SaveFileIO = require("src.import.SaveFileIO")
|
|
local SaveData = require("src.core.SaveData")
|
|
local ok, slotId, info = SaveFileIO.importToSlot(SAV, "emerald")
|
|
if not check(ok == true, "the cart imports into an Emerald slot (" .. tostring(slotId) .. ")") then return finish() end
|
|
check(info == nil, "no fallback note")
|
|
check(SaveData.activeSlot("emerald") == slotId, "the imported slot is active")
|
|
|
|
pcall(function() game:returnToTitle() end)
|
|
check(waitFor(function() return game.phase == "boot" and game.boot ~= nil and game.boot.custom ~= nil end, 600),
|
|
"back at the title")
|
|
local menu = toMenu(game)
|
|
if not check(menu ~= nil, "title START opens the main menu") then return finish() end
|
|
U.wait(60)
|
|
check(menu.items[1] == "CONTINUE", "CONTINUE is offered (" .. tostring(menu.items[1]) .. ")")
|
|
local box = menu.info or {}
|
|
print(string.format("[driver] continue box name=%s badges=%s dex=%s time=%s:%s", tostring(box.name), tostring(box.badges),
|
|
tostring(box.dexCount), tostring(box.hours), tostring(box.minutes)))
|
|
check(box.name == "NICK", "continue box player NICK")
|
|
check(box.hours == 135 and box.minutes == 30, "continue box play time 135:30")
|
|
check(box.badges == 8, "continue box 8 badges like the cart")
|
|
check(box.dexCount == 386, "continue box POKeDEX 386 like the cart")
|
|
shot(game, "01_main_menu_continue")
|
|
|
|
pcall(function() game:_handleBootAction({ action = "continue" }) end)
|
|
local Runtime = require("src.core.game3.runtime")
|
|
local s
|
|
check(waitFor(function()
|
|
s = Runtime.getSession()
|
|
return s ~= nil and game.phase ~= "boot"
|
|
end, 1200), "CONTINUE reached the field")
|
|
U.wait(120)
|
|
s = Runtime.getSession()
|
|
if not s then return finish() end
|
|
print(string.format("[driver] field map=%s x=%s y=%s money=%s coins=%s party=%d", tostring(s.map), tostring(s.x),
|
|
tostring(s.y), tostring(s.money), tostring(s.coins), #(s.party or {})))
|
|
check(s.map == "EM_ROUTE117" and s.x == 51 and s.y == 6, "continue lands on Route 117 at 51,6 like the cart")
|
|
check(s.money == 999999 and s.coins == 9999, "money 999999, coins 9999")
|
|
local C = require("src.core.game3.constants").of("emerald")
|
|
local want = { { "SPECIES_SALAMENCE", 50 }, { "SPECIES_MAGCARGO", 38 }, { "SPECIES_SMEARGLE", 100 } }
|
|
for i, w in ipairs(want) do
|
|
local m = s.party[i] or {}
|
|
check(m.species == C:require("species", w[1]) and m.level == w[2],
|
|
("party %d is %s Lv%d (%s Lv%s)"):format(i, w[1], w[2], tostring(m.species), tostring(m.level)))
|
|
end
|
|
local boxed = 0
|
|
for _, b in pairs(s.storage and s.storage.boxes or {}) do
|
|
for _ in pairs(type(b) == "table" and b.mons or {}) do boxed = boxed + 1 end
|
|
end
|
|
check(boxed == 411, "411 boxed mons (" .. boxed .. ")")
|
|
check(type(s.berryTrees) == "table", "berry trees restored")
|
|
check(type(s.localTimeOffset) == "table" and type(s.rtcSkew) == "number", "RTC offset and anchor restored")
|
|
shot(game, "02_field_route117")
|
|
|
|
U.tap(game, "start")
|
|
U.wait(30)
|
|
if check(topId() == "start", "START opens the start menu") then
|
|
local StartMenu = require("src.ui.game3.start_menu")
|
|
for i, e in ipairs(StartMenu.ENTRIES or {}) do
|
|
if e.id == "pokemon" then
|
|
for _ = 1, 20 do
|
|
if StartMenu.cursor == i then break end
|
|
U.tap(game, "down")
|
|
U.wait(6)
|
|
end
|
|
U.tap(game, "a")
|
|
break
|
|
end
|
|
end
|
|
check(waitFor(function() return topId() == "party" end, 240), "POKEMON opens the party screen")
|
|
U.wait(40)
|
|
shot(game, "03_party")
|
|
for _ = 1, 30 do
|
|
if topId() == nil then break end
|
|
U.tap(game, "b")
|
|
U.wait(8)
|
|
end
|
|
end
|
|
|
|
local okSave = game:saveGame()
|
|
check(okSave ~= false, "the imported game saves in the port")
|
|
local exOk, path = SaveFileIO.exportActiveSlot("emerald")
|
|
if check(exOk == true, "the slot exports to a .sav (" .. tostring(path) .. ")") then
|
|
local ef = io.open(path, "rb")
|
|
local bytes = ef and ef:read("*a")
|
|
if ef then ef:close() end
|
|
local out = io.open(DIR .. "/em_sav_import_export.sav", "wb")
|
|
if out and bytes then out:write(bytes); out:close() end
|
|
local E = require("src.save_convert.Gen3Save").forVersion("emerald")
|
|
local c = bytes and E.decode(bytes)
|
|
check(c ~= nil and c.name == "NICK" and c.money == 999999, "the export decodes as Emerald with NICK and 999999")
|
|
local a, b = E.readBlocks(original), bytes and E.readBlocks(bytes)
|
|
if b then
|
|
check(b.counter == a.counter + 1, "export writes the next save counter")
|
|
check(E.decode(original).playHours == c.playHours, "play hours carried")
|
|
local party = 0
|
|
for _ = 1, #c.party do party = party + 1 end
|
|
check(party == 3, "three party mons in the export")
|
|
end
|
|
end
|
|
finish()
|
|
end
|