Overlay pointer-table code Inferred
Sister format to MIPS overlay code: the same family of on-disc code blobs (overlays - chunks of MIPS code the game loads into RAM on demand), but instead of starting with a function prologue, these start with a table of function pointers, then the code those pointers reference. 42 PROT entries (entries of PROT.DAT, the disc's main asset archive) match. A few of them open with an ASCII title like Hell's Music - which reads like a song name but is actually the battle special attack the overlay implements.
Overview
Implementation: crates/asset/src/overlay_ptr_table.rs.
Layout
+0x00 u32 ptr_0 ; address in 0x801C0000..=0x801FFFFF (overlay window)
+0x04 u32 ptr_1 ; same range
...
+N*4 u32 ptr_{N-1} ; last pointer (still in range)
+(N+1)*4 ... ; first non-pointer u32 (typically MIPS code, sometimes
; a `jr ra; nop` stub or a leading ASCII string)
The overlay window 0x801C0000..0x801FFFFF is the RAM region reserved for swappable code - a valid pointer into it is a strong sign the blob expects to live there. The pointer-table length N ranges from 4 to 64. Tables come in three shapes:
- Function entry-point tables - small, monotonic; one slot per public function.
- Switch dispatch tables - larger, repeating; emitted by the C compiler for
switch(x)over a dense integer range. - Per-mode actor vtables - small alphabet of 3 distinct values across N slots.
Detection
Two checks produce zero overlap with already-named formats:
- The first u32 is in
0x801C0000..=0x801FFFFF(the overlay window). - The run of consecutive overlay-pointer u32s is between 4 and 64 long.
Monotonicity is not required - switch dispatch tables legitimately contain repeating handler addresses.
Cluster anatomy
All matches cluster in the 0900..=0968_xxx_dat PROT range - sized 14 KB to 160 KB. Pointer-range distribution:
- 30 entries:
0x801F6Axx–0x801F71xxcluster (small function-entry tables, monotonic, 5–14 entries). - 12 entries:
0x801F84xx+cluster (some monotonic, some switch dispatch with repeating handlers).
A handful of entries lead with an ASCII title string before the pointer table - the title is the battle special-attack name the overlay stages, not a song. Per entry (extraction numbering):
0907_xxx_dat.BIN"Hell's Music" - Nighto's summon stager, capture-pinned on the spell-0x85slot of the summon loader's903..=913range; the SCUS spell table carries the same name, andsummon.dat's attack-name records list it parallel to Gimard's "Burning Attack".0924_xxx_dat.BIN"Ultimate Rave" and0927_xxx_dat.BIN"Dark Eclipse" - the same attack-titled, stager-shaped family (part-spawn call census matches the pinned stagers); their loader callsites are computed rather than constant, but which action ids drive them is no longer open - both are capture-pinned mid-cast off the loader-B current id: 0924 is the rare-Seru flute summon Lippian (spell0x96) and 0927 is Juggernaut on Evil Seru Magic (spell0x99).0957_xxx_dat.BIN- a different shape: its head is a summon string table (Dies/Puera/Both/Damage/Recover) followed by an absolute-pointer table and code - the slot-Bsummon_effect_tableoverlay.
All the attack-titled entries sit in the static overlay map at slot-B base 0x801F69D8. The titles look like song names ("Hell's Music" reads like a Disco King dance track) but they are not: the dance overlay (0980) contains zero slot-B loader callsites - its music is sequenced BGM. See static overlay pipeline.
Reading the format
use legaia_asset::overlay_ptr_table;
if let Some(t) = overlay_ptr_table::detect(buf) {
println!("Overlay pointer table: {} entries, first=0x{:08x}, last=0x{:08x}",
t.count, t.first_ptr, t.last_ptr);
}
Each entry can be imported into Ghidra (the disassembler this project uses) via scripts/ghidra-analysis/bulk-import-overlays.sh once the load address is determined; the pointer values bound the load address from above (<= min_ptr).