Asset descriptor format Confirmed
When a town loads, the game pulls one compressed bundle off the disc and has to find the pieces inside it - the meshes, textures and animations that become the scene. The asset descriptor is that bundle's table of contents: a count, then a list of (type_size, data_offset) pairs, each pointing at one asset in the buffer and saying what kind it is.
At a glance
- In the game
- Every field scene load - the town overlay's init walks one of these to install the scene's assets.
- Magic / marker
- None - a leading
u32 countand a header word, then the pairs. - Lives in
- In RAM, at the head of a decoded buffer; PROT 0874 (
player.lzs, extraction-index space) is the three-descriptor instance the character mesh pack is read through. - Retail reader
- The descriptor walker, reached from the town overlay's init (how we know).
- Parser
legaia_asset::parse_player_lzs(crates/asset/src/lib.rs).- Confidence
- Confirmed - walker disassembled, overlay caller traced; the second header word's purpose is Unknown.
Layout
Two header words, then a flat array of 8-byte pairs. data_offset is byte-relative to the buffer start; type_size packs the asset-type byte (the 8-bit tag saying TIM / TMD / MES / …) in its high byte with the payload size in the low 24 bits. Each payload is an LZS stream when the container is a player.lzs-style chain.
| Offset | Size | Field | Meaning |
|---|---|---|---|
+0x00 | 4 | count | Number of descriptor pairs that follow. |
+0x04 | 4 | header word | Purpose unknown. On PROT 0874 it is 0x2CBA0, the offset of a VDF data tail past the LZS payload that the battle loader registers verbatim - see character mesh § on-disc layout. Editors must keep it byte-exact. |
+0x08 + 8i | 4 | type_size_i | (type << 24) | size: asset-type byte in the high byte, size in the low 24 bits. |
+0x0C + 8i | 4 | data_offset_i | Byte offset of asset i from the buffer start. |
Where it appears
- In RAM only. Strictly scanning all 1233 PROT entries (the numbered slots of
PROT.DAT, the disc's main asset archive) against this shape as a top-level entry finds zero hits. The table only exists once an LZS chain such asplayer.lzshas been decoded; the DATA_FIELD streaming format carries descriptor-shaped chunks under its own header instead. - Reached only through an overlay. The executable's static call graph never touches the walker - it looks like dead code until the town overlay's init is followed (how we know). This "zero static callers but alive via overlay" pattern recurs across the whole codebase; treat a zero-xref result as "needs an overlay sweep", never as "dead".
- Installs the party's field meshes. The same walk registers PROT 0874 §0's five TMDs into the model slots the field-form character meshes draw from.
The Rust parser is named parse_player_lzs after the container it was first read through; the format itself is not player-specific.
How we know
| Function | Address | What it proves |
|---|---|---|
| Descriptor walker | FUN_80020224 | Reads count, then walks the (type_size, data_offset) pairs and dispatches on the high type byte. |
Town overlay MAIN_INIT | FUN_801D6704, call at 0x801D6B0C | Calls the walker with a0 = 0 on every field scene load; result stored at 0x80087AF8. Zero static xrefs exist in SCUS_942.54. |
| Type-2 (TMD) handler chain | FUN_8001F05C case 2 → FUN_80026B4C | Registers PROT 0874 §0's five TMDs into DAT_8007C018[0..=4] - the field character-mesh slots. |