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

124 lines
5.3 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
local K = require("src.import.gba.rse.boot_gfx")
eq(select(1, K.objDims(0, 3)), 64, "square size 3 is 64 wide")
eq(select(2, K.objDims(1, 3)), 32, "wide size 3 is 32 tall")
eq(select(1, K.objDims(2, 2)), 16, "tall size 2 is 16 wide")
check(not pcall(K.objDims, 3, 0), "shape 3 is invalid")
local r, g, b = K.rgb8(0x7FFF)
eq(r + g + b, 765, "white BGR555")
r, g, b = K.rgb8(0x001F)
eq(r, 255, "red channel") eq(g, 0, "no green") eq(b, 0, "no blue")
local function tile4(fill)
return string.rep(string.char(fill * 17), 32)
end
local gfx = tile4(0) .. tile4(3)
local map = string.char(1, 0x20) .. string.rep("\0", 2046)
local idx, W, H = K.bakeText(gfx, map, 32, 32)
eq(W, 256, "text layer width") eq(H, 256, "text layer height")
eq(idx[1], 2 * 16 + 3, "bank 2 colour 3 packs to 35")
eq(idx[9], 0, "tile 0 colour 0 is transparent")
local asym = string.char(0x21) .. string.rep("\0", 31)
local flipMap = string.char(0, 0x04) .. string.rep("\0", 2046)
idx = K.bakeText(asym, flipMap, 32, 32)
eq(idx[1], 0, "hflip moves the pixel off column 0")
eq(idx[8], 1, "hflip puts column 0 at column 7")
local tall = K.bakeText(gfx, string.rep("\0", 2048) .. string.char(1, 0), 32, 64)
eq(#tall, 256 * 512, "256x512 layer size")
eq(tall[256 * 256 + 1], 3, "second screen block starts at row 32")
local wide = K.bakeText(gfx, string.rep("\0", 2048) .. string.char(1, 0), 64, 32)
eq(wide[257], 3, "second screen block of a 512-wide map starts at column 32")
local g8 = string.rep("\0", 64) .. string.rep(string.char(200), 64)
local aff, AW = K.bakeAffine(g8, string.char(1) .. string.rep("\0", 1023), 32)
eq(AW, 256, "affine 256") eq(aff[1], 200, "8bpp affine keeps the full index") eq(aff[9], 0, "tile 0 index 0")
local spr = K.bakeSprite(string.rep("\0", 64) .. string.rep(string.char(7), 64), 8, 8, 2, 8)
eq(spr[1], 7, "8bpp sprite tile offsets count 32-byte units")
local s4 = K.bakeSprite(tile4(0) .. tile4(5), 16, 8, 0, 4)
eq(s4[1], 0, "1D mapping left tile") eq(s4[9], 5, "1D mapping right tile")
local stacked, SW, SH = K.stack({ { 1, 2 }, { 3, 4 } }, 2, 1)
eq(SW, 2, "stack width") eq(SH, 2, "stack height") eq(stacked[3], 3, "second frame below the first")
local png = K.encodeIndexed(3, 2, { 0, 1, 2, 2, 1, 0 }, { [0] = 0, 0x7FFF, 0x001F }, true)
local pw, ph = K.pngSize(png)
eq(pw, 3, "png width") eq(ph, 2, "png height")
check(png:find("PLTE", 1, true) ~= nil, "indexed png has PLTE")
check(png:find("tRNS", 1, true) ~= nil, "transparent png has tRNS")
check(K.encodeIndexed(1, 1, { 0 }, { [0] = 0 }, false):find("tRNS", 1, true) == nil, "opaque png has no tRNS")
eq(select(1, K.pngSize(K.encodeGray(4, 5, K.blank(4, 5)))), 4, "gray png width")
local stored = K.zlibStored(string.rep("a", 70000))
eq(stored:byte(1), 0x78, "zlib header")
eq(#stored, 2 + 70000 + 5 * 2 + 4, "two stored blocks plus adler")
local mods = {
"extract_intro_emerald", "intro_credits_gfx_extract", "extract_title_rse",
"extract_birch_rse", "extract_naming_rse", "extract_wallclock_rse",
}
local seen, all = {}, {}
for _, name in ipairs(mods) do
local M = require("src.import.gba.rse." .. name)
check(type(M.run) == "function" and type(M.ready) == "function", name .. " exposes run/ready")
check(#M.REQUIRED > 1, name .. " exports REQUIRED")
eq(M.REQUIRED[1], M.SUB .. "/manifest.lua", name .. " manifest is first")
for _, rel in ipairs(M.REQUIRED) do
check(not seen[rel], "unique required path " .. rel)
seen[rel] = true
all[#all + 1] = rel
end
end
local plan = require("src.import.gba.plans.rse.boot")
local Plans = require("src.import.gba.plans.registry")
local stepMods = {}
for _, t in ipairs(plan.tasks) do
eq(t.run, "steps", t.id .. " is a steps task")
check(t.id:match("^em_boot_") ~= nil, t.id .. " is namespaced")
for _, s in ipairs(t.steps) do
local modName = Plans.moduleFor(s.name)
check(pcall(require, modName), modName .. " resolves")
stepMods[modName] = true
end
end
for _, name in ipairs(mods) do
check(stepMods["src.import.gba.rse." .. name], name .. " is in the boot plan")
end
local okCC, CacheContract = pcall(require, "src.import.CacheContract")
if okCC and CacheContract.planFilesFor then
local files = {}
for _, f in ipairs(CacheContract.planFilesFor("emerald")) do files[f] = true end
local missing = 0
for _, rel in ipairs(all) do
if not files["data/generated/gba/" .. rel] then missing = missing + 1 end
end
eq(missing, 0, "every boot REQUIRED path is in the emerald cache contract")
end
local V = require("src.import.gba.games.emerald")
check(type(V.NAMING) == "table" and V.NAMING.menu_gfx ~= nil, "emerald NAMING keys")
eq(V.NAMING.rival_gfx, nil, "no FRLG rival naming art on emerald")
eq(V.BOOT_RSE_FORMAT, K.FORMAT, "boot format stamp")
local memo = {}
local fakeCache = { read = function(_, rel) return memo[rel] end }
check(not K.ready("title", fakeCache, "data/generated/gba"), "not ready without a manifest")
memo["data/generated/gba/title/manifest.lua"] = "return {\n format = " .. K.FORMAT .. ",\n}\n"
check(K.ready("title", fakeCache, "data/generated/gba"), "ready with a current manifest")
memo["data/generated/gba/title/manifest.lua"] = "return {\n format = 0,\n}\n"
check(not K.ready("title", fakeCache, "data/generated/gba"), "stale format is not ready")
T.finish("game3_emerald_boot_assets_test")