mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
123feef3a1
CLOSES #2425, CLOSES #2428, CLOSES #2431, CLOSES #2432, CLOSES #2433, CLOSES #2435, CLOSES #2436, CLOSES #2437, CLOSES #2440
232 lines
8.9 KiB
Lua
232 lines
8.9 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
|
|
|
|
local VoidFill = require("src.core.game3.void_fill")
|
|
|
|
local T = { 11, 12, 13, 14 }
|
|
local W = 21
|
|
local LAYOUTS = {
|
|
FR_PALLET_TOWN = {
|
|
pair = "pallet_outdoor", borderWidth = 2, borderHeight = 2,
|
|
borderMids = { T[1], T[2], T[3], T[4] },
|
|
},
|
|
FR_CINNABAR_ISLAND = {
|
|
pair = "sevii_outdoor", borderWidth = 2, borderHeight = 2,
|
|
borderMids = { W, W, W, W },
|
|
},
|
|
}
|
|
local lookups = 0
|
|
VoidFill.layoutFor = function(mapId)
|
|
lookups = lookups + 1
|
|
return LAYOUTS[mapId]
|
|
end
|
|
VoidFill.invalidate()
|
|
|
|
local function fill(mode, cx, cy, hasMid)
|
|
return VoidFill.fillAt(mode, cx, cy, hasMid, "general")
|
|
end
|
|
|
|
print("[test] 1. mode plumbing")
|
|
eq(VoidFill.normalize("trees"), "trees", "normalize trees")
|
|
eq(VoidFill.normalize("nope"), "map", "normalize unknown -> map")
|
|
eq(VoidFill.normalize(nil), "map", "normalize nil -> map")
|
|
eq(VoidFill.cycle("map", 1), "trees", "cycle map -> trees")
|
|
eq(VoidFill.cycle("trees", 1), "water", "cycle trees -> water")
|
|
eq(VoidFill.cycle("water", 1), "black", "cycle water -> black")
|
|
eq(VoidFill.cycle("black", 1), "map", "cycle black wraps to map")
|
|
eq(VoidFill.cycle("map", -1), "black", "cycle map backwards -> black")
|
|
eq(VoidFill.label("trees"), "TREES", "label trees")
|
|
eq(VoidFill.label("black"), "BLACK", "label black")
|
|
|
|
print("[test] 2. map / black branches")
|
|
eq(VoidFill.fillAt("map", 0, 0), nil, "map fill is nil")
|
|
eq(VoidFill.fillAt("map", 7, -3), nil, "map fill is nil off-origin")
|
|
eq(VoidFill.fillAt("black", 0, 0), false, "black fill is false")
|
|
eq(VoidFill.fillAt("black", -5, 9), false, "black fill is false off-origin")
|
|
|
|
print("[test] 3. tree border is the cart 2x2 (PalletTown/border.bin)")
|
|
eq(fill("trees", 0, 0), T[1], "trees (0,0)")
|
|
eq(fill("trees", 1, 0), T[2], "trees (1,0)")
|
|
eq(fill("trees", 0, 1), T[3], "trees (0,1)")
|
|
eq(fill("trees", 1, 1), T[4], "trees (1,1)")
|
|
eq(fill("trees", 2, 2), T[1], "trees (2,2) wraps to (0,0)")
|
|
eq(fill("trees", 43, 16), T[2], "trees (43,16) odd/even")
|
|
|
|
print("[test] 4. negative coords wrap non-negative")
|
|
eq(fill("trees", -1, -1), T[4], "trees (-1,-1)")
|
|
eq(fill("trees", -2, -2), T[1], "trees (-2,-2)")
|
|
eq(fill("trees", -1, 0), T[2], "trees (-1,0)")
|
|
eq(fill("trees", 0, -1), T[3], "trees (0,-1)")
|
|
|
|
print("[test] 5. water border is one metatile (CinnabarIsland/border.bin)")
|
|
for _, xy in ipairs({ { 0, 0 }, { 1, 0 }, { 0, 1 }, { -3, 7 }, { 12, -9 } }) do
|
|
eq(fill("water", xy[1], xy[2]), W,
|
|
string.format("water (%d,%d)", xy[1], xy[2]))
|
|
end
|
|
|
|
print("[test] 6. availability gate falls through to the map's own border")
|
|
eq(fill("trees", 0, 0, function() return false end), nil,
|
|
"trees gated off when the pair has no tree mids")
|
|
eq(fill("water", 0, 0, function() return false end), nil,
|
|
"water gated off when the pair lacks the water mid")
|
|
local partial = { [T[1]] = true, [T[2]] = true, [T[3]] = true }
|
|
eq(fill("trees", 1, 1, function(m) return partial[m] == true end), nil,
|
|
"no partial tree when one quadrant is missing")
|
|
eq(fill("trees", 1, 1, function() return true end), T[4],
|
|
"gate passes when every mid is packed")
|
|
eq(VoidFill.fillAt("black", 0, 0, function() return false end), false,
|
|
"gate does not apply to black")
|
|
|
|
print("[test] 7. regression: the fill is not a single metatile (#2309)")
|
|
local seen, n = {}, 0
|
|
for cy = 0, 1 do
|
|
for cx = 0, 1 do
|
|
local mid = fill("trees", cx, cy)
|
|
if not seen[mid] then
|
|
seen[mid] = true
|
|
n = n + 1
|
|
end
|
|
end
|
|
end
|
|
eq(n, 4, "a 2x2 tree window yields 4 distinct mids")
|
|
eq(VoidFill.borderFor("trees").w, 2, "tree border width")
|
|
eq(VoidFill.borderFor("trees").h, 2, "tree border height")
|
|
check(VoidFill.midFor == nil, "modal-scan midFor is gone")
|
|
check(VoidFill.TREE_BORDER == nil and VoidFill.WATER_BORDER == nil,
|
|
"no literal border tables in the module")
|
|
|
|
print("[test] 8. primary tileset gate (building pairs never get General mids)")
|
|
local all = function() return true end
|
|
eq(VoidFill.fillAt("trees", 0, 0, all, "building"), nil,
|
|
"trees on a building primary with every mid packed -> nil")
|
|
eq(VoidFill.fillAt("water", 0, 0, all, "building"), nil,
|
|
"water on a building primary with every mid packed -> nil")
|
|
eq(VoidFill.fillAt("trees", 0, 0, all, nil), nil, "unknown primary fails closed")
|
|
eq(VoidFill.fillAt("trees", 0, 0, all), nil, "omitted primary fails closed")
|
|
eq(VoidFill.fillAt("trees", 0, 0, all, "general"), T[1], "general primary passes")
|
|
eq(VoidFill.fillAt("black", 0, 0, all, "building"), false, "black ignores the primary")
|
|
eq(VoidFill.primaryFor("player_house"), "building", "primaryFor player_house")
|
|
eq(VoidFill.primaryFor("pallet_outdoor"), "general", "primaryFor pallet_outdoor")
|
|
eq(VoidFill.primaryFor("general__rom_082d4af4"), "general", "primaryFor auto general pair")
|
|
eq(VoidFill.primaryFor("building__rom_082d4bcc"), "building", "primaryFor auto building pair")
|
|
eq(VoidFill.primaryFor("no_such_pair"), nil, "primaryFor unknown pair")
|
|
eq(VoidFill.primaryFor(nil), nil, "primaryFor nil")
|
|
|
|
print("[test] 9. borders come from the cached layouts, resolved once")
|
|
VoidFill.invalidate()
|
|
lookups = 0
|
|
for cy = 0, 9 do
|
|
for cx = 0, 9 do fill("trees", cx, cy, all) end
|
|
end
|
|
eq(lookups, 1, "one layout lookup for 100 cells")
|
|
eq(VoidFill.borderFromLayout(nil), nil, "no layout -> no border")
|
|
eq(VoidFill.borderFromLayout({
|
|
pair = "player_house", borderWidth = 1, borderHeight = 1, borderMids = { T[1] },
|
|
}), nil, "a building-primary source layout is refused")
|
|
eq(VoidFill.borderFromLayout({
|
|
pair = "pallet_outdoor", borderWidth = 1, borderHeight = 1,
|
|
borderMids = { VoidFill.PRIMARY_MIDS },
|
|
}), nil, "a secondary-tileset mid is refused")
|
|
LAYOUTS.FR_PALLET_TOWN = nil
|
|
VoidFill.invalidate()
|
|
lookups = 0
|
|
eq(fill("trees", 0, 0, all), nil, "missing source layout falls through")
|
|
eq(fill("trees", 1, 0, all), nil, "missing source layout falls through again")
|
|
eq(lookups, 1, "the miss is cached until invalidate")
|
|
local realLayoutFor = VoidFill.layoutFor
|
|
VoidFill.layoutFor = function() return nil, true end
|
|
VoidFill.invalidate()
|
|
eq(fill("trees", 0, 0, all), nil, "no game yet falls through")
|
|
VoidFill.layoutFor = realLayoutFor
|
|
LAYOUTS.FR_PALLET_TOWN = {
|
|
pair = "pallet_outdoor", borderWidth = 2, borderHeight = 2,
|
|
borderMids = { T[1], T[2], T[3], T[4] },
|
|
}
|
|
eq(fill("trees", 0, 0, all), T[1], "a pending lookup is not cached as a miss")
|
|
|
|
print("[test] 10. cached Pallet / Cinnabar borders are usable (skipped without cache)")
|
|
local root = require("tests.game3_cache").root("native/layouts/FR_PALLET_TOWN.mid", { native = true })
|
|
local function readAll(path)
|
|
local f = io.open(path, "rb")
|
|
if not f then return nil end
|
|
local s = f:read("*a")
|
|
f:close()
|
|
return s
|
|
end
|
|
local blob = root and readAll(root .. "/native/layouts/FR_PALLET_TOWN.mid")
|
|
if not blob then
|
|
print("[skip] no current FireRed cache; canonical mids unverified against ROM")
|
|
else
|
|
local NativePack = require("src.import.gba.native_pack")
|
|
local decoded = NativePack.decodeMidLayout(blob)
|
|
check(decoded ~= nil, "decoded FR_PALLET_TOWN.mid")
|
|
local Versions = require("src.import.gba.versions")
|
|
local function pairOf(mapId)
|
|
local spec = Versions.MAPS and Versions.MAPS[mapId]
|
|
if spec and spec.pair then return spec.pair end
|
|
local NATIVE = root .. "/native/manifest.lua"
|
|
local chunk = loadfile(NATIVE)
|
|
local manifest = chunk and chunk() or {}
|
|
local info = manifest.layouts and manifest.layouts[mapId]
|
|
return info and info.pair
|
|
end
|
|
if decoded then
|
|
decoded.pair = pairOf("FR_PALLET_TOWN")
|
|
local b = VoidFill.borderFromLayout(decoded)
|
|
check(b ~= nil, "Pallet border accepted as a General border")
|
|
if b then
|
|
eq(b.w, 2, "Pallet border width")
|
|
eq(b.h, 2, "Pallet border height")
|
|
local distinct, saw = 0, {}
|
|
for _, mid in ipairs(b.mids) do
|
|
if not saw[mid] then saw[mid] = true; distinct = distinct + 1 end
|
|
end
|
|
eq(distinct, 4, "Pallet border is four distinct metatiles")
|
|
end
|
|
end
|
|
local cin = readAll(root .. "/native/layouts/FR_CINNABAR_ISLAND.mid")
|
|
if cin then
|
|
local d2 = NativePack.decodeMidLayout(cin)
|
|
if d2 then
|
|
d2.pair = pairOf("FR_CINNABAR_ISLAND")
|
|
local b2 = VoidFill.borderFromLayout(d2)
|
|
check(b2 ~= nil, "Cinnabar border accepted as a General border")
|
|
if b2 then
|
|
for i = 2, #b2.mids do
|
|
eq(b2.mids[i], b2.mids[1], "Cinnabar border is uniform [" .. i .. "]")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local house = readAll(root .. "/native/layouts/FR_PLAYERS_HOUSE_1F.mid")
|
|
if house then
|
|
local d3 = NativePack.decodeMidLayout(house)
|
|
if d3 then
|
|
d3.pair = pairOf("FR_PLAYERS_HOUSE_1F")
|
|
eq(VoidFill.primaryFor(d3.pair), "building", "player's house primary is building")
|
|
eq(VoidFill.borderFromLayout(d3), nil, "player's house border is refused as a source")
|
|
end
|
|
end
|
|
end
|
|
|
|
if failed > 0 then
|
|
print("[test] FAILED " .. failed)
|
|
os.exit(1)
|
|
end
|
|
print("[test] all passed")
|