Field-pack Confirmed
There is no field-pack format. The entries named after the magic 0x01059B84 are ordinary
DATA_FIELD streams whose single chunk is an container-formats::pack of PSX
TIMs or Legaia TMDs - a scene's texture or mesh pack. The "magic" is that chunk's (type << 24) | size
header, the "97-entry schema" is the pack's own [count][word offsets] table, and the "byte-identical
91 KB constant block" is two Rim Elm scenes sharing their leading textures.
At a glance
- What it really is
- One DATA_FIELD chunk wrapping one container-formats::pack - two formats this site already documents
- Where
- Raw-TOC offset
+4of a scene's CDNAME block (extraction index#define + 2). 23 blocks carry one; the rest reserve the slot with a pochi filler - The "magic"
0x01059B84=(TIM_LIST << 24) | 0x059B84. The low 24 bits are that carrier's payload length, so the word differs per file - it occurs exactly once in the whole archive- Two forms
- 18 carriers lead with the chunk header; 5 carry the bare pack at offset 0. The bundle's
Flagdescriptor picks which - Parser
legaia_scene_formats::field_pack::scene_pack(corrected reader);detect/CANONICAL_SCHEMAkept for the classifier and theasset field-packCLI- Confidence
- Confirmed - loader disassembly plus a whole-archive byte sweep (how we know)
- Used by
- scene bundles, asset loader
Layout
Grey = the optional DATA_FIELD chunk header (offsets shown with it present; the five bare carriers start at count). Everything after it is a plain container-formats::pack: word offsets are × 4 from the count word, and member 0 starts exactly where the table ends.
| Offset | Size | Field | Notes |
|---|---|---|---|
+0x00 | 4 | chunk_header | (type << 24) | size. Present in 18 of 23 carriers; absent in the other 5 |
+0x04 | 4 | count | Pack member count (30..173 across the corpus) |
+0x08 | 4 × count | word_offset[] | Byte offset = word_offset[i] * 4, relative to the count word |
| after the table | rest | members | PSX TIMs under type 0x01, Legaia TMDs under type 0x02 |
The type byte agrees with the members' own magic in every carrier: all-TIM (0x00000010)
under 0x01, all-TMD (0x80000002) under 0x02. Eight carriers are TIM packs and ten
TMD packs; the five bare ones hold TIMs but for monster_data.
What each old claim really was
| Old claim | What it is |
|---|---|
Magic 0x01059B84 | town01's chunk header. 0x059B84 = 367,492 = the pack's byte length, so every carrier's word differs - which is why a corpus scan finds the "magic" exactly once |
| "97-entry schema, byte-identical everywhere" | [u32 count = 96][u32 word_offset[96]]. CANONICAL_SCHEMA[0] = 0x60 is the count, not an offset. town0b's table is 98 words with count 97, so it is not identical |
| "~91 KB schema-indexed region, a global constant" | town01 and town0c share the first three members of their texture packs - the same Rim Elm atlases. Their offset tables diverge at the fifth word (0x6609 vs 0x6a09) |
| Slot clusters named NPC record / event trigger / collision box | Member sizes in words. ×4 gives bytes: 0x2088 is the standard 0x8220 64×256 4bpp atlas, 0x218 a 2144-byte 16×64 sprite. All TIMs |
| "The per-scene payload is the preamble" (234 / 233 / 227 KB) | The superseded over-reading entry size. Those bytes are the block's earlier entries - the prescript and the scene asset table. On its own sectors the carrier starts with the chunk header |
| "The magic has zero runtime references" | True, and now expected: there is no magic to reference |
| "Eight carriers, four per block" | Four entries of one block all "carrying" the same block-final pack is the over-read signature. Each block has one carrier |
Where a carrier sits
Every scene's CDNAME block seats the same slots at the same offsets from its #define, in raw-TOC space:
| Block offset | Content |
|---|---|
+0 | <scene>.MAP - the per-scene field map, 0x12000 bytes |
+1 | <scene>.PCH - the walk-on trigger sidecar, 0x800 bytes |
+2 | efect.dat - the move-VM stager prescript |
+3 | the scene asset table |
+4 | this page - the streamed TIM / TMD pack, or a pochi filler |
+5… | the battle-stage backdrop meshes |
The first three names come from the loader's own path literals; the +4 position is what the loader's
by-index branch computes directly, FUN_8003EB98(scene_index + 4, …) with the scene index equal to the
block's #define value.
How it reaches RAM
FUN_800255B8 loads one streamed file into the scene asset buffer and returns its sector count,
choosing the path by a mode argument; FUN_8002541C then reads the buffer with the reader that mode
selects.
| Mode | Path | Reader |
|---|---|---|
0x0A | h:\PROT\FIELD\<scene>\tim.dat | Bare pack: count = base[0], then LoadImage at base + word_offset[i] * 4 per member |
0x0F | h:\PROT\FIELD\<scene>\move.mdt | Move table |
0x14 | DATA\FIELD\<scene>.pac | DATA_FIELD chunk walk into the asset-type dispatcher |
Nothing else picks the mode: it comes out of the scene's own asset table. The dispatcher handles type bytes
0x0A / 0x0F / 0x14 by returning (case << 8) and nothing else;
the descriptor walker ORs every return into one status word, and the field init shifts that right by 8 and hands it
to FUN_8002541C. So a Flag descriptor in the bundle is a request to stream the
block's +4 entry.
| Bundle carries | Block +4 holds | Blocks |
|---|---|---|
Flag(0x14) | a chunk-headered single-chunk pack | 16 |
Flag(0x14) | a multi-chunk DATA_FIELD stream | 12 |
Flag(0x0A) | a bare pack | 4 |
no Flag | a one-sector pochi filler | 64 |
Two blocks break the pattern in the harmless direction - opurud and other7 carry a pack
with no Flag descriptor to reach it. Flag(0x0F) appears in no retail bundle, consistent with
the move table being a descriptor inside the bundle rather than a streamed file.
How we know
Provenance: the loader chain, function by function
| Function / evidence | Address | What it proves | Dump |
|---|---|---|---|
| Streamed-file loader | FUN_800255B8 | Builds tim.dat / move.mdt / .pac per mode; loads into the scene asset buffer; by-index branch resolves scene_index + 4 | funcs/800255b8.txt |
| Streamed-file reader | FUN_8002541C | Mode 0x0A walks a bare pack into LoadImage; mode 0x14 walks DATA_FIELD chunks into the dispatcher | funcs/8002541c.txt |
| Asset-type dispatcher | FUN_8001F05C at 8001f574 / 8001f60c / 8001f658 | Types 0x0A / 0x0F / 0x14 return (case << 8) and do nothing else | funcs/8001f05c.txt |
| Descriptor walker | FUN_80020224, caller at 801d6bf8 | Status word is the OR of the descriptor returns; the caller shifts it right by 8 into the mode | funcs/80020224.txt, funcs/overlay_0897_801d6704.txt |
| Field-file loader | FUN_8001F7C0 | .MAP at +0, .PCH at +0x12000, efect.dat at +0x12800 - the block's first three slots by name | funcs/8001f7c0.txt |
| Corpus sweep | whole-archive byte scan | One magic occurrence archive-wide, at offset 0; every member's magic agrees with the chunk type; the Flag / form correspondence | - |
Deep dive
CLI and Rust API
asset field-pack <PATH> # legacy view: chunk header + pack table
asset field-pack <PATH> --all-slots # every member offset/size
asset field-pack <PATH> --groups # cluster members by size
asset field-pack-scan <DIR> # find the chunk-headered carriers
use legaia_scene_formats::field_pack;
// Handles both forms: chunk_header is None for the bare carriers.
if let Some(p) = field_pack::scene_pack(&entry_bytes) {
println!("{} members, type {:?}", p.members.len(), p.asset_type());
for r in &p.members { /* a PSX TIM or a Legaia TMD */ }
}
The RAM base this page used to name
_DAT_8007B8D0 - 0x12800 recovers the field-file scratch base - the buffer holding
<scene>.MAP at +0 and <scene>.PCH at +0x12000. It is
not a field-pack base, and the GP0 packets an earlier reading found at base + 0x60 are that buffer's
primitive scratch, not a transformed schema slot. The scene's own asset buffer is a separate
0x62C00-byte allocation.
Source of record: docs/formats/field-pack.md.