Files
gen1recomp/tests/emerald_ow_test.lua
T
bryanthaboi 690f0cd9a1 TOUCHDOWN
2026-09-27 16:07:31 -04:00

146 lines
5.9 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
local PRET = os.getenv("POKEPORT_POKEEMERALD") or "../pokeemerald"
local ROM_PATH = os.getenv("POKEPORT_EMERALD_ROM") or (PRET .. "/pokeemerald.gba")
local f = io.open(ROM_PATH, "rb")
if not f then
print("emerald_ow_test: skipped (no ROM at " .. ROM_PATH .. ")")
os.exit(0)
end
local data = f:read("*a")
f:close()
local rom = { md5 = "f3ae088181bf583e55daf962a92bb46f4f1d07b7", size = #data }
function rom:get(o) return data:byte(o + 1) end
function rom:u16(o) local a, b = data:byte(o + 1, o + 2); return a + b * 256 end
function rom:u32(o)
local a, b, c, d = data:byte(o + 1, o + 4)
return a + b * 256 + c * 65536 + d * 16777216
end
function rom:readString(o, n) return data:sub(o + 1, o + n) end
function rom:readBytes(o, n)
local t = {}
for i = 1, n do t[i] = data:byte(o + i) end
return t
end
function rom:ptrOffset(p)
if not p or p < 0x08000000 or p >= 0x0A000000 then return nil end
return p - 0x08000000
end
local files = {}
local cache = {
write = function(_, rel, s) files[rel] = s; return true end,
read = function(_, rel) return files[rel] end,
exists = function(_, rel) return files[rel] ~= nil end,
}
local GameVersion = require("src.core.GameVersion")
GameVersion.set("emerald")
local Versions = require("src.import.gba.versions")
Versions.select("emerald")
local S = Versions.SYMS
local OwExtract = require("src.import.gba.ow_extract")
local ROOT = "data/generated/gba"
eq(Versions.NUM_OBJ_EVENT_GFX, 239, "239 Emerald object event graphics")
local res = OwExtract.run(rom, cache, { cacheRoot = ROOT })
eq(res.count, 239, "every gfx id extracted")
eq(res.total, 239, "manifest total")
check(res.avatars, "avatar table written")
check(OwExtract.ready(cache, ROOT), "step ready after run")
for _, rel in ipairs(OwExtract.REQUIRED) do
check(files[ROOT .. "/" .. rel] ~= nil, "wrote " .. rel)
end
local manifest = assert(load(files[ROOT .. "/ow/manifest.lua"], "@manifest", "t", {}))()
eq(manifest.ow_version, Versions.OW_VERSION, "manifest stamp")
eq(manifest.count, 239, "manifest count")
local pals = OwExtract.loadPaletteTable(rom)
local np = 0
for _ in pairs(pals) do np = np + 1 end
eq(np, Versions.OW_SPRITE_PALETTE_COUNT - 1, "every sObjectEventSpritePalettes entry loaded")
local frameMismatch, missingPal, blobBad = 0, 0, 0
for gid = 0, 238 do
local s = manifest.sprites[gid]
local info = rom:u32(Versions.OW_GFX_POINTERS + gid * 4) - 0x08000000
local images = rom:u32(info + 0x1C) - 0x08000000
local want
for _, n in ipairs(S.namesAt(images)) do
if n:find("PicTable", 1, true) then want = S.size(n) / 8 end
end
if not s or s.frameCount ~= want then frameMismatch = frameMismatch + 1 end
if s and not pals[s.paletteTag] then missingPal = missingPal + 1 end
local rgba = files[ROOT .. "/ow/" .. gid .. ".rgba"]
if not (s and rgba and #rgba == s.atlasW * s.atlasH * 4 and s.atlasH == s.height * s.frameCount) then
blobBad = blobBad + 1
end
local meta = OwExtract.decodeMeta(files[ROOT .. "/ow/" .. gid .. ".meta"])
if not meta or meta.graphicsId ~= gid then blobBad = blobBad + 1 end
end
eq(frameMismatch, 0, "frame counts equal pret pic table lengths")
eq(missingPal, 0, "every sprite palette tag resolves")
eq(blobBad, 0, "every rgba sheet and meta blob is well formed")
local av = manifest.avatars
local want = {
NORMAL = { Versions.OW_PLAYER_MALE, Versions.OW_PLAYER_FEMALE },
MACH_BIKE = { Versions.OW_PLAYER_MALE_BIKE, Versions.OW_PLAYER_FEMALE_BIKE },
ACRO_BIKE = { Versions.OW_PLAYER_MALE_ACRO_BIKE, Versions.OW_PLAYER_FEMALE_ACRO_BIKE },
SURFING = { Versions.OW_PLAYER_MALE_SURF, Versions.OW_PLAYER_FEMALE_SURF },
UNDERWATER = { Versions.OW_PLAYER_MALE_UNDERWATER, Versions.OW_PLAYER_FEMALE_UNDERWATER },
FIELD_MOVE = { Versions.OW_PLAYER_MALE_FIELD_MOVE, Versions.OW_PLAYER_FEMALE_FIELD_MOVE },
FISHING = { Versions.OW_PLAYER_MALE_FISH, Versions.OW_PLAYER_FEMALE_FISH },
WATERING = { Versions.OW_PLAYER_MALE_WATERING, Versions.OW_PLAYER_FEMALE_WATERING },
}
eq(#av.player, 8, "8 player avatar states")
eq(#av.rival, 8, "8 rival avatar states")
for _, row in ipairs(av.player) do
local w = want[row.state]
check(w and row.male == w[1] and row.female == w[2], "player " .. row.state .. " gfx matches event_objects.h")
end
eq(av.rival[1].male, 100, "rival Brendan normal")
eq(av.rival[1].female, 105, "rival May normal")
eq(#av.stateFlags.male, 5, "5 gfx-to-state flag rows per gender")
eq(av.stateFlags.male[1].gfx, Versions.OW_PLAYER_MALE, "state flag row 0 is Brendan normal")
eq(av.stateFlags.female[4].gfx, Versions.OW_PLAYER_FEMALE_SURF, "state flag row 3 is May surfing")
local function png_indices(path, fw, fh, frames)
local cmd = string.format(
"python3 -c \"import sys\nfrom PIL import Image\nim=Image.open(sys.argv[1]);px=im.load()\n"
.. "for f in range(%d):\n print(''.join('%%x'%%(px[f*%d+x,y]&15) for y in range(%d) for x in range(%d)))\" %s 2>/dev/null",
frames, fw, fh, fw, path)
local p = io.popen(cmd)
if not p then return nil end
local out = {}
for line in p:lines() do out[#out + 1] = line end
p:close()
if #out ~= frames then return nil end
return out
end
local pals2 = OwExtract.loadPaletteTable(rom)
for _, spec in ipairs({
{ gid = Versions.OW_PLAYER_MALE, png = PRET .. "/graphics/object_events/pics/people/brendan/walking.png" },
{ gid = Versions.OW_PLAYER_FEMALE, png = PRET .. "/graphics/object_events/pics/people/may/walking.png" },
}) do
local spr = assert(OwExtract.extractOne(rom, spec.gid, pals2))
local ref = png_indices(spec.png, spr.width, spr.height, 9)
if not ref then
print("emerald_ow_test: pixel spot check skipped (no python3/PIL or pret png)")
else
for fi = 1, 9 do
local got = {}
for p = 1, spr.width * spr.height do got[p] = string.format("%x", spr.frames[fi][p]) end
eq(table.concat(got), ref[fi], "gfx " .. spec.gid .. " walk frame " .. (fi - 1) .. " pixel exact vs pret png")
end
end
end
T.finish("emerald_ow_test")