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

147 lines
6.1 KiB
Lua

package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
local GameVersion = require("src.core.GameVersion")
local Versions = require("src.import.gba.versions")
local Constants = require("src.core.game3.constants")
local E = require("src.import.gba.extract_audio")
local function le16(v) return string.char(v % 256, math.floor(v / 256) % 256) end
local function le32(v)
return string.char(v % 256, math.floor(v / 256) % 256, math.floor(v / 65536) % 256, math.floor(v / 16777216) % 256)
end
local function song_table(count, titleId)
local parts = {}
for id = 0, count - 1 do
local player = 0
if id == 1 then player = 1 elseif id == 5 then player = 2 elseif id ~= titleId and id ~= 0 then player = 1 end
parts[#parts + 1] = le32(0x08001000 + id * 16) .. le16(player) .. le16(player)
end
return table.concat(parts)
end
do
local pad = string.rep("\0", 64)
local data = pad .. song_table(20, 12) .. pad
eq(E.findSongTable(data, 20, 12), 64, "scan finds the table")
eq(E.findSongTable(data, 20, 12, 64), 64, "hint accepted when it validates")
eq(E.findSongTable(data, 20, 12, 8), 64, "bad hint falls back to the scan, not to the hint")
eq(E.findSongTable(data, 20, 7), nil, "title id that is not a BGM entry finds nothing")
check(not E.isSongTable(data, 64, 20, 30), "title id past the count is rejected")
check(not pcall(E.findSongTable, data, 20), "probe needs a title id")
end
do
local em = Constants.of("emerald")
local sp = em.species.byName
local treeckoSlot = sp.SPECIES_TREECKO - 1
local n = sp.NUM_SPECIES - 1 - treeckoSlot
local tbl = {}
for i = 0, n - 1 do tbl[#tbl + 1] = le16(300 + i) end
local data = "\0\0\0\0" .. table.concat(tbl)
local ids = E.cryIdsFromTable(data, 4, n, sp, sp.NUM_SPECIES)
eq(ids[1], 0, "Bulbasaur -> cry 0")
eq(ids[sp.SPECIES_CELEBI], sp.SPECIES_CELEBI - 1, "Celebi keeps its own cry")
eq(ids[sp.SPECIES_CELEBI + 1], sp.SPECIES_UNOWN - 1, "old Unown slot -> Unown cry")
eq(ids[sp.SPECIES_TREECKO - 1], sp.SPECIES_UNOWN - 1, "last old Unown slot -> Unown cry")
eq(ids[sp.SPECIES_TREECKO], 300, "Treecko reads table entry 0")
eq(ids[sp.NUM_SPECIES - 1], 300 + n - 1, "last species reads the last table entry")
eq(ids[sp.NUM_SPECIES], nil, "no entry past the species count")
check(not pcall(E.cryIdsFromTable, data, 4, n - 1, sp, sp.NUM_SPECIES), "short table raises")
end
do
local em = Constants.of("emerald")
local data = le16(em.songs.byName.MUS_LEVEL_UP) .. le16(80) .. le16(em.songs.byName.MUS_AWAKEN_LEGEND) .. le16(710)
local ff = E.readFanfares(data, 0, 2, em)
eq(ff[em.songs.byName.MUS_LEVEL_UP].frames, 80, "fanfare frames")
eq(ff[em.songs.byName.MUS_AWAKEN_LEGEND].name, "MUS_AWAKEN_LEGEND", "fanfare name from constants")
end
do
local legacy = {
battleWild = 298, battleTrainer = 297, battleGymLeader = 296, battleChampion = 299,
victoryWild = 311, victoryTrainer = 310, victoryGymLeader = 312,
encounterBoy = 285, encounterGirl = 284, encounterRival = 315, encounterRocket = 283,
encounterGymLeader = 342, pokeCenter = 303, heal = 256, surf = 305, cycling = 282,
caught = 322, caughtIntro = 319, evolution = 264, evolutionIntro = 263, evolved = 259,
levelUp = 257, obtainItem = 258, followMe = 272, title = 278,
}
local roles = E.resolveRoles(Constants.of("firered"))
local n = 0
for k, v in pairs(roles) do
n = n + 1
eq(v, legacy[k], "FireRed role " .. k)
end
eq(n, 25, "FireRed resolves all 25 roles")
local V = Versions.forGame("emerald")
local em = E.resolveRoles(Constants.of("emerald"), V.AUDIO.roles)
eq(em.title, 413, "Emerald MUS_TITLE role")
eq(em.battleWild, 474, "Emerald MUS_VS_WILD role")
eq(em.underwater, 411, "Emerald MUS_UNDERWATER role")
eq(em.encounterRocket, nil, "FRLG-only role absent on Emerald")
end
do
local V = Versions.forGame("emerald")
local A = V.AUDIO
eq(A.song_count, 610, "610 songs")
eq(A.cry_count, 388, "388 cries")
eq(A.cry_table_reverse_count, 388, "388 reverse cries")
eq(A.fanfare_count, 18, "18 Emerald fanfares")
eq(A.cry_id_count, 135, "gSpeciesIdToCryId rows")
eq(A.fanfares, 0x5248BC, "sFanfares offset")
eq(A.cry_id_table, 0x31F61C, "gSpeciesIdToCryId offset")
eq(A.cry_table_reverse, 0x69EF24, "gCryTable_Reverse offset")
eq(require("src.import.gba.versions_frlg").AUDIO.fanfares, nil, "FRLG AUDIO keeps its keys")
end
do
local Plans = require("src.import.gba.plans.registry")
local plan = Plans.of("emerald")
local found
for _, t in ipairs(plan.tasks) do
if t.id == "audio" then found = t end
end
check(found and found.run == "steps" and found.steps[1].name == "extract_audio", "rse plan has the audio task")
local seq = false
for _, s in ipairs(plan.sequential) do seq = seq or s == "audio" end
check(seq, "audio task in the sequential order")
local req = {}
for _, p in ipairs(require("src.import.CacheContract").planFilesFor("emerald")) do req[p] = true end
check(req["data/generated/gba/audio/index.lua"], "Emerald cache requires audio/index.lua")
check(req["data/generated/gba/audio/samples.bin"], "Emerald cache requires audio/samples.bin")
end
do
local store = {}
local cache = {
read = function(_, p) return store[p] end,
exists = function(_, p) return store[p] ~= nil end,
}
check(not E.ready(cache, "g"), "not ready without meta")
store["g/audio/meta.json"] = '{"stub":true}'
store["g/audio/index.lua"] = "return {}"
check(not E.ready(cache, "g"), "stub marker is not ready")
local prev = Versions.active()
Versions.select("emerald")
store["g/audio/meta.json"] = ('{"version":%d,"sha1":""}'):format(Versions.AUDIO_VERSION)
check(E.ready(cache, "g"), "current meta + index is ready")
store["g/audio/meta.json"] = ('{"version":%d,"sha1":""}'):format(Versions.AUDIO_VERSION + 1)
check(not E.ready(cache, "g"), "stale audio version is not ready")
Versions.select(prev)
end
do
local prevV, prevG = Versions.active(), GameVersion.get()
Versions.select("emerald")
check(not pcall(E.run, "", { write = function() return true end }, {}), "no song table found raises")
Versions.select(prevV)
if prevG then GameVersion.set(prevG) end
end
T.finish("game3_emerald_audio_extract_test")