At a glance

Magic
none - the first u32 is already a chunk header (type byte in the top 8 bits)
Where on disc
34 PROT.DAT entries strict-validate as class data_field_streaming: the other5 character/effect run (1204..1219) and a family of scene bundles
Parser
crates/asset/src/lib.rs::parse_streaming; asset scan-stream / asset extract
Confidence
Confirmed - consumer FUN_8002541C, the 0x14 (DATA_FIELD) branch, feeding FUN_8001F05C with copy_only=1
Used by
asset loader, battle scene loader, characters page (battle-form meshes + atlases)

Overview

The walker passes every chunk through the dispatcher with copy_only=1, so chunks are always uncompressed - the container trades disc space for a load path with no decompression step. Implementation: crates/asset/src/lib.rs::parse_streaming.

Layout

A DATA_FIELD stream: a chain of 4-byte type-and-size headers each followed by raw data, ended by a header whose low 24 bits are zero +0 hdr type|size chunk 0 data (e.g. TIM) size bytes +4+(size&~3) hdr type|size chunk 1 data (e.g. TMD2) size bytes ... terminator low 24 bits = 0 trailer optional, unparsed next header = this header + 4 + (size & ~3) header u32 = (type << 24) | (size & 0x00FFFFFF)
The chunk chain. Every chunk is self-describing through its header; the walker never needs a count up front.
OffsetSizeFieldMeaning
+0x00u32type_size(type_byte << 24) | (size_bytes & 0x00FFFFFF); type_byte per the asset type table
+0x04size_bytesdatathe raw asset, uncompressed
+0x04 + (size & ~3)u32next type_sizeheader + size truncated to a 4-byte boundary; sizes are 4-aligned in practice
lastu32terminatorany header whose low 24 bits are zero

What's in the wild

asset scan-stream strict-validates 34 PROT entries (class data_field_streaming), in two families.

Character / effect streams

The other5 run, entries 1204..1219. The common shape is three single-asset chunks:

chunk[0]: TIM   (single, magic 0x10)        - sprite atlas / texture
chunk[1]: TMD2  (single, magic 0x80000002)  - single Legaia TMD
chunk[2]: MOVE2 (single, magic 0x08)        - animation data
terminator

The two heads of the run are homogeneous instead: 1204 is five TMD2 chunks (the battle-form party meshes used by the Baka Fighter minigame and as the default-equipment sibling pack - the in-battle player meshes themselves are assembled from the PLAYER1..4 files) and 1205 is eight TIM chunks of 0x8220 bytes each (their atlases) - which is what makes the atlas stride 0x8224, chunk header included.

Scene bundles

MAN / MES / MOVE / VDF, four chunks (dolk2, rikuroa, rikuroa2, rayman, station, taiku, taiku2, doman, nilboa2, edbalden, eddoman). Shorter variants drop the trailing VDF (balden2, ropeway2) or lead with a bare TMD where a MAN would sit (balden, bubu1, edbubu). Two entries are neither family: init_data is TIM_LIST + TMD, and 0890_sound_data2 is two TIMs.

The chunk layouts are single assets in the other5 family (one TIM, one TMD2, one MOVE2), not packs. Other clusters do use pack-shaped TIM_LIST / TMD chunks; the pack format handles that case.

Trailer data

Some entries contain bytes past the streaming terminator. asset extract preserves these as _trailer.bin next to the extracted chunks. The function that consumes the trailer has not been located; tracing the caller of FUN_8002541C in the field/town overlay is the next move if a specific entry's trailer looks structured.

Per-scene field bundles - what's still open

The CDNAME block for a typical field/town scene (e.g. town01, bubu1) carries 8-12 PROT entries. Categorize (the sweep that classifies every archive entry by format) identifies several known shapes per block. Slots below are in the retail block frame (see CDNAME § numbering space): slot 0 is the scene's .MAP and slot 1 its v12 trigger sidecar, which is why the classes below start one or two slots later than a naive count.

Common slotClassTypical content
0 / 1SceneTmdStream or TmdSizePrefixscene mesh (room geometry)
1 / 2Pack (TIM-pack)scene textures / sprite atlas
2 / 3SceneEventScriptsper-event field-VM bytecode (the event-script prescript)
3 / 4MesContainerdialog text
4 / 5Pack (ANM-pack)per-actor animation sets
5..7PochiFillerreserved-but-unused dev fillers (one sector each)
6..8SceneVabStream (rare; only on scenes with custom audio)per-scene VAB + SEQ

What is not modelled yet:

  • Cross-entry pointers (NPC references that point into other PROT entries - the asset chain in asset loader is best-effort).
  • The runtime-reconstructed slot-to-asset mapping inside field-pack containers (magic 0x01059B84, 124 entries) - known shape, unknown slot semantics.
  • The retail engine's per-scene "asset table" indirection (the SceneAssetTable detector fires on a small fraction of entries; a full reverse needs an overlay capture of FUN_8001f7c0 at scene-load time).

The categorize sweep classifies every PROT entry to something; the items above are the residual edge. See the engine page for how scene loading consumes these shapes.

History: the "scripted asset table" class

A SceneScriptedAssetTable class - a descriptor table found at a sector-aligned offset inside an entry that led with an event prescript - was an artefact of the over-read entry size: every such offset was the next entry's start LBA, i.e. the neighbour's ordinary offset-0 table. Read against their own sectors, the prescript entries classify as event scripts and the 88 bare asset tables stay where they are. See PROT TOC § history of the over-read.

How we know

EvidenceAddress / testWhat it proves
Retail consumerFUN_8002541C, the 0x14 branchThe chunk walk: header split, (size & ~3) + 4 advance, zero-size terminator.
Dispatcher hand-offFUN_8001F05C called with copy_only=1Chunks are consumed uncompressed, one dispatcher call per chunk.
Corpus sweepasset scan-stream strict validationThe 34-entry census and the two family shapes above.

See also