mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-02 07:50:13 -04:00
151 lines
5.7 KiB
Lua
151 lines
5.7 KiB
Lua
love = love or require("tests.love_stub")
|
|
|
|
local SaveConvert = require("src.save_convert.SaveConvert")
|
|
local Gen2Save = require("src.save_convert.Gen2Save")
|
|
local Gen3Save = require("src.save_convert.Gen3Save")
|
|
local Gen3Layout = require("src.save_convert.Gen3Layout")
|
|
local Compat = require("src.save_convert.Compat")
|
|
local Diff = require("tests.save_compat._diff")
|
|
local G2 = require("tests.fixtures.save.gen2_build")
|
|
|
|
local K = {}
|
|
|
|
function K.gen1Available()
|
|
return loadfile("data/generated/pokemon.lua") ~= nil
|
|
end
|
|
|
|
local stamped = {}
|
|
local supplied = {}
|
|
function K.gen1Data(version)
|
|
if not supplied[version] then
|
|
local dir = os.getenv("GEN1_" .. version:upper() .. "_CACHE") or os.getenv(version:upper() .. "_CACHE")
|
|
or (os.getenv("HOME") .. "/Library/Application Support/LOVE/pokemon-love2d/" .. version)
|
|
local generated = SaveConvert.gen1DataFromDir(dir)
|
|
if not generated and version == "red" then generated = SaveConvert.gen1DataFromDir(".") end
|
|
assert(generated, "missing " .. version .. " fixture cache; set " .. version:upper() .. "_CACHE")
|
|
SaveConvert.setGen1DataStub(generated, version)
|
|
supplied[version] = generated
|
|
end
|
|
local data = SaveConvert.loadData(version)
|
|
if not data then
|
|
SaveConvert.setGen1DataStub(supplied[version], version)
|
|
data = assert(SaveConvert.loadData(version))
|
|
end
|
|
if not stamped[data] then
|
|
local stamp = loadfile("tests/fixture_data/map_window.lua")()
|
|
for mapId in pairs(data.maps) do stamp(data, mapId) end
|
|
stamped[data] = true
|
|
end
|
|
return data
|
|
end
|
|
|
|
K.GEN2_MAP = "SAVE_COMPAT_FIXTURE_MAP"
|
|
K.gen2Data = {
|
|
items = G2.ITEMS,
|
|
maps = { [K.GEN2_MAP] = { group = 24, map = 7, objectEventsAddr = 0x5A17, width = 2, height = 2,
|
|
blocks = { 1, 2, 3, 4 }, objects = {} } },
|
|
}
|
|
|
|
function K.gen3Opts(version, template)
|
|
if version == "emerald" then
|
|
return { template = template, version = "emerald", metGame = 3,
|
|
toNational = function() return nil end, itemId = function(id) return tonumber(id) end,
|
|
mapLayoutId = function(g, n) return ({ ["25:40"] = 237 })[g .. ":" .. n] end,
|
|
healWarp = function() return { group = 0, num = 9, warpId = -1, x = 5, y = 8 } end }
|
|
end
|
|
return {
|
|
template = template, version = version,
|
|
metGame = version == "leafgreen" and Gen3Layout.VERSION_LEAF_GREEN or Gen3Layout.VERSION_FIRE_RED,
|
|
toNational = function(sp) return sp >= 1 and sp <= 251 and sp or nil end,
|
|
speciesFromNational = function(n) return n end,
|
|
mapLayoutId = function(g, n) return ({ ["4:1"] = 2 })[g .. ":" .. n] end,
|
|
healWarp = function() return { group = 3, num = 0, warpId = -1, x = 6, y = 8 } end,
|
|
}
|
|
end
|
|
|
|
function K.import(gen, version, bytes)
|
|
if gen == 1 then
|
|
K.gen1Data(version)
|
|
return SaveConvert.importSav(bytes, version, version)
|
|
elseif gen == 2 then
|
|
return Gen2Save.decode(bytes, version, K.gen2Data)
|
|
end
|
|
return SaveConvert.importSav(bytes, version, version)
|
|
end
|
|
|
|
function K.export(gen, version, save, template)
|
|
local ok, out, err
|
|
if gen == 1 then
|
|
K.gen1Data(version)
|
|
if template == false then save.rawImport = nil end
|
|
ok, out, err = pcall(SaveConvert.exportSav, save, version)
|
|
elseif gen == 2 then
|
|
ok, out, err = pcall(Gen2Save.encode, save, version, template or nil, K.gen2Data)
|
|
else
|
|
if template == false and type(save.modData) == "table" then save.modData.cartImage = nil end
|
|
ok, out, err = pcall(Gen3Save.forVersion(version).exportPort, save, K.gen3Opts(version, template or nil))
|
|
end
|
|
if not ok then return nil, "raised: " .. tostring(out) end
|
|
return out, err
|
|
end
|
|
|
|
local function family(version) return version == "emerald" and "emerald" or "frlg" end
|
|
|
|
local function patch(s, off, src)
|
|
return s:sub(1, off) .. src .. s:sub(off + #src + 1)
|
|
end
|
|
|
|
K.N = {
|
|
{ id = "N1", gen = 1, why = "bank 2/3 box checksums are recomputed; derived regions are not diffed" },
|
|
{ id = "N3", gen = 3, why = "Gen 3 is compared on reassembled sections; slot, rotation and counter change by design" },
|
|
{ id = "N5", gen = 1, why = "party box-level byte is rewritten with the party level (G1-21)" },
|
|
{ id = "N6", gen = 2, why = "a primary sum whose low byte is 0 is nudged through the pad byte after GREEN, which OpenHome rejects otherwise" },
|
|
}
|
|
|
|
function K.r1Diff(gen, version, src, out)
|
|
if gen == 1 then
|
|
local count = src:byte(0x2F2C + 1)
|
|
if count <= 6 then
|
|
for i = 0, count - 1 do
|
|
local at = 0x2F2C + 8 + i * 44 + 3
|
|
out = patch(out, at, src:sub(at + 1, at + 1))
|
|
end
|
|
end
|
|
return Diff.diff(src, out, Diff.regionsFor(1, version))
|
|
elseif gen == 2 then
|
|
return Diff.diff(src, out, Diff.regionsFor(2, version))
|
|
end
|
|
local regions = Diff.regionsFor(3, version)
|
|
local fam = family(version)
|
|
local a, b = Compat.gen3Blocks(src, fam), Compat.gen3Blocks(out, fam)
|
|
if not (a and b) then return { { name = "sections", count = 1, first = 0, last = 0, sample = {} } } end
|
|
local entries = {}
|
|
for _, blk in ipairs({ "sb2", "sb1", "storage" }) do
|
|
for _, e in ipairs(Diff.diff(a[blk], b[blk], regions, { block = blk })) do entries[#entries + 1] = e end
|
|
end
|
|
for _, e in ipairs(Diff.diff(src:sub(0x1C001, 0x20000), out:sub(0x1C001, 0x20000), regions,
|
|
{ block = "flash", base = 0x1C000 })) do
|
|
entries[#entries + 1] = e
|
|
end
|
|
if #src ~= #out then
|
|
entries[#entries + 1] = { name = "trailer", count = math.abs(#src - #out), first = 0x20000, last = 0x20000,
|
|
sample = { ("%d>%d bytes"):format(#src, #out) } }
|
|
end
|
|
return entries
|
|
end
|
|
|
|
function K.compatKeys(bytes, version)
|
|
local report = Compat.check(bytes, version)
|
|
local keys = {}
|
|
local seen = {}
|
|
for _, e in ipairs(report.errors) do
|
|
if not seen[e.rule] then
|
|
seen[e.rule] = true
|
|
keys[#keys + 1] = { key = "compat:" .. e.rule, detail = e.msg }
|
|
end
|
|
end
|
|
return keys, report
|
|
end
|
|
|
|
return K
|