Files
gen1recomp/tests/save_compat/gen3_image_pack_test.lua
T
2026-10-01 17:30:52 -04:00

111 lines
4.9 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
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 Pack = require("src.save_convert.gen3_port.imagepack")
local Gen3Save = require("src.save_convert.Gen3Save")
local VERSIONS = { "firered", "leafgreen", "emerald" }
local fixtures = {}
for _, c in ipairs(G3.cases()) do fixtures[c.id] = c end
local seed = 0x1234567
local function rnd(n)
seed = (seed * 1103515245 + 12345) % 2147483648
return math.floor(seed / 65536) % n
end
for _, n in ipairs({ 1, 2, 3, 4, 5, 127, 128, 4095, 4096, 4097, 32768, 70000 }) do
for _, mode in ipairs({ "random", "zero", "ff", "period7", "mixed" }) do
local parts = {}
for i = 1, n do
local v
if mode == "random" then v = rnd(256)
elseif mode == "zero" then v = 0
elseif mode == "ff" then v = 255
elseif mode == "period7" then v = i % 7
else v = (i % 61 < 40) and 0 or rnd(256) end
parts[i] = string.char(v)
end
local raw = table.concat(parts)
local packed = Pack.pack(raw)
check(packed ~= nil and Pack.isPacked(packed), ("%s/%d packs"):format(mode, n))
eq(Pack.unpack(packed), raw, ("%s/%d round trips"):format(mode, n))
eq(Pack.pack(raw), packed, ("%s/%d packs deterministically"):format(mode, n))
check(not packed:find("[^%w+/=:]"), ("%s/%d is printable ASCII"):format(mode, n))
end
end
eq(Pack.unpack("raw cart bytes"), "raw cart bytes", "a legacy raw image passes through unchanged")
eq(Pack.pack(""), nil, "an empty image does not pack")
eq(Pack.pack(string.rep("\0", 0x100001)), nil, "an oversized image does not pack")
local broken = {
"PKCI1:", "PKCI1:5:", "PKCI1:5:AAAA", "PKCI1:0:", "PKCI1:999999999:AAAA", "PKCI1:4:!!!!", "PKCI1:4:AAA",
"PKCI1:4:A=AA", "PKCI1:abc:AAAA", "PKCI1:4:AAAAAAAA", "PKCI1:1048577:AAAA",
}
for _, s in ipairs(broken) do
local ok, r = pcall(Pack.unpack, s)
check(ok and r == nil, "malformed packed image is refused without raising: " .. s)
end
do
local src = string.rep("abcdefgh", 400)
local packed = Pack.pack(src)
local raised, accepted = 0, 0
for pos = 7, #packed do
for _, b in ipairs({ 0, 65, 255 }) do
local bad = packed:sub(1, pos - 1) .. string.char(b) .. packed:sub(pos + 1)
local ok, r = pcall(Pack.unpack, bad)
if not ok then raised = raised + 1 end
if ok and r ~= nil and r ~= src and #r ~= #src then accepted = accepted + 1 end
end
end
eq(raised, 0, "single-byte corruption anywhere never raises")
eq(accepted, 0, "corruption never yields a wrong-length image")
end
for _, v in ipairs(VERSIONS) do
local codec = Gen3Save.forVersion(v)
for _, name in ipairs({ "basic", "full_boxes", "max_stacks", "hall_of_fame", "eggs", "trailer" }) do
local c = fixtures["g3." .. v .. "." .. name]
local save = assert(K.import(3, v, c.bytes))
local stored = save.modData.cartImage
check(Pack.isPacked(stored), v .. " " .. name .. ": the slot stores the compact image")
eq(codec.slotTemplate(save), c.bytes, v .. " " .. name .. ": the compact image restores every byte")
if name == "basic" then
check(#stored * 4 < #c.bytes, ("%s basic: compact image %d bytes vs %d raw"):format(v, #stored, #c.bytes))
end
local viaPacked = assert(K.export(3, v, save, nil))
local viaRaw = assert(K.export(3, v, save, c.bytes))
eq(viaPacked, viaRaw, v .. " " .. name .. ": exporting from the compact slot equals exporting from the raw file")
local legacy = assert(K.import(3, v, c.bytes))
legacy.modData.cartImage = c.bytes
eq(assert(K.export(3, v, legacy, nil)), viaRaw, v .. " " .. name .. ": a legacy raw slot image still exports the same file")
local entries = K.r1Diff(3, v, c.bytes, viaPacked)
eq(#entries, 0, v .. " " .. name .. ": R1 logical round trip from the compact image")
end
end
for _, v in ipairs(VERSIONS) do
local c = fixtures["g3." .. v .. ".basic"]
for _, bad in ipairs({ "PKCI1:131072:AAAA", "broken raw image", Pack.pack("broken image") }) do
local save = assert(K.import(3, v, c.bytes))
save.modData.cartImage = bad
local out, err = K.export(3, v, save, nil)
check(out == nil, v .. ": a damaged embedded image refuses export")
check(type(err) == "string" and err:find("preserved cartridge image", 1, true) ~= nil,
v .. ": a damaged image names the export failure")
local recovered = assert(K.export(3, v, save, c.bytes))
eq(#K.r1Diff(3, v, c.bytes, recovered), 0, v .. ": a compatible explicit image can recover the embedded image")
end
local fresh = assert(K.import(3, v, c.bytes))
fresh.modData.cartImage = nil
check(K.export(3, v, fresh) ~= nil, v .. ": a save with no preserved image still exports fresh")
local retained = assert(K.import(3, v, c.bytes))
eq(#K.r1Diff(3, v, c.bytes, assert(K.export(3, v, retained, "PKCI1:131072:AAAA"))), 0,
v .. ": a valid embedded image can recover a damaged explicit image")
end
T.finish()