mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
291 lines
12 KiB
Lua
291 lines
12 KiB
Lua
#!/usr/bin/env luajit
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
require("src.core.GameVersion").set("firered")
|
|
|
|
package.loaded["src.core.game3.audio"] = {
|
|
playSe = function() end,
|
|
playCry = function() end,
|
|
playSong = function() end,
|
|
stopAll = function() end,
|
|
}
|
|
|
|
local Cache = require("tests.game3_cache")
|
|
local Versions = require("src.import.gba.versions")
|
|
local Extract = require("src.import.gba.pokedex_chrome_extract")
|
|
local PokedexChrome = require("src.ui.game3.pokedex_chrome")
|
|
local Pokedex = require("src.ui.game3.pokedex")
|
|
|
|
print("[test] 1. versions.lua carries the area-page / size-page ROM offsets")
|
|
check(Versions.CACHE_VERSION >= 103, "CACHE_VERSION at or past the dex chrome import ("
|
|
.. tostring(Versions.CACHE_VERSION) .. ")")
|
|
eq(Extract.FORMAT_VERSION, 5, "pokedex FORMAT_VERSION bumped")
|
|
|
|
local gfxByFile = {}
|
|
for _, g in ipairs(Versions.POKEDEX_CHROME_GFX or {}) do gfxByFile[g.file] = g end
|
|
check(gfxByFile["map_kanto.rgba"] ~= nil, "map_kanto.rgba is extracted")
|
|
if gfxByFile["map_kanto.rgba"] then
|
|
local g = gfxByFile["map_kanto.rgba"]
|
|
-- pokefirered/src/pokedex_screen.c:678
|
|
eq(g.w, 96, "kanto map is 96 px wide")
|
|
eq(g.h, 72, "kanto map is 72 px tall")
|
|
eq(g.gfx, 0x443620, "kanto map comes from sTilemap_AreaMap_Kanto")
|
|
eq(g.lz, true, "kanto map is LZ77")
|
|
end
|
|
for _, name in ipairs({ "one", "two", "three" }) do
|
|
local g = gfxByFile["map_" .. name .. "_island.rgba"]
|
|
check(g and g.w == 32 and g.h == 24, "map_" .. name .. "_island is 32x24")
|
|
end
|
|
for _, name in ipairs({ "four", "five", "six", "seven" }) do
|
|
local g = gfxByFile["map_" .. name .. "_island.rgba"]
|
|
check(g and g.w == 32 and g.h == 32, "map_" .. name .. "_island is 32x32")
|
|
end
|
|
-- pokefirered/src/pokedex_screen.c:3133
|
|
check(gfxByFile["blit_wide_ellipse.rgba"] ~= nil, "AREA UNKNOWN ellipse is extracted")
|
|
if gfxByFile["blit_wide_ellipse.rgba"] then
|
|
local g = gfxByFile["blit_wide_ellipse.rgba"]
|
|
check(g.w == 88 and g.h == 16 and not g.lz, "wide ellipse is a raw 88x16 4bpp blit")
|
|
end
|
|
check(gfxByFile["mini_page.rgba"] ~= nil, "mini_page is extracted")
|
|
check(gfxByFile["caught_marker.rgba"] ~= nil, "caught_marker is extracted")
|
|
eq(#(Versions.POKEDEX_CATEGORY_ICONS or {}), 16, "all 16 category icons have offsets")
|
|
eq(Versions.POKEDEX_SILHOUETTE_PAL, 0x452368, "sPalette_Silhouette offset present")
|
|
-- pokefirered/src/pokedex_area_markers.c:219
|
|
eq(Versions.POKEDEX_MARKER_BLEND_EVA, 12, "BLDALPHA eva")
|
|
eq(Versions.POKEDEX_MARKER_BLEND_EVB, 8, "BLDALPHA evb")
|
|
|
|
-- pokefirered/src/pokedex_area_markers.c:41
|
|
local shapes = Versions.POKEDEX_AREA_MARKER_SHAPES or {}
|
|
eq(#shapes, 7, "seven area marker shapes")
|
|
local wantShapes = {
|
|
{ 0, 8, 8 }, { 1, 16, 8 }, { 3, 8, 16 }, { 5, 32, 16 },
|
|
{ 13, 16, 32 }, { 21, 32, 16 }, { 29, 16, 32 },
|
|
}
|
|
for i, w in ipairs(wantShapes) do
|
|
local s = shapes[i]
|
|
check(s and s.tile == w[1] and s.w == w[2] and s.h == w[3],
|
|
string.format("marker shape %d is tile %d, %dx%d", i - 1, w[1], w[2], w[3]))
|
|
end
|
|
|
|
print("[test] 2. bakeImage turns 4bpp tiles into RGBA with a transparent index 0")
|
|
check(type(Extract.bakeImage) == "function", "Extract.bakeImage present")
|
|
if type(Extract.bakeImage) == "function" then
|
|
local tile = {}
|
|
for i = 1, 32 do tile[i] = 0x11 end
|
|
tile[1] = 0x10
|
|
local pal = {}
|
|
for i = 0, 15 do pal[i] = 0 end
|
|
pal[0] = 0x7FFF
|
|
pal[1] = 0x001F
|
|
local out = Extract.bakeImage(tile, pal, 8, 8)
|
|
eq(#out, 8 * 8 * 4, "8x8 tile bakes to 256 RGBA bytes")
|
|
eq(out:byte(4), 0, "index 0 is transparent")
|
|
eq(out:byte(5), 255, "index 1 red channel is the palette color")
|
|
eq(out:byte(8), 255, "index 1 is opaque")
|
|
|
|
local opaque = Extract.bakeImage(tile, pal, 8, 8, { opaqueZero = true })
|
|
eq(opaque:byte(4), 255, "opaqueZero keeps index 0 visible")
|
|
eq(opaque:byte(1), 255, "opaqueZero index 0 uses the palette color")
|
|
|
|
local mask = Extract.bakeImage(tile, pal, 8, 8, { mask = true })
|
|
eq(mask:byte(4), 0, "mask index 0 is transparent")
|
|
eq(mask:byte(5), 255, "mask non-zero index is white")
|
|
eq(mask:byte(8), 255, "mask non-zero index is opaque")
|
|
|
|
local two = {}
|
|
for i = 1, 64 do two[i] = (i <= 32) and 0x00 or 0x11 end
|
|
local second = Extract.bakeImage(two, pal, 8, 8, { tile0 = 1, mask = true })
|
|
eq(second:byte(8), 255, "tile0=1 reads the second tile")
|
|
local first = Extract.bakeImage(two, pal, 8, 8, { tile0 = 0, mask = true })
|
|
eq(first:byte(4), 0, "tile0=0 reads the first tile")
|
|
end
|
|
|
|
print("[test] 3. ready() gates on the new area-page assets")
|
|
local files = {}
|
|
local fakeCache = {
|
|
read = function(_, rel) return files[rel] end,
|
|
}
|
|
local root = "R/" .. Extract.CACHE_SUB
|
|
local base = {
|
|
"manifest.lua", "entries.lua", "categories.lua", "orders.lua", "area_markers.lua",
|
|
Extract.TILE_SHEETS.kanto, Extract.TILE_SHEETS.national,
|
|
}
|
|
for _, f in ipairs(base) do files[root .. "/" .. f] = string.rep("x", 4096) end
|
|
check(Extract.ready(fakeCache, "R") == false, "ready() is false without the chrome graphics")
|
|
for _, f in ipairs({ Extract.CHROME_FILE, "map_kanto.rgba", "mini_page.rgba",
|
|
"blit_wide_ellipse.rgba", "marker_0.rgba", "cat_icon_grassland.rgba" }) do
|
|
files[root .. "/" .. f] = string.rep("x", 4096)
|
|
end
|
|
check(Extract.ready(fakeCache, "R") == false, "ready() is false without the dex page assets")
|
|
files[root .. "/" .. Extract.PAPER_BG_FILE] =
|
|
string.rep("x", Extract.PAPER_BG_W * Extract.PAPER_BG_H * 4)
|
|
for _, f in ipairs({ "1.rgba", "bulbasaur.rgba", Extract.FOOTPRINT_QUESTION_MARK_FILE }) do
|
|
files[root .. "/" .. Extract.FOOTPRINT_SUB .. "/" .. f] =
|
|
string.rep("x", Extract.FOOTPRINT_W * Extract.FOOTPRINT_H * 4)
|
|
end
|
|
check(Extract.ready(fakeCache, "R") == true, "ready() is true once they are present")
|
|
|
|
print("[test] 4. PokedexChrome reads the ROM-derived chrome colors")
|
|
check(type(PokedexChrome.getColor) == "function", "PokedexChrome.getColor present")
|
|
if type(PokedexChrome.getColor) == "function" then
|
|
PokedexChrome._installed = true
|
|
PokedexChrome._colors = nil
|
|
eq(PokedexChrome.getColor("marker"), nil, "no chrome.lua means no color")
|
|
PokedexChrome._colors = { marker = { 10, 20, 30, 40 }, silhouette = { 50, 60, 70 } }
|
|
local r, g, b, a = PokedexChrome.getColor("marker")
|
|
eq(math.floor(r * 255 + 0.5), 10, "marker red")
|
|
eq(math.floor(g * 255 + 0.5), 20, "marker green")
|
|
eq(math.floor(b * 255 + 0.5), 30, "marker blue")
|
|
eq(math.floor(a * 255 + 0.5), 40, "marker alpha passes through")
|
|
local sr, _, _, sa = PokedexChrome.getColor("silhouette")
|
|
eq(math.floor(sr * 255 + 0.5), 50, "silhouette grey")
|
|
eq(sa, 1, "silhouette has no alpha entry")
|
|
check(type(PokedexChrome.getMarkerBlend) == "function", "PokedexChrome.getMarkerBlend present")
|
|
if type(PokedexChrome.getMarkerBlend) == "function" then
|
|
eq(PokedexChrome.getMarkerBlend(), nil, "no marker_blend entry means no blend pair")
|
|
PokedexChrome._colors.marker_blend = { 4, 2 }
|
|
local eva, evb = PokedexChrome.getMarkerBlend()
|
|
eq(eva, 4 / 16, "eva is sixteenths")
|
|
eq(evb, 2 / 16, "evb is sixteenths")
|
|
end
|
|
PokedexChrome._colors = nil
|
|
end
|
|
|
|
print("[test] 5. Size comparison uses both pret sprites")
|
|
-- pokefirered/src/trainer_pokemon_sprites.c:276, include/constants/trainers.h:156
|
|
check(type(PokedexChrome.TRAINER_PIC_IDS) == "table", "trainer pic ids present")
|
|
if type(PokedexChrome.TRAINER_PIC_IDS) == "table" then
|
|
eq(PokedexChrome.TRAINER_PIC_IDS.male, 135, "TRAINER_PIC_RED")
|
|
eq(PokedexChrome.TRAINER_PIC_IDS.female, 136, "TRAINER_PIC_LEAF")
|
|
end
|
|
-- pokefirered/src/pokedex_screen.c:3113
|
|
check(type(Pokedex.silhouetteScale) == "function", "Pokedex.silhouetteScale present")
|
|
if type(Pokedex.silhouetteScale) == "function" then
|
|
eq(Pokedex.silhouetteScale(256), 1, "scale 256 draws 1:1")
|
|
eq(Pokedex.silhouetteScale(512), 0.5, "scale 512 draws at half size")
|
|
eq(Pokedex.silhouetteScale(128), 2, "scale 128 draws at double size")
|
|
eq(Pokedex.silhouetteScale(0), 1, "scale 0 falls back to 1:1")
|
|
eq(Pokedex.silhouetteScale(nil), 1, "missing scale falls back to 1:1")
|
|
end
|
|
check(type(Pokedex.playerGender) == "function", "Pokedex.playerGender present")
|
|
if type(Pokedex.playerGender) == "function" then
|
|
Pokedex._session = nil
|
|
eq(Pokedex.playerGender(), "male", "no session defaults to RED")
|
|
Pokedex._session = { gender = 1 }
|
|
eq(Pokedex.playerGender(), "female", "gender 1 is LEAF")
|
|
Pokedex._session = { gender = "female" }
|
|
eq(Pokedex.playerGender(), "female", "gender \"female\" is LEAF")
|
|
Pokedex._session = { gender = 0 }
|
|
eq(Pokedex.playerGender(), "male", "gender 0 is RED")
|
|
Pokedex._session = nil
|
|
end
|
|
|
|
print("[test] 6. Registration closes on A or B only (pokedex_screen.c:3427)")
|
|
local pressed = {}
|
|
local fakeInput = { wasPressed = function(_, b) return pressed[b] == true end }
|
|
|
|
local calls = 0
|
|
Pokedex.showRegistration(19, { onDone = function() calls = calls + 1 end })
|
|
eq(Pokedex.screen, "registration", "registration screen open")
|
|
pressed.start = true
|
|
Pokedex.handleInput(fakeInput)
|
|
eq(calls, 0, "START does not close the registration card")
|
|
eq(Pokedex.open, true, "registration card still open after START")
|
|
pressed.start = false
|
|
pressed.select = true
|
|
Pokedex.handleInput(fakeInput)
|
|
eq(calls, 0, "SELECT does not close the registration card")
|
|
pressed.select = false
|
|
pressed.b = true
|
|
Pokedex.handleInput(fakeInput)
|
|
eq(calls, 1, "B closes the registration card")
|
|
eq(Pokedex.open, false, "registration card closed on B")
|
|
pressed.b = false
|
|
|
|
calls = 0
|
|
Pokedex.showRegistration(19, { onDone = function() calls = calls + 1 end })
|
|
pressed.a = true
|
|
Pokedex.handleInput(fakeInput)
|
|
eq(calls, 1, "A closes the registration card")
|
|
pressed.a = false
|
|
|
|
print("[test] 7. Imported cache holds the area-page graphics")
|
|
local cacheRoot = Cache.root("meta.json")
|
|
if not cacheRoot then
|
|
print("[skip] no FireRed cache mounted")
|
|
else
|
|
local dexRoot = cacheRoot .. "/pokemon/pokedex"
|
|
local function slurp(rel)
|
|
local f = io.open(dexRoot .. "/" .. rel, "rb")
|
|
if not f then return nil end
|
|
local d = f:read("*a")
|
|
f:close()
|
|
return d
|
|
end
|
|
local kanto = slurp("map_kanto.rgba")
|
|
if not kanto then
|
|
print("[skip] cache predates the dex chrome import (CACHE_VERSION < 103)")
|
|
else
|
|
eq(#kanto, 96 * 72 * 4, "map_kanto.rgba is a 96x72 RGBA buffer")
|
|
local opaque = 0
|
|
for i = 4, #kanto, 4 do
|
|
if kanto:byte(i) ~= 0 then opaque = opaque + 1 end
|
|
end
|
|
check(opaque > 1000, "map_kanto.rgba has real opaque pixels (" .. opaque .. ")")
|
|
local ellipse = slurp("blit_wide_ellipse.rgba")
|
|
check(ellipse and #ellipse == 88 * 16 * 4, "blit_wide_ellipse.rgba is 88x16")
|
|
local marker = slurp("marker_0.rgba")
|
|
check(marker and #marker == 8 * 8 * 4, "marker_0.rgba is 8x8")
|
|
if marker then
|
|
local hit = false
|
|
for i = 4, #marker, 4 do
|
|
if marker:byte(i) == 255 then
|
|
hit = true
|
|
eq(marker:byte(i - 3), 255, "marker mask pixels are white")
|
|
break
|
|
end
|
|
end
|
|
check(hit, "marker_0.rgba has an opaque mask")
|
|
end
|
|
local chrome = slurp(Extract.CHROME_FILE)
|
|
check(chrome ~= nil, "chrome.lua present in the cache")
|
|
if chrome then
|
|
local tbl = assert(loadstring(chrome))()
|
|
eq(type(tbl.marker), "table", "chrome.lua carries the marker blend color")
|
|
eq(tbl.marker[4], 191, "marker alpha is 12/16")
|
|
eq(type(tbl.marker_blend), "table", "chrome.lua carries the BLDALPHA pair")
|
|
if type(tbl.marker_blend) == "table" then
|
|
eq(tbl.marker_blend[1], Versions.POKEDEX_MARKER_BLEND_EVA, "marker_blend eva")
|
|
eq(tbl.marker_blend[2], Versions.POKEDEX_MARKER_BLEND_EVB, "marker_blend evb")
|
|
end
|
|
eq(type(tbl.silhouette), "table", "chrome.lua carries the silhouette color")
|
|
end
|
|
local icon = slurp("cat_icon_grassland.rgba")
|
|
check(icon and #icon == 64 * 48 * 4, "cat_icon_grassland.rgba is 64x48")
|
|
local mini = slurp("mini_page.rgba")
|
|
check(mini and #mini == 64 * 40 * 4, "mini_page.rgba is 64x40")
|
|
end
|
|
end
|
|
|
|
print("")
|
|
if failed > 0 then
|
|
print("FAILED: " .. failed)
|
|
os.exit(1)
|
|
end
|
|
print("ALL PASS")
|