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 count and 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_container_formats::parse_player_lzs (crates/container-formats/src/lib.rs).
Confidence
Confirmed - walker disassembled, overlay caller traced, and the second header word measured across every carrier on the disc.

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's decompressed size in the low 24 bits. Each payload is an LZS stream when the container is a player.lzs-style chain.

OffsetSizeFieldMeaning
+0x004countNumber of descriptor pairs that follow.
+0x044total_decompressed_sizeThe sum of the descriptors' decompressed sizes - see the second header word. Retail never reads it, and editors must keep it consistent.
+0x08 + 8i4type_size_i(type << 24) | size: asset-type byte in the high byte, size in the low 24 bits.
+0x0C + 8i4data_offset_iByte offset of asset i from the buffer start.

The second header word

The word at +0x04 is the container's total decompressed size: the sum of every descriptor's size field. Across the 105 carriers on the disc the identity holds 105 times out of 105, and in all 105 the sum is larger than the PROT entry that carries it - which is what rules out reading it as a file size or a sector count. It is where the last asset ends once the container has been unpacked into the runtime buffer, not a position inside the entry.

Retail never reads it. The walker takes its loop count from +0x00 and steps descriptor pairs from +0x08, so +0x04 is never addressed. Sweeping the executable and every extracted overlay for dereferences of the table-base pointer _DAT_8007B85C - including the lui+load and materialised-base forms an absolute-address scan cannot see - finds one store that publishes the buffer base and four sites that dereference the pointer, every one of them at displacement 0x0; every other site hands the pointer straight to a callee as a load destination.

Inert at runtime is not the same as free to change. The word is an authoring total, so an editor that rewrites a descriptor's size has to keep it consistent; the Rust side checks it with SceneAssetTable::total_size_is_consistent.

Where it appears

  • On the disc, at the top level. A structural sweep of all 1233 PROT entries (the numbered slots of PROT.DAT, the disc's main asset archive) finds 105 entries wearing this header at offset 0 - the scene bundles, the player.lzs-style character and effect containers, and the minigame art container. An earlier reading of "zero hits" came from a detector that demanded a fixed descriptor count; the count word varies (80 carriers use 7, the rest 1, 3, 4, 5 or 6). The same corpus is what scene bundles classes as scene_asset_table.
  • 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

Provenance: the walker, its overlay caller, and the TMD handler chain
FunctionAddressWhat it proves
Descriptor walkerFUN_80020224Reads count, then walks the (type_size, data_offset) pairs and dispatches on the high type byte.
Town overlay MAIN_INITFUN_801D6704, call at 0x801D6B0CCalls 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 chainFUN_8001F05C case 2 → FUN_80026B4CRegisters PROT 0874 §0's five TMDs into DAT_8007C018[0..=4] - the field character-mesh slots.

See also