At a glance

Magic
none - a string cluster in SCUS_942.54 at RAM 0x8007B380 (file offset 0x6BB80)
Where on disc
The .dpk payloads are the sound_data2 PROT family (0877..0885); bse.dat is the battle SFX descriptor bank, loaded from battle init (FUN_800513F0), not at boot
In the port
legaia_container_formats::sound_pack (extract returns the reconstituted VAB + SEQ); walker parse_streaming_with(.., StreamTerminator::TypeTwo)
Confidence
Confirmed - loader, path builders and chunk walker traced (how we know); VAB reconstitution byte-verified across 0877..0885
Used by
Audio stack

The .dpk sound pack is a VAB + SEQ bundle

A per-scene sound pack is a VAB followed by a SEQ in a streaming-chunk container - not a novel .MAP / .PCH / .spk layout. Each chunk is [u32 (type << 24) | size][payload, 4-padded]; the walk ends on the type-2 chunk, which is itself the SEQ.

type 0 - pBAVVAB header type 1VAB sample pool (VAG bodies) type 2 = end - pQESSEQ one contiguous VABtotal_size = size0 + size1 the scene's SEQplays against the VAB
Types 0 + 1 concatenate into one VAB; the terminator chunk is the SEQ itself.
Chunk typePayloadMagic
0VAB header sectionpBAV (0x5641_4270 LE)
1VAB sample section (SPU-ADPCM / VAG waveform pool)- (raw samples)
2SEQ (the stream terminator)pQES
  • Decisive invariant: type-0 + type-1 reconstitute one contiguous VAB whose header total_size equals chunk[0].size + chunk[1].size. Holds across 0877–0885 (PROT 0877: 0x2820 + 0x1BA90 = 0x1E2B0).
  • Same walker, different terminator. The chunk walker is the one the DATA_FIELD dispatcher models, but it ends on a type-0x02 chunk (non-zero size) where DATA_FIELD ends on a zero-size header - so the DATA_FIELD parser mis-reads a .dpk. Use StreamTerminator::TypeTwo.
  • Type-1 depends on the consumer. On the graphics battle-init walk, the type-1 handler is the TIM/CLUT upload; in a sound pack it is the VAB's sample pool.
  • The content shape matches the scene_vab_stream BGM wrapper; only the container differs. The .MAP (sample-address map) and .PCH (patch data) are the PsyQ SoundArtist dev-side inputs that produce this VAB - the retail disc does not carry them, so no dedicated parser is warranted.

The string cluster

OffsetBytesMeaning
0x8007B38012 bytesPer-extension flag/mode metadata table: 00 01 FF FF FF FF 00 00 00 02 00 00.
0x8007B38C"sound\"Path prefix for streaming-asset loads.
0x8007B394".spk"SPU sample bank.
0x8007B39C".LZS"Compressed wrapper (per-file).
0x8007B3A48 bytesNot sound data - the per-character equipment-swap selector bytes read by a graphics-side routine; adjacent in BSS but unrelated (trap below).
0x8007B3AC"bse.dat"Battle SFX descriptor-bank file name (loaded at battle init).
0x8007B3B4".dpk"Per-scene sound pack - the VAB + SEQ bundle above.
0x8007B3BC".MAP"Sound bank map (PsyQ SoundArtist output; dev-only).
0x8007B3C4".PCH"Patch / instrument data (PsyQ output; dev-only).
0x8007B3CC".LZS"Duplicate, fallback.
0x8007B3D4".pac"Per-scene generic pack.
0x8007B3DC"STR"Streaming audio (raw PSX .STR containers).

Trap: a third routine reads the 0x8007B3A4/0x8007B3A8 bytes and looks like a sound consumer, but it is the character-TMD equipment-conditional group-transform swap - a graphics function. See character-mesh and how we know.

bse.dat master-bank header

Battle init loads bse.dat into a 0x1800-byte buffer and parks the record-table pointer at gp+0x678 (0x8007B990); the buffer slot itself is repointed at every field load. A single u16 at offset +2, rounded down to even, is the byte offset of a second section:

The loader computes gp[0x678] = base + (word@2 & ~1) - a round-to-even byte offset, not a record-count divisor.

Reading that split as two sections overstates it: @2 is 4 in both retail carriers, so the "second section" is simply the record table and nothing precedes it but the header. What follows the table in entry 888 belongs to a different file - a foreign VAB tone table left in the sector. The pointer indexes the battle-loaded bank, not the per-scene .dpk. The records use the SFX descriptor columns, for cue ids ≥ 0x200.

Dev-build vs retail-build paths

Both path builders carry a debug-flag carve-out (_DAT_8007B8C2):

  • Retail branch (flag non-zero, the value retail boots with) loads sound data by PROT index via the index-based loader: raw TOC 0x37A = extraction 888, which is bse.dat rather than the .dpk, plus param_1 + 5 for the per-set pack.
  • Dev branch (flag zero) opens an h:\main\bg\domepack\<name>.dpk path through a helper that is strcpy, a dev-station host trap (break 0x103), then fseek/fread/fclose. Retail hardware cannot service it.

Only the retail branch runs on a real disc; the dev branch is a build-time artefact whose paths are not in the filesystem. The same split appears in the asset loader, so it is a pattern that repeats across loading subsystems.

How we know

Provenance by function
FunctionAddressWhat it provesDump
Sound init / .dpk loaderFUN_8001FA88Allocates the 0x1800 buffer at _DAT_8007B8D0, loads bse.dat, derives the section-B offset from +2; assembles the dev .dpk path from the template at 0x800105C8; retail branch loads PROT index 0x37Afuncs/8001fa88.txt
Streaming-asset path builderFUN_8001FC00Builds paths under the sound\ prefix; the XA / .pac / STR consumerfuncs/8001fc00.txt
Chunk walker (consumer)FUN_8001FE70, called by FUN_800513F0 on the loaded buffer[type|size] chunk walk, advance (size & ~3) + 4, type-2 terminator; type-1 → TIM upload on the graphics walkfuncs/8001fe70.txt, funcs/800513f0.txt
Not-a-sound-consumer trapFUN_8001EBECReads 0x8007B3A4/0x8007B3A8 as equipment-swap selectors against DAT_8007C018 TMD pointers - a graphics routinefuncs/8001ebec.txt
Locator sweepghidra/scripts/find/find_sound_path_builders.pyThese are the only SCUS functions touching the cluster-
Disc checkPROT 0877..0885PROT 0877 walks as [0: 0x2820][1: 0x1BA90][2: SEQ]; 0x2820 + 0x1BA90 == 0x1E2B0 = the VAB's declared total_size; verified across 0877..0885-

See also