mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-28 23:00:21 -04:00
160 lines
7.1 KiB
Lua
160 lines
7.1 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
|
|
local ROM_PATH = os.getenv("POKEPORT_EMERALD_ROM") or "../pokeemerald/pokeemerald.gba"
|
|
local f = io.open(ROM_PATH, "rb")
|
|
if not f then
|
|
print("emerald_field_assets_test: skipped (no ROM at " .. ROM_PATH .. ")")
|
|
os.exit(0)
|
|
end
|
|
local raw = f:read("*a")
|
|
f:close()
|
|
if raw:sub(0xAD, 0xB0) ~= "BPEE" then
|
|
print("emerald_field_assets_test: skipped (not an Emerald ROM)")
|
|
os.exit(0)
|
|
end
|
|
|
|
local rom = { size = #raw }
|
|
function rom:get(o) return raw:byte(o + 1) end
|
|
function rom:u16(o) local a, b = raw:byte(o + 1, o + 2); return a + b * 256 end
|
|
function rom:u32(o) local a, b, c, d = raw:byte(o + 1, o + 4); return a + b * 256 + c * 65536 + d * 16777216 end
|
|
function rom:readString(o, n) return raw:sub(o + 1, o + n) end
|
|
function rom:readBytes(o, n) return { raw:byte(o + 1, o + n) } end
|
|
function rom:ptrOffset(p) if p >= 0x08000000 and p < 0x0A000000 then return p - 0x08000000 end end
|
|
function rom:clearCache() end
|
|
|
|
local files = {}
|
|
local cache = {}
|
|
function cache:write(rel, body) files[rel] = body; return true end
|
|
function cache:read(rel) return files[rel] end
|
|
function cache:exists(rel) return files[rel] ~= nil end
|
|
|
|
local ROOT = "gba"
|
|
local Versions = require("src.import.gba.versions")
|
|
Versions.select("emerald")
|
|
|
|
local function manifest(rel)
|
|
local body = files[ROOT .. "/" .. rel]
|
|
if not body then return nil end
|
|
return assert(load(body, "=" .. rel, "t", {}))()
|
|
end
|
|
|
|
local function fnv(s)
|
|
local h = 2166136261
|
|
local M = 4294967296
|
|
for i = 1, #s do
|
|
h = bit.bxor(h, s:byte(i)) % M
|
|
h = (bit.lshift(h, 24) % M + h * 403) % M
|
|
end
|
|
return string.format("%08x", h)
|
|
end
|
|
|
|
local mods = {
|
|
"src.import.gba.field_effect_extract", "src.import.gba.door_anim_extract", "src.import.gba.weather_extract",
|
|
"src.import.gba.cave_transition_extract", "src.import.gba.rse.berry_tree_gfx_extract",
|
|
"src.import.gba.rse.rotating_gate_gfx_extract",
|
|
}
|
|
for _, m in ipairs(mods) do
|
|
local mod = require(m)
|
|
local ok, err = pcall(mod.run, rom, cache, { cacheRoot = ROOT })
|
|
check(ok, m .. " runs: " .. tostring(err))
|
|
if mod.ready then check(mod.ready(cache, ROOT), m .. " ready after run") end
|
|
for _, rel in ipairs(mod.REQUIRED or {}) do
|
|
check(files[ROOT .. "/" .. rel] ~= nil, m .. " wrote " .. rel)
|
|
end
|
|
end
|
|
|
|
local fx = manifest("field_effects/objects.lua")
|
|
eq(fx.count, 37, "37 FLDEFFOBJ templates walked")
|
|
eq(#fx.extras, 13, "13 named extra templates")
|
|
local byName, sheets, colored = {}, 0, 0
|
|
for _, list in ipairs({ fx.objects, fx.extras }) do
|
|
for _, e in ipairs(list) do
|
|
byName[e.name] = e
|
|
if e.file then sheets = sheets + 1 end
|
|
if e.rgba then colored = colored + 1 end
|
|
end
|
|
end
|
|
eq(sheets, 49, "every template with images has an index sheet")
|
|
eq(colored, 48, "every sheet but the unused-tag Deoxys fragment has an rgba bake")
|
|
eq(fx.objects[1].name, "shadow_small", "FLDEFFOBJ_SHADOW_S first")
|
|
eq(fx.objects[37].name, "rayquaza", "FLDEFFOBJ_RAYQUAZA last")
|
|
for name, dims in pairs({
|
|
tall_grass = "16x16x5", surf_blob = "32x32x3", long_grass = "16x16x4", sand_footprints = "16x16x2",
|
|
deep_sand_footprints = "16x16x2", bike_tire_tracks = "16x16x4", ash = "16x16x5", bubbles = "16x32x8",
|
|
sparkle = "16x16x6", tree_disguise = "16x32x7", bird = "32x32x1", shadow_extra_large = "64x32x1",
|
|
arrow = "16x16x8", sand_pile = "16x8x3", exclamation_question_mark = "16x16x2", heart_icon = "16x16x1",
|
|
}) do
|
|
local e = byName[name]
|
|
eq(e and (e.fw .. "x" .. e.fh .. "x" .. e.frames), dims, name .. " frame layout")
|
|
end
|
|
eq(byName.tall_grass.palette, "gFieldEffectObjectPalette1", "tall grass uses FLDEFF_PAL_TAG_GENERAL_1")
|
|
eq(byName.splash.palette, "gFieldEffectObjectPalette0", "splash uses FLDEFF_PAL_TAG_GENERAL_0")
|
|
eq(byName.surf_blob.palette, "gObjectEventPal_Brendan", "surf blob uses the player slot")
|
|
eq(byName.record_mix_lights.palette, "sRecordMixLights_Pal", "record mix lights own palette")
|
|
eq(byName.reflection_distortion.file, nil, "reflection distortion has no images")
|
|
eq(#byName.sparkle.anims[1] > 6, true, "sparkle anim read from ROM")
|
|
eq(#files[ROOT .. "/field_effects/field_move_streaks_outdoors.rgba"], 256 * 80 * 4, "outdoor streaks baked")
|
|
|
|
local doors = manifest("doors/manifest.lua")
|
|
eq(doors.family, "rse", "door manifest is rse")
|
|
eq(doors.count, 53, "53 door rows before the terminator")
|
|
eq(doors.by_mid[0x248].tile, "littleroot", "METATILE_Petalburg_Door_Littleroot")
|
|
eq(doors.by_mid[0x2AD].size_type, 2, "multi corridor door is size 2")
|
|
eq(doors.doors.battle_tower_multi_corridor.width, 32, "big door frame is 32 wide")
|
|
eq(doors.doors.littleroot.height, 96, "three 16x32 frames")
|
|
local pair = doors.pairs["gTileset_General|gTileset_Petalburg"]
|
|
check(pair and pair.doors[0x248] and pair.doors[0x248].file == "littleroot__petalburg.rgba",
|
|
"Littleroot door keyed by its tileset pair")
|
|
eq(#doors.by_mid[0x21].pairs, 30, "general door present in every General pair")
|
|
eq(doors.by_mid[0x3B0].pairs[1], nil, "unused frontier door matches no tileset")
|
|
eq(#doors.anim.open, 4, "sDoorOpenAnimFrames rows")
|
|
eq(doors.by_mid[0x61].sound, "sliding", "Pokemon Center door slides")
|
|
eq(doors.by_mid[0x21B].sound, "arena", "Battle Arena door sound")
|
|
|
|
local w = manifest("weather/manifest.lua")
|
|
eq(w.blobs.rain.w .. "x" .. w.blobs.rain.h, "16x192", "rain sheet")
|
|
eq(w.blobs.ash.w .. "x" .. w.blobs.ash.h, "64x128", "ash sheet")
|
|
eq(w.blobs.sandstorm.w .. "x" .. w.blobs.sandstorm.h, "64x80", "sandstorm sheet")
|
|
eq(w.blobs.cloud.palette, "clouds", "clouds palette")
|
|
eq(table.concat(w.cycles.route119, ","), "2,3,5,3", "sWeatherCycleRoute119")
|
|
eq(table.concat(w.cycles.route123, ","), "2,2,3,2", "sWeatherCycleRoute123")
|
|
eq(#w.color_map_types, 32, "sBasePaletteColorMapTypes")
|
|
eq(#files[ROOT .. "/weather/drought_colors.bin"], 49152, "drought color maps")
|
|
eq(#files[ROOT .. "/weather/fog_horizontal.rgba"], 64 * 64 * 4, "FR-compatible fog sheet")
|
|
|
|
eq(#files[ROOT .. "/cave_transition/screen.bin"], 240 * 160, "cave transition screen")
|
|
|
|
local berries = manifest("berry_trees/manifest.lua")
|
|
eq(berries.count, 43, "43 berry trees")
|
|
eq(berries.trees[1].sheet, "cheri", "berry 1 is Cheri")
|
|
eq(berries.trees[43].sheet, "durin", "Enigma reuses the Durin tree")
|
|
eq(#berries.trees[1].frames, 9, "dirt, 2 sprout, 6 tree frames")
|
|
eq(berries.trees[1].height, 240, "frames stacked")
|
|
eq(#berries.trees[1].stages, 5, "five growth stages")
|
|
eq(berries.trees[2].stages[3].paletteSlot, 2, "Chesto stage 3 palette slot")
|
|
|
|
local gates = manifest("rotating_gates/manifest.lua")
|
|
eq(gates.count, 8, "8 gate shapes")
|
|
eq(gates.sheets[1].w, 32, "L1 is 32x32")
|
|
eq(gates.sheets[2].w, 64, "L2 is 64x64")
|
|
eq(#gates.puzzles.fortree, 8, "Fortree gates")
|
|
eq(gates.puzzles.trick_house[1].x, 14, "Trick House first gate x")
|
|
|
|
for rel, hash in pairs({
|
|
["field_effects/tall_grass.idx"] = "92fa72d7",
|
|
["field_effects/surf_blob.idx"] = "43843211",
|
|
["doors/littleroot.idx"] = "d73a2b9b",
|
|
["weather/rain.idx"] = "0f4fba30",
|
|
["berry_trees/pecha.idx"] = "9c3eca38",
|
|
["rotating_gates/gate_5.idx"] = "5a20b264",
|
|
}) do
|
|
local body = files[ROOT .. "/" .. rel]
|
|
eq(body and fnv(body), hash, rel .. " matches the pixel-verified sheet")
|
|
end
|
|
|
|
Versions.select("firered")
|
|
T.finish("emerald_field_assets_test")
|