MIPS overlay code Inferred
Some entries of PROT.DAT (the disc's main asset archive) contain not game assets but code: overlays, chunks of MIPS machine code the game loads into RAM on demand because the whole engine does not fit in memory at once. Most of Legaia's game logic lives in overlays rather than the main executable. This page covers how the project recognises the smaller specialised code blobs in the 0901..=0969_xxx_dat range - distinct from the big full-mode overlays (title / town / battle / options). All of them load into the overlay window, the RAM region reserved for swappable code.
At a glance
- In the game
- The specialised code behind cutscenes, the world map, menu screens and minigames - loaded when that mode starts, gone when it ends. Every loading pause between modes is an overlay swap.
- Magic / marker
- No header - the blob begins with a MIPS function prologue (
addiu sp, sp, -X=0x27BDFFxx) - Lives in
- PROT.DAT entries in the
0901..=0969range (extraction index), 14-37 KB each; loads into the overlay window0x801C0000..0x80200000 - Parser
legaia_asset::mips_overlay::detect(crates/asset/src/mips_overlay.rs); categorymips_overlayin the categorize pipeline- Confidence
- Inferred - a shape heuristic with zero false positives over the corpus, not a loader trace; each blob's load base comes from the asset chain that pulls it in.
- Used by
- Static overlay pipeline, Overlay capture
Layout
Nothing on the disc labels these entries as code - they look like any other binary blob. Every compiled MIPS function opens by moving the stack pointer down to make room for its locals, so a blob that begins with that instruction is a code image whose entry function starts at byte 0.
| Offset | Size | Field | Meaning |
|---|---|---|---|
+0x00 | 4 | first instruction | 0x27BDFFxx - addiu sp, sp, -X, the negative stack adjust that opens a compiled function |
+0x04 | 4 | prologue follow-up | sw ra/s* (0xAFB?_00XX), or another addiu / lui / lw / R-type - the second instruction of the entry function's prologue |
+0x08 | rest | code | The overlay body, position-dependent on its load base |
Detection
Three checks together produce zero false positives across the corpus:
u32_le[0] & 0xFFFF_FF00 == 0x27BD_FF00-addiu sp, sp, -X.(u32_le[0] & 0xFF) ∈ [0x80, 0xF8]- only accept reasonable stack adjustments (8 to 128 bytes).u32_le[1]'s 6-bit MIPS opcode field is one of the common function-prologue follow-ups (sw,addiu,lui,lw, R-type, ldc/sdc).
use legaia_asset::mips_overlay;
if let Some(m) = mips_overlay::detect(buf) {
println!("MIPS overlay: stack frame = {} bytes; second op = {:#04x}",
m.stack_frame_bytes, m.second_op);
}
Where they sit and what they do
- Range. Every match is in the
0901..=0969extraction range, 14 KB to 37 KB, with one 163 KB outlier at0969_xxx_dat.BIN. - Load base. The overlay window is
0x801C0000..0x80200000; each blob loads at a fixed offset inside it, determined by the asset chain that pulls it in. Recovered bases are committed incrates/asset/data/static-overlays.toml. - Likely jobs. From size and the
xxx_datclustering: cutscenes, world map, menu screens, minigames, and per-scene special code that does not fit in the main town-field overlay. - Importing. Once a base is known,
scripts/ghidra-analysis/bulk-import-overlays.shloads the blob into Ghidra (the disassembler this project uses) at that base.
Trusting an address from an overlay dump
A dumped function's printed address is only as good as the base the dump was made at; a filename prefix is not evidence of the base. Before citing an overlay address, run the byte-match arbiter (scripts/ghidra-analysis/classify-worklist.py --explain <addr>) and trust its image= verdict. Only a REAL self-entry body at a recovered base attests a distinct function at its printed address.
| Failure mode | Where it bites | What the arbiter reports |
|---|---|---|
| Mis-recovered base | The bat_back_dat (PROT 0896) dump family, whose link base is unrecovered | Field / battle bodies or interior fragments - never a standalone 0896 function at the printed VA |
| Relocation duplicate | Debug-menu (0971), minigame (0977 / 0978) and menu (0899) overlays re-image a shared library at a different base per mode | DUPLICATE of the canonical VA in 0970 / 0972 / 0899 |
| Inherited CDNAME label | The other_game block opens at extraction 0972 and its name carries forward to 0977 | Name by extraction index: 0977 is the Muscle Dome door/init slot, not a fishing overlay; 0978 opens monster_test |
Details: the two 0896 imports and the re-image pairs
- PROT 0896 is dumped twice, neither at a runtime base: an untagged batch at
0x801C0000, whose prints above 0896's own0x9000bytes read through into the field (0897) and battle-action (0898) overlays, and a batch tagged[overlay_0896 base=0x801C5818]- a phantom base recovered from ajal. No dump in this family can be cited by its printed address. Per-program re-key law:docs/reference/overlay-va-aliases.md. - The re-image pattern is the same one functions records for
0900↔0901and0965↔0967: bodies byte-identical modulo relocation to an already-dumped body. - A CDNAME label is a hint, not a verdict - see CDNAME numbering.
How we know
| Claim | Evidence | Where |
|---|---|---|
| Prologue shape identifies code | Zero false positives over every PROT entry; every match disassembles as MIPS from byte 0 | crates/asset/src/mips_overlay.rs, categorize sweep |
Overlay window 0x801C0000..0x80200000 | Load targets of the overlay loader; RAM map | Memory map, Boot sequence |
| Per-blob load bases | Statically recovered from the loader constants and cross-checked against captures | crates/asset/data/static-overlays.toml, static overlay pipeline |
| Address attestation rules | Byte-match of each dumped body against every image | scripts/ghidra-analysis/classify-worklist.py, docs/tooling/dump-corpus-integrity.md |