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

212 lines
9.1 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_places_test: skipped (no ROM at " .. ROM_PATH .. ")")
os.exit(0)
end
local data = f:read("*a")
f:close()
local rom = {}
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:ptrOffset(p)
if not p or p < 0x08000000 or p >= 0x0A000000 then return nil end
return p - 0x08000000
end
local cache = { files = {} }
function cache:write(rel, bytes) self.files[rel] = bytes; return true end
function cache:read(rel) return self.files[rel] end
function cache:exists(rel) return self.files[rel] ~= nil end
local function loadLua(rel)
local src = assert(cache:read(rel), "missing " .. rel)
return assert(load(src, "@" .. rel, "t", {}))()
end
local function readFile(path)
local h = io.open(path, "rb")
if not h then return nil end
local s = h:read("*a")
h:close()
return s
end
local GameVersion = require("src.core.GameVersion")
local before = GameVersion.get()
GameVersion.set("emerald")
local Versions = require("src.import.gba.versions")
Versions.select("emerald")
local C = require("src.core.game3.constants").of("emerald")
local ROOT = "gba"
local EncountersExtract = require("src.import.gba.encounters_extract")
local enc = EncountersExtract.run(rom, cache, { cacheRoot = ROOT })
eq(enc.headers, 124, "124 wild headers")
eq(enc.extraHeaders, 11, "7 pyramid + 4 pike headers")
local wild = loadLua(ROOT .. "/encounters.lua")
local r101 = C:map("MAP_ROUTE101")
local w101 = wild[r101.group .. ":" .. r101.num]
check(w101 and w101.land, "route 101 land table")
eq(w101 and w101.land.rate, 20, "route 101 land rate")
eq(w101 and w101.land.slots[1].species, C.species.byName.SPECIES_WURMPLE, "route 101 slot 0 wurmple")
check(wild.EM_ROUTE101 ~= nil, "EM_ROUTE101 alias")
local cave = wild.EM_ALTERING_CAVE
eq(cave and cave.variants and #cave.variants, 9, "altering cave has 9 header variants")
local groups = 0
for k in pairs(wild) do
if type(k) == "string" and k:match("^%d+:%d+$") then groups = groups + 1 end
check(not k:find("^FR_"), "no FR alias " .. k)
end
eq(groups, 116, "116 distinct wild maps")
local extra = loadLua(ROOT .. "/wild_extra.lua")
eq(#extra.headerSets.pyramid, 7, "7 pyramid rounds")
eq(#extra.headerSets.pike, 4, "4 pike rooms")
eq(extra.headerSets.pyramid[1].mapNum, 1, "pyramid round index 1")
eq(extra.feebas.mon.species, C.species.byName.SPECIES_FEEBAS, "feebas species")
eq(extra.feebas.mon.minLevel, 20, "feebas min level")
eq(extra.feebas.mon.maxLevel, 25, "feebas max level")
eq(#extra.feebas.sections, 3, "3 route 119 sections")
eq(extra.feebas.sections[3].spotBase, 131 + 167, "section 3 spot base")
eq(extra.feebas.sections[3].yMax, 139, "section 3 y max")
eq(#extra.alteringCaveHeldItems, 9, "9 altering cave rows")
eq(extra.alteringCaveHeldItems[2].species, C.species.byName.SPECIES_MAREEP, "altering cave row 1 mareep")
eq(extra.alteringCaveHeldItems[2].item, C.items.byName.ITEM_GANLON_BERRY, "altering cave row 1 ganlon")
eq(extra.alteringCaveHeldItems[9].item, C.items.byName.ITEM_SALAC_BERRY, "altering cave row 8 salac")
local Heal = require("src.import.gba.heal_locations_extract")
local okH, detail = Heal.run(rom, cache, { cacheRoot = ROOT, force = true, strict = true })
check(okH, "heal locations extract")
eq(detail.healLocations, 22, "22 heal rows")
eq(detail.flyDestinations, 16, "16 fly destinations")
local heal = loadLua(ROOT .. "/region_map/heal_locations.lua")
eq(heal.model, "heal_row", "heal row model")
eq(heal.whiteout[1].map, "EM_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F", "heal row 1 map")
eq(heal.whiteout[1].x, 4, "heal row 1 x")
eq(heal.whiteout[1].y, 2, "heal row 1 y")
eq(heal.whiteout[3].map, "EM_PETALBURG_CITY", "heal row 3 petalburg")
eq(heal.whiteout[22].map, "EM_BATTLE_FRONTIER_OUTSIDE_EAST", "heal row 22 battle frontier")
for id, row in pairs(heal.whiteout) do
eq(row.healerLocalId, nil, "no healer npc on row " .. id)
end
local fly = loadLua(ROOT .. "/region_map/fly_destinations.lua")
local f0 = fly.fly_destinations[0] or fly.fly_destinations.MAPSEC_LITTLEROOT_TOWN
eq(f0 and f0.healLocation, 1, "mapsec 0 flies to heal location 1")
eq(f0 and f0.townMap, "EM_LITTLEROOT_TOWN", "mapsec 0 town map")
local warps = 0
for _ in pairs(fly.map_warps) do warps = warps + 1 end
eq(warps, 34, "34 route mapsecs warp to their map")
local MapSections = require("src.import.gba.rse.map_sections_extract")
local ms = MapSections.run(rom, cache, { cacheRoot = ROOT })
eq(ms.sections, 213, "213 region map entries")
eq(ms.themes, 6, "6 popup themes")
check(MapSections.ready(cache, ROOT), "map sections ready")
local pack = MapSections.load(cache:read(ROOT .. "/" .. MapSections.SECTIONS_REL))
eq(pack.sections[0].name, "LITTLEROOT TOWN", "mapsec 0 LITTLEROOT TOWN")
eq(pack.sections[0].x, 4, "mapsec 0 x")
eq(pack.sections[0].y, 11, "mapsec 0 y")
eq(pack.sections[0].theme, "wood", "littleroot popup wood")
eq(pack.sections[7].theme, "brick", "petalburg popup brick")
eq(pack.sections[88].name, "PALLET TOWN", "kanto section name")
eq(pack.KANTO_MAPSEC_COUNT, 109, "109 kanto mapsecs")
local themed = 0
for i = 0, pack.count - 1 do
if pack.sections[i].theme then themed = themed + 1 end
end
eq(themed, 213, "every section resolves a theme")
local manifest = loadLua(ROOT .. "/chrome/map_popup/manifest.lua")
eq(#manifest.palettes.wood, 16, "wood palette")
eq(#manifest.underwaterPalette, 16, "underwater palette")
for _, t in ipairs(MapSections.THEMES) do
eq(#cache:read(ROOT .. "/chrome/map_popup/" .. t .. ".rgba"), 80 * 24 * 4, t .. " rgba size")
eq(#cache:read(ROOT .. "/chrome/map_popup/" .. t .. "_outline.idx"), 80 * 24, t .. " outline idx size")
end
local header = readFile(PRET .. "/include/constants/region_map_sections.h")
local popupSrc = readFile(PRET .. "/src/map_name_popup.c")
if header and popupSrc then
local enum, idx = {}, {}
for name in header:gmatch("\n%s*(MAPSEC_[%w_]+),") do
enum[#enum + 1] = name
idx[name] = #enum - 1
end
local kc = idx.MAPSEC_SPECIAL_AREA - idx.MAPSEC_PALLET_TOWN + 1
local ids = { WOOD = 0, MARBLE = 1, STONE = 2, BRICK = 3, UNDERWATER = 4, STONE2 = 5 }
local body = popupSrc:match("sMapSectionToThemeId%[.-%]%s*=%s*(%b{})")
local expect = {}
for name, sub, theme in body:gmatch("%[(MAPSEC_[%w_]+)(%s*%-?%s*[%w_]*)%]%s*=%s*MAPPOPUP_THEME_([%w_]+)") do
local s = idx[name]
if sub:find("KANTO_MAPSEC_COUNT", 1, true) then s = s - kc end
expect[s] = ids[theme]
end
local bad = 0
for i = 0, 103 do
local sec = i
if i >= idx.MAPSEC_PALLET_TOWN then sec = i + kc end
if (expect[i] or 0) ~= pack.sections[sec].themeId then bad = bad + 1 end
end
eq(bad, 0, "theme ids match pret sMapSectionToThemeId")
end
local Multichoice = require("src.import.gba.multichoice_extract")
Multichoice.run(rom, cache, { cacheRoot = ROOT })
local lists = loadLua(ROOT .. "/" .. Multichoice.CACHE_REL)
local n = 0
for i = 0, 200 do if lists[i] then n = n + 1 end end
eq(n, 114, "114 multichoice lists")
eq(lists[0].labels[1], "PETALBURG", "MULTI_SSTIDAL_SLATEPORT_WITH_BF first label")
eq(lists[12].labels[2], "ACRO", "MULTI_BIKE acro")
eq(lists[113].labels[1], "NORMAL TAG MATCH", "MULTI_TAG_MATCH_TYPE")
eq(lists[113].count, 5, "tag match 5 rows")
eq(lists[86].labels[5], "", "forced start menu blank player row")
eq(lists[95].labels[4], "{PKMN} TYPE & NO.", "PKMN ligature survives")
local qmark = 0
for byte, ch in pairs(require("src.core.game3.scripting.text_ir").CHARMAP) do
if ch == "?" then qmark = byte end
end
for i = 0, 113 do
local listOff = rom:u32(Versions.MULTICHOICE_LISTS + i * 8) - 0x08000000
for a, s in ipairs(lists[i].labels) do
local textOff = rom:u32(listOff + (a - 1) * 8) - 0x08000000
local want = 0
for k = 0, 63 do
local b = rom:get(textOff + k)
if b == 0xFF then break end
if b == qmark then want = want + 1 end
end
local _, got = s:gsub("%?", "")
eq(got, want, ("list %d label %d decodes cleanly: %s"):format(i, a, s))
end
end
local Marts = require("src.import.gba.marts_extract")
local S = Versions.SYMS
local scripts = {
a = { { op = "pokemartdecoration2", S.addr("LilycoveCity_DepartmentStore_5F_Pokemart_Dolls") } },
b = { { op = "pokemart", S.addr("OldaleTown_Mart_Pokemart_Basic") } },
}
local marts = Marts.build(rom, scripts)
local dolls = marts[S.addr("LilycoveCity_DepartmentStore_5F_Pokemart_Dolls")]
eq(dolls and dolls.kind, "decorations", "lilycove dolls tagged")
eq(dolls and C:name("decorations", dolls.items[1], "DECOR_"), "DECOR_PICHU_DOLL", "dolls list first DECOR")
eq(dolls and C:name("decorations", dolls.items[2], "DECOR_"), "DECOR_PIKACHU_DOLL", "dolls list second DECOR")
local basic = marts[S.addr("OldaleTown_Mart_Pokemart_Basic")]
eq(basic and basic.kind, nil, "item mart untagged")
eq(basic and C:name("items", basic.items[1], "ITEM_"), "ITEM_POTION", "oldale first item")
GameVersion.set(before)
T.finish("emerald_places_test")