Formats
Byte-level specifications for every file format on the Legend of Legaia disc - from the raw CD sector layout up through the archive that holds the game's assets, the compression around them, and each individual asset type (textures, meshes, sound banks, dialog, stat tables). Every claim traces back to a function in the game's own code, disassembled in Ghidra, and every format here has a clean-room Rust parser somewhere in the workspace.
How this section is organised
The pages are grouped the way the disc is layered. Disc + container is the outermost shell: raw sectors, the single big archive (PROT.DAT) almost everything lives in, and the developer-left name map for its entries. Compression + dispatch covers how the game unpacks and routes what it loads. Per-asset formats are the individual asset types themselves. Battle / stat tables are the data tables baked into the game's executable. Streaming + scene containers are the bundles a scene loads as a unit, and runtime overlay carriers are the archive entries that hold code rather than data.
If you're new, start with disc geometry and PROT.DAT
- every other page assumes those two.
Each format carries a badge for how solid its decode is. The badge is a claim about evidence, not about how complete the page looks - trust it accordingly.
- Confirmed - verified end-to-end against real on-disc data, with passing tests.
- Inferred - deduced empirically from byte patterns; structurally consistent but not yet exhaustively validated.
A format can mix the two, and the better pages rate it per-field rather than per-page - the encounter record's shape and reader are Confirmed while the encoding within scripts stays Inferred. Where a page has its own confidence section, that section is authoritative over the badge here.
Disc + container layer
What the disc image looks like before any in-game format gets involved: the sector layout, the one archive file that holds nearly everything, and the text file that names its contents.
iso crate's RawDisc::read_sector hides the sector layer and returns clean 2048-byte user data.start_lba = toc[p+2], size = toc[p+5] - toc[p+3] + 4. Walked at boot by FUN_8003E4E8.#define name N lines; each define tags a block start and names inherit forward. The numbers are the game's own raw TOC indices, offset 2 from this project's extraction numbering - a shift that explains a whole family of historical "mislabeled block" findings.Compression + dispatch
How the game turns archive bytes into usable assets: the compression scheme, the type-byte dispatcher that routes each blob to its handler, and the several near-identical "many assets in one file" pack layouts.
FUN_8001A55C. 4096-byte ring buffer, zero-init - which means "decompresses without error" is not a validity signal; always magic-check the decoded output.FUN_8001F05C dispatches on an 8-bit type byte (0=TIM, 2=TMD, 4=MES, 6=ANM, 7=VDF, 8=SIN, 9=TMD2, B=MOVE2), deciding LZS-vs-raw per asset. The type names come from the devs' own error strings left in the executable.(type_byte << 24) | size calling convention used by FUN_8001F05C, and the descriptor-pair walker that no code in the executable calls - it's reached only from a runtime overlay.u32 count + u32 word_offsets[count], then assets back-to-back. Used inside DATA_FIELD streaming chunks.(magic_lo, magic_hi, count<16, marker=0x01) + offsets; byte_offset = word_index*4 + 4. Includes the hidden boot-UI bundle at the head of the archive.0x01059B84) scene bundle whose 97-slot indexed block turns out to be a byte-identical shared template across all 4 carrier entries - and whose magic nothing in the game ever checks. Consumer is FUN_801D6704 (overlay 0897); slots are read at hard-coded offsets.data\battle\PLAYER1..4 (retail battle_data, extraction 0863..0866): header + LZS palette chain + 12-byte descriptor table + per-slot LZS streams that decompress to [32-byte header + Legaia TMD + texture pool]. The old “16 MB container at 0865” was an over-read window into the monster archive (0867).(fb_x=0, fb_y=479, w=256, h=1). The engine uploads them with merge-zeros semantics so multiple scene TIMs targeting the same row coexist (full slots 0..14 + partial slots 0..7).Per-asset formats
The individual asset types - what a single texture, model, sound bank, or dialog file looks like once unpacked.
crates/tim exports to PNG.0x80000002. Object pointers are byte offsets relative to header end; primitives use a custom 8-byte group header + per-mode descriptor table at DAT_8007326C.crates/vab extracts and decodes to WAV. Banks live across the sound blocks; the retail vab_01 block (extraction 1070..1192) is wall-to-wall VAB - the old “vab_01 is misleading” was the +2 filename shift.pQES container. 13-byte header (PPQN, tempo, time-sig) + delta-time / MIDI events with running status. Drives SsSeqOpen / SsSeqPlay. crates/seq parses + walks.crates/xa demuxes per (file_no, ch_no) channel and emits one WAV per stream.0x404 magic) and Records (0x44 0x78). Bytecode tokens (Glyph / Op65 / Op4c / Op26 / End). Renderer is overlay-resident.FUN_80036888). 256-byte width table at 0x80073F1C; 38-entry escape table at 0x80074050; glyph bitmaps in VRAM at (896, 0), 4bpp, 16x16 cells (drawn 14x15).(count + byte_offsets + records); marker_1 = 0x080C across all observed records. Keyframe pose decoder ported in legaia_anm::AnimPlayer.legaia_asset::character_pack); battle form = assembled per character from the player battle files' equipment-id sections (battle_char_assembly), with PROT 1204 other5 as the Baka Fighter / default-equipment sibling pack. Object-local vertices; rest poses come from the ANM / record[0] idle streams.+0x8c: [u8 parts][u8 frames] + nine-byte TRS records (one part per TMD object). Action 0 is the idle loop; decoder FUN_8004998c.FUN_800204F8. The runtime MOVE base is sourced per-scene from a CDNAME slot, not from a single boot-time PROT entry.0x801F4F5C, PROT 0898). Damage roll, homing, hit reaction, sound cue and spawned-effect lists; resolved via the id→index map at 0x801F4E63 (the same id space as the spell table).0x05C4). 40-field schema: command sequence, action constant, power bytes, hit timing, special / hit effect cues, status effects, repeat-frame replay.0x414 bytes at 0x80084708 + slot * 0x414. Cheat-database-pinned offset table for stats, level, magic rank, spells, summons, equipment.Battle / stat tables
The game's numbers - item names, spell stats, equipment bonuses, steal chances - live as static data tables inside SCUS_942.54 (the game's main executable), not in the asset archive. Each table below is byte-pinned against the executable; DAT_80xxxxxx names are the tables' RAM addresses as labeled in Ghidra.
DAT_800754C8) + name pointers (DAT_800754D0), 12-byte stride. Player Seru-magic block 0x81..=0x8B; named monster attacks at 0x25... Parser legaia_asset::spell_names.PTR_DAT_8007436C[id*3] (256 ids, 12-byte stride). The id space monster drops, steals, and equipment all index. Parser legaia_asset::item_names.DAT_800752C0 - effect class, tier, all-party / field / battle usability flags. Parser legaia_asset::item_effect.DAT_80074F68 per equippable item: attack / defense / agility / speed bonuses, equip-character mask, slot type, Ra-Seru flag. Parser legaia_asset::equip_stats.char+0xF4, aggregator FUN_80042558), with stat-% magnitudes inline. Parser legaia_asset::accessory_passive.[steal_chance_pct, steal_item_id] pairs at DAT_80077828 (chance FIRST - the reverse of the drop field order). What the Evil God Icon steals. Parser legaia_asset::steal_table.0x80078C4C (Vahn / Noa / Gala / Terra; 26-byte stride) that seeds the live 0x80084708 character records; opening scene town01. Parser legaia_asset::new_game.0x37F/0x380), 0x10800 bytes each: per-special-attack CLUTs, texture pages, summon-creature actor records, and the player art-anim "ME" archives. Parser legaia_asset::summon_readef.Streaming + scene containers
The bundle formats a scene loads as a unit - room geometry, textures, scripts, and audio wrapped together - plus the records that drive encounters and scene transitions.
crates/asset::parse_streaming. Sister detector data_field_truncated catches the shape where the final chunk's declared size walks past EOF.scene_tmd_stream, scene_vab_stream, scene_asset_table, scene_scripted_asset_table, scene_event_scripts, plus tmd_size_prefix.0x800. 97 PROT entries (one per scene); strict header algebra N = 4*param + 22; the field VM installs prescript records via FUN_800252EC, the move VM (FUN_80023070) runs them.0x05): a per-kingdom runtime library of small object-local 3D meshes. Each 8-byte record is a GTE vertex (i16 x, y, z, attr); the renderer reads the pool in place. Container byte-verified vs live RAM. Not the bulk continent terrain source.0x02018B0C (efect.dat). 2-pack wrapper: 14 sprite-anim entries + 33 effect scripts indexed by effect ID.0x80108EA4..0x80109550 that earlier readings called a "navmesh" is actually a per-scene scratch buffer the renderer fills with assembled GPU primitive data on scene entry. No RAM cell anywhere holds a pointer into the window.actor[+0x94] by the script-VM and read by FUN_801DA51C. Layout: [3 reserved][count: u8][monster_ids: u8[count]]. Reader copies the ids into the global formation cell at 0x8007BD0C.0x3F door) destinations are partition-2 records reached via the partition-2 record-offset table (runtime-pinned); resizing a destination name fixes the partition tables, section-0 offset, intra-record jump deltas, and the external descriptor size. Powers the door randomizer.CdlFILE directory cache at 0x801CAE08 and the 23-slot FMV dispatch table at 0x801D0A6C (32-byte stride) the play loop reads - nine retail slots covering every movie on the disc; per-scene fmv_id triggers are disc-pinned as literal operands in the scene MAN scripts.Runtime overlay carriers
Archive entries that hold code, not assets: overlays - chunks of MIPS machine code the game loads into RAM on demand, where most of the game's logic actually lives.
0x801C0000+. The detector finds them by the function-prologue fingerprint at byte 0.Auxiliary
[opcode][window id][operand], resident as a program table in the menu overlay's own data segment, interpreted by FUN_801D6628.DAT_8006F198 (8-byte stride, 100 entries). Per cue: VAB program/tone, voice count, mixer channel. Byte-exact vs live save-state RAM.