sabf: sead sound banks
- Source
docs/formats/sabf.md- SHA-256
7d901804…9e89ec00
Where banks are and how the game finds them · Sound tables · Header · Chunk table · Chunks · Materials · How the game reads it · Rebuilding and replacing audio · Leads
A sound bank of Square Enix's sead sound engine (SQEX::Sd), which the game links: a 16-byte header with the bank's name and number, a table of four chunks (snd , seq , trk , mtrl), then the materials, which hold the audio itself. Every material in the game is CRI HCA audio. Banks are loose .sab files under sound/bin/ (music, voices, movie audio), entries in the pakd of sound/bin/SECommon.dat (sound effects), and entries in title/title.dat and event/scene/*.dat. The tables that say which bank and file a sound comes from are in sound/sound.dat, sound_ja.dat, sound_en.dat and SECommon.dat#0 (Sound tables). The engine's own settings are in sound/sead.config.
| Based on | Every sabf in the game: the 22,105 loose .sab files under sound/bin/, and 620 pakd entries (4 in sound/bin/SECommon.dat, 5 in title/title.dat, 611 in 244 event/scene/*.dat); sound/sound.dat, sound/sound_ja.dat, sound/sound_en.dat; FileTable.bin; the unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599. Outside source: vgmstream's src/meta/sqex_sead.c (checked 2026-10-11). |
| Checked | 2026-10-11 |
| Method | Single-file C# programs (session scratchpad, with the toolchain's Containers library): sabscan.cs walks every bank's header, chunk table, chunk headers, chunk offsets and materials and checks how they tile the file; ftsound.cs lists every sound/ file in FileTable.bin with its group and file ID; voicejoin.cs joins the voice tables with the file table; sounddat.cs dumps the tables. Static analysis with Ghidra 12.1.4 through the decompiled code in local/exe/decomp/ of the game's sound manager (0x140287200 and around), its stream reader (0x140290830), and sead's bank code (0x1405d95b0, 0x1405d9920, 0x1405f3570, 0x1405fe4e0, 0x1405faf80); hex inspection of the executable's data. Not yet decoded or rebuilt by the toolchain, and no in-game tests. |
Where banks are and how the game finds them
Every loose bank and .dat here has a record in FileTable.bin, and the game opens them by group and file ID, never by name (confirmed: ftsound.cs; the opens below are static analysis, likely). Group 400 is sound/; 401 holds the Japanese voices and 402 the English ones, picked by the voice-language setting (byte 0x2B of the game's settings at DAT_140ce4e30: 0 Japanese, 1 English).
| Banks | Where | Group, file ID | Loaded | Bank number (0x0A) |
Found by |
|---|---|---|---|---|---|
Sound effects: menu, battle, weather, effect |
sound/bin/SECommon.dat #1–#4 |
400, 0 | Whole, into memory, at startup | 2, 3, 1, 6 | Sound ID, through SECommon.dat#0 |
Music, taco_BGM01–84 |
sound/bin/bgm/taco_BGMnn.sab |
400, nn (1–84) |
Streamed | 0 | File ID; in English, 4 IDs swap through sound.dat#2 |
| Opening movie audio | sound/bin/OPN_movie/chapter1.sab |
400, 100 | Streamed | 7 | File ID |
| Movie audio | sound/bin/{ja,en}/Movie/*.sab (7 each) |
401 or 402, 1000–1012, even IDs | Streamed | 7 | File ID |
| Voice lines | sound/bin/{ja,en}/voice_*/*.sab (10,310 ja, 11,693 en) |
401 or 402, 2000–25446, even IDs | Streamed | 2000–2012, one per folder | Name, through sound_ja.dat or sound_en.dat; title calls through sound.dat#0 |
| Damage cries | sound/bin/{ja,en}/voice_DAMAGE/voice_DAMAGE.sab |
401, 0x1FF; 402, 0x200 |
Whole, into memory | 2013 | Sound index in the bank |
| Scene sounds | event/scene/*.dat, 1–8 in each of 244 files |
(the scene's file) | Whole, into memory, up to 16 per scene | 5 | The scene's sound list |
| Title sounds | title/title.dat #6–#10 |
2500, 0 | Whole, into memory | 5 | Entry index, fixed in the code (0x1404abe70) |
3013_wheel_of_fortune |
sound/bin/event/3013_wheel_of_fortune.sab |
700, 40000 | Not traced | 5 | — |
- likely: the sound manager is created with group 400 (
0x1402c2110calls0x140287200with 400) and at startup opens 400/0 (SECommon.dat) and 400/500, 501, 502 (sound.dat,sound_ja.dat,sound_en.dat), each read whole (0x140287fd0,0x140288200). Source:0x1402c2110. - likely: music and voices are streamed through one of two stream players: player 0 opens group 400, player 1 group 401 or 402 by the voice language; both open the file by ID (
0x140288700→0x140290290). Source:0x140288700. - likely: a sound effect is played by a sound ID; the game finds the
SECommon.dat#0row whose range holds the ID and plays soundID − first_sound_idof that row's bank. Source:0x140288850. confirmed: each row's range is exactly its bank'ssndcount: 1–104 (menu, 104 sounds), 1000–1006 (weather, 7), 2000–2174 (battle, 175), 4000–4400 (effect, 401). Source:sabscan.cs,sounddat.cs. - likely: a streamed bank plays its sound 0. confirmed: every streamed bank (every loose
.sabbut the twovoice_DAMAGE.sab) has exactly one sound. Source:0x140290830(0x1401a9c00with sound index 0);sabscan.cs. - likely: a scene loads up to 16 of its banks into memory, each into a free slot, and plays them by index in the scene's list (natives 187–193). Source:
0x1403585a0→0x140285930; natives.
Sound tables
Tables that map sound IDs, file IDs and voice names to banks. All are xlce tables in a pakd. None has a table schema yet; the row shapes here are from the code that reads them and the C# programs above.
SECommon.dat#0: sound-effect banks
4 rows of 16 bytes, one per bank in entries #1–#4, in order. Read by 0x140285a50 (which checks the entry's xlc magic with strncmp) and 0x140288850.
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x0 | 4 | u32 | bank_slot |
Equals the bank's number at 0x0A in all 4 (2, 3, 1, 6). The game doesn't read it: it loads the banks into the slots in its own array at 0x14067bc1c ({2, 3, 1, 6}) and overwrites this field in memory with the slot it got. |
likely | 0x140285a50 → 0x1401a9840; sounddat.cs, sabscan.cs. |
| 0x4 | 4 | u32 | index_offset |
Added to ID − first_sound_id to give the sound index. 0 in all 4. |
likely | 0x140288850. |
| 0x8 | 4 | u32 | first_sound_id |
First sound ID of the bank. | confirmed | 0x140288850; ranges match the snd counts. |
| 0xC | 4 | u32 | end_sound_id |
One past the last sound ID: first_sound_id + the bank's snd count. |
confirmed | Same. |
sound_ja.dat and sound_en.dat: voice names
11 tables each (#0–#10), all with 16-byte rows. #0–#9 cover the story by chapter (SCENARIO_C1, C2A, C2B, C3A, C3B, C3C, C4 from 001 and from 331, DLC, EP); #10 lists every voice file of the language once (11,724 en, 10,310 ja rows). Read by 0x140285c90, which checks the file's pak magic and builds one name → row map per language; the first row with a name wins.
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x0 | 8 | str_ofs64 | name |
The voice's name, e.g. SCENARIO_C1_001_001_00, after a U+F8FC header that the game skips (txte.md); in every row the header is EF A3 BC 02 02 01 and two more bytes. |
likely | 0x140285c90; voicejoin.cs. |
| 0x8 | 2 | u16 | file_id |
The voice file's ID in group 401 (ja) or 402 (en). | confirmed | voicejoin.cs: every row with has_voice 1 names the file <name>.sab. |
| 0xA | 2 | u16 | has_voice |
1: the line is voiced. 0: it isn't, and file_id points at some other line's file or at no file. |
likely | voicejoin.cs: none of the 145 en and 1,605 ja rows with 0 names its own file; meaning not traced in code. |
| 0xC | 4 | u32 | unk_0xC |
0 in every row. | confirmed | voicejoin.cs. |
sound.dat
| Entry | Rows × size | What it is | Row | Read by |
|---|---|---|---|---|
#0 |
114 × 10 | Title-call voices, in groups: a row with a non-zero group starts a group, the rows with 0 after it continue it. The game picks one row of a group at random, weighted. |
u8 group; u8 unk_0x01; u16 ja_file_id (group 401); u16 en_file_id (group 402, 0 in 19 rows); u16 ja_weight; u16 en_weight |
0x1402889c0 (likely) |
#1 |
48 × 8 | Which title-call groups can play, by sound flag (a bit of the 128-bit array at DAT_140ce4e30 +0x254, 0x1402d6960). |
u8 index (= row); u8 unk_0x01; u8 unk_0x02; u8 group (in #0); u8 unk_0x04; u8 0; u16 sound_flag_id |
0x14032b5c0 (guess) |
#2 |
4 × 4 | Music swapped when the voice language is English: 66 → 77, 70 → 78, 80 → 81, 83 → 84. | u16 from_file_id; u16 to_file_id (group 400) |
0x140288700, 0x1402889c0 (likely) |
confirmed: every #0 file ID names a file in voice_TITLECALL/, except the 19 English IDs that are 0. Source: voicejoin.cs.
Header
0x10 bytes, then the name. Offsets are from the start of the bank.
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x00 | 4 | raw[4] | magic |
sabf. The only field the game checks. |
confirmed | C# scan, all 22,725; checked by 0x1405d90e0. |
| 0x04 | 1 | u8 | version |
2 in every bank. sead finds the chunk table by it (below). | confirmed | C# scan; 0x1405d8920. |
| 0x05 | 1 | u8 | unk_0x05 |
0 in the 6 on-memory banks of sound/ (the 4 in SECommon.dat, both voice_DAMAGE.sab), 1 in all others, the title banks (on-memory too) among them. sead reads bit 0 (0x1405b8070, 0x1405db4f0). |
— | C# scan (hdr05.cs); 0x1405b8070, 0x1405db4f0. vgmstream: flags. |
| 0x06 | 2 | u16 | header_size |
0x10 in every bank. |
confirmed | C# scan; 0x1405d8920. |
| 0x08 | 1 | u8 | chunk_count |
4 in every bank. | confirmed | C# scan; 0x1405d8920 (loop bound). |
| 0x09 | 1 | u8 | name_length |
Length of name, without its NUL. |
confirmed | C# scan (= strlen in all); 0x1405d8920. |
| 0x0A | 2 | u16 | bank_number |
The bank's number in sead: see Where banks are. | likely | 0x1405db750, 0x1405db610 (% 257, a hash bucket); C# scan (values). |
| 0x0C | 4 | u32 | file_size |
The bank's size. The game doesn't read it. | confirmed | C# scan: the file's size in all 22,105 loose banks; the entry's size − 16 in all 620 pakd entries. Not read by 0x1405d95b0, 0x1405fe4e0, 0x1405f3570. |
| 0x10 | name_length + 1 |
raw | name |
ASCII, NUL-terminated: the folder or kind (bgm, menu, voice_SCENARIO_C1, event, …). |
confirmed | C# scan. |
after name |
to chunk_table |
raw | padding | Zeros up to 0x10 + align16(name_length + 1). |
confirmed | C# scan, all zero. |
Chunk table
At header_size + align16(name_length + 1) (sead computes it as ((name_length + 0x10) & 0xF0) + header_size when version is 2 or more, else header_size); chunk_count records of 16 bytes. In every bank the tags are snd , seq , trk , mtrl, in that order, and the first chunk starts right after the table (confirmed, C# scan).
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x0 | 4 | raw[4] | tag |
The chunk's tag. sead finds a chunk by searching the table for its tag (tags at 0x1406a60d0). |
likely | 0x1405d8920 (mtrl), the readers around it; C# scan. |
| 0x4 | 1 | u8 | unk_0x4 |
0 in every record. | confirmed | C# scan. |
| 0x5 | 1 | u8 | unk_0x5 |
0 in every record. | confirmed | C# scan. |
| 0x6 | 2 | u16 | unk_0x6 |
16 in every record. vgmstream: the record's size. | confirmed | C# scan; vgmstream. |
| 0x8 | 4 | u32 | offset |
The chunk's start, from the start of the bank; a multiple of 16, ascending. | confirmed | C# scan; 0x1405d8920. |
| 0xC | 4 | u32 | unk_0xC |
0 in every record. | confirmed | C# scan. |
Chunks
Each chunk starts with a 16-byte header and a table of offsets to what it holds: sounds (snd ), sequences (seq ), tracks (trk ) or materials (mtrl), the kinds sead's own classes name.
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x0 | 1 | u8 | version |
1 in snd , 0 in the others. When it isn't 0, an offset of 0xFFFFFFFF means none there. |
confirmed | C# scan; 0x1405d9100. |
| 0x1 | 1 | u8 | unk_0x1 |
0 in every chunk. | confirmed | C# scan. |
| 0x2 | 2 | u16 | header_size |
0x10 in every chunk: where the offset table starts. |
confirmed | C# scan; 0x1405d89b0. |
| 0x4 | 2 | u16 | count |
Number of sounds, sequences, tracks or materials. | confirmed | C# scan; 0x1405d9100. |
| 0x6 | 2 | u16 | unk_0x6 |
0 in every chunk. | confirmed | C# scan. |
| 0x8 | 8 | raw[8] | unk_0x8 |
0 in every chunk. | confirmed | C# scan. |
| 0x10 | 4 × count |
u32[count] |
offsets |
Where each one starts, from the start of the chunk. | confirmed | C# scan (all inside their chunk); 0x1405d89b0. |
- confirmed: the first one starts at
align16(end of the offset table). Source: C# scan, every chunk. - confirmed: 28
sndoffsets are0xFFFFFFFF(27 inSECommon.dat#1, 1 in#2): sound IDs with no sound. Source: C# scan. - The sounds, sequences and tracks aren't laid out here (Leads). A sound plays sequences, whose tracks play materials. Every loose bank but the two
voice_DAMAGE.sabhas exactly 1 sound, 1 sequence, 1 track and 1 material (confirmed, C# scan).
Materials
The mtrl chunk's materials, one per piece of audio. Each is a 32-byte header, the codec's extra data, then the audio.
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x00 | 1 | u8 | unk_0x00 |
1 in every material. No code read of it found. | — | C# scan. |
| 0x01 | 1 | u8 | unk_0x01 |
0 in every material. | confirmed | C# scan. |
| 0x02 | 2 | u16 | header_size |
0x20 in every material: where the extra data starts. |
confirmed | C# scan; 0x1405faf80. |
| 0x04 | 1 | u8 | channels |
1 (23,062) or 2 (392); 0 in empty materials. | confirmed | C# scan; read by 0x1405faf80. |
| 0x05 | 1 | u8 | codec |
See Codecs. 7 (HCA) in 23,454 materials, 0 (none) in 20. | confirmed | C# scan; 0x1405faf80. |
| 0x06 | 2 | u16 | number |
The material's index in the chunk (0 in empty ones). | confirmed | C# scan. |
| 0x08 | 4 | u32 | sample_rate |
48000 (22,534), 44100 (862), or 18000–32000 in 58; 0 in empty ones. | confirmed | C# scan; read by 0x1405faf80. |
| 0x0C | 4 | u32 | loop_start |
Loop start in samples; 0 when the material doesn't loop. | likely | 0x1405faf80; C# scan (looping music has values). Agrees with vgmstream. |
| 0x10 | 4 | u32 | loop_end |
Loop end in samples; 0 when the material doesn't loop. | likely | Same. |
| 0x14 | 4 | u32 | extra_data_size |
Size of the extra data. For HCA, 0x10 + the HCA header's size. |
confirmed | C# scan (all 23,454 HCA). |
| 0x18 | 4 | u32 | stream_size |
Size of the audio after the extra data. | confirmed | C# scan (tiling, below). |
| 0x1C | 2 | u16 | extra_data_id |
Varies (0 to over 1000). Not read by the code traced. vgmstream treats its low byte as an HCA key; no HCA here is encrypted. | guess | C# scan. vgmstream extradata_id. |
| 0x1E | 2 | u16 | unk_0x1E |
0 in every material. | confirmed | C# scan. |
| 0x20 | extra_data_size |
raw | extra_data |
Codec data; for HCA, below. | confirmed | C# scan. |
0x20 + extra_data_size |
stream_size |
raw | stream |
The encoded audio. | confirmed | C# scan. |
- confirmed: each material starts at
align16of the previous one's end, andfile_sizeisalign16of the last one's end, with zero padding. Source: C# scan, all 22,725 banks. - confirmed: the 20 empty materials (
codec0, every other field 0, no extra data or audio) are inSECommon.dat#1–#4and the Japanesevoice_DAMAGE.sab. Source: C# scan.
Codecs
The codec names come from sead's table at 0x140ca57e0: 0 None, 1 Pcm, 2 ADPCM, 3 VORBIS, 4 ATRAC9, 5 XMA2, 6 MP3, 7 HCA, 8 SWITCHOPUS (likely, hex of the executable's data; vgmstream has the same list). On PC, a sead voice plays only 1 (16-bit PCM), 2 (Microsoft ADPCM) and 7 (CRI HCA); any other code fails with Voice::Initialize failed no support format!!! (likely, 0x1405faf80). The game's banks use only 7.
HCA extra data
The extra_data of an HCA material: a 16-byte sead header, then the HCA file's own header (HCA, fmt, comp, ciph, … chunks, big-endian), whose frames are the stream. sead hands the HCA header and frames to CRI's HCA decoder (HCADecoder_*, overview); the HCA format isn't laid out here.
| Offset | Size | Type | Name | Meaning | Confidence | Source |
|---|---|---|---|---|---|---|
| 0x00 | 1 | u8 | version |
1 in every material. | confirmed | C# scan. |
| 0x01 | 1 | u8 | header_size |
0x10: where the HCA header starts. |
likely | C# scan. |
| 0x02 | 2 | u16 | hca_header_size |
The HCA header's size; equals the size in the HCA header itself. | confirmed | C# scan, all 23,454. |
| 0x04 | 2 | u16 | frame_size |
HCA frame size. | likely | 0x1405faf80. Agrees with vgmstream. |
| 0x06 | 2 | u16 | loop_start_frame |
The loop's first frame: equals the HCA loop chunk's value. |
likely | 0x1405faf80; C# scan. Agrees with vgmstream. |
| 0x08 | 2 | u16 | loop_end_frame |
The loop's last frame, as loop_start_frame. |
likely | Same. |
| 0x0A | 2 | u16 | encoder_delay |
A copy of the HCA fmt chunk's value at +0x0C, in all 22,538 where it's set. |
guess | C# scan. vgmstream "inserted samples (encoder delay)". |
| 0x0C | 1 | u8 | use_mixer |
0 in every material. When it isn't 0, the sead voice skips its own decode buffers and registers itself elsewhere (0x140627a60). |
guess | 0x1405faf80; C# scan. vgmstream "use mixer". |
| 0x0D | 1 | u8 | encrypted |
1 in the 84 music banks and the 14 Movie banks, 0 in all others; with 1 the sead voice allocates a one-frame buffer. Every HCA header here has cipher type 0 (none), so the name is only vgmstream's reading. |
guess | 0x1405faf80; C# scan. vgmstream (encryption flag). |
| 0x0E | 2 | u16 | unk_0x0E |
likely | vgmstream (reserved). | |
| 0x10 | hca_header_size |
raw | hca_header |
The HCA header, starting HCA\0. |
confirmed | C# scan, all 23,454. |
How the game reads it
Static analysis, so likely throughout; the functions are in the names list.
On-memory banks
The sound effects, voice_DAMAGE.sab, the scene banks and the title banks.
- likely: the game reads the whole file or
pakdentry into memory and hands sead a pointer to it (0x1401a9840→0x1405b8bf0); sead keeps the pointer and doesn't copy or size-check the bank. Source:0x1405d95b0,0x1405d9670,0x1405fe4e0. - likely: sead checks only the magic:
sabfmakes a sound bank,mabfa music bank, anything else fails (0xF0000003). It readsversion,header_size,chunk_countandname_lengthto find the chunk table, then each chunk by its tag, with no bounds checks: a missing chunk is a null pointer.file_sizeisn't read, and there is no checksum. Source:0x1405d95b0,0x1405fe4e0,0x1405d8920. - likely: a bank goes into one of 32 bank-controller slots: the sound effects into the slots of the array at
0x14067bc1c({2, 3, 1, 6}, not theirSECommon.dat#0rows), the scene and title banks into the first free one. Source:0x1401a9840,0x140285a50,0x140285930,0x1403585a0,0x1404abe70.
Streaming banks
Music, voices and movie audio.
- likely: a stream player opens the file by ID and reads it through a ring buffer, up to the size the file table gives for it (the handle's size). Source:
0x140290290,0x140293590,0x1402936d0; filetable.md. - likely: the player creates a sead streaming bank with a 64 KiB (
0x10000) work buffer and one audio stream (0x140290830→0x1401a99b0→0x1405b8e00→0x1405d9920). sead reads the first 16 bytes and checks the magic (stream data is invalid!!if wrong), then reads up to the end of the chunk table, then up to the end of themtrlchunk's offset table, all into the work buffer; the rest of the buffer becomes the audio stream's buffer, which reads each material from the file by its offset. Source:0x1405f3570,0x1405f2ad0. - confirmed: the part before the materials (header, chunk table,
snd,seq,trk, themtrlheader and offsets) is at most 832 bytes in every loose bank; only the twovoice_DAMAGE.sab, which are loaded whole, are larger than 64 KiB there (0x138E0and0x13A20). Source: C# scan.
Playing a material
- likely: the sead voice reads
channels,codec,sample_rate,loop_startandloop_endfrom the material, and for HCA the extra data (frame_size, the loop frames,use_mixer,encrypted) and the HCA header, which it passes to the HCA decoder; it fails on a codec other than 1, 2 or 7. Source:0x1405faf80(Ghidra names itSQEX::Sd::MabFile::Music::Music; its strings areVoice::Initialize's).
Rebuilding and replacing audio
None of this is proven by a round trip or an in-game test yet.
- A bank is rebuilt from its parts in this order, every offset recomputed: header and name, padding to 16, the chunk table, then each chunk (header, offset table, padding to 16, its sounds, sequences or tracks), then the materials, each at a 16-byte boundary, then zero padding to 16.
file_sizeis the result's size. confirmed for the original files: every bank fits this layout (C# scan). - A bank inside a
pakdis followed by 16 zero bytes, like otherpakdentries (containers.md). confirmed: all 620 (C# scan). - likely: the game checks no checksum or size in a bank, so a replaced bank need only be valid, not byte-identical. Source: How the game reads it.
To replace a sound's audio, a modder keeps:
- the magic,
version2, the four chunks in the same order, and every offset correct; sead follows them without checks; bank_number, and the sounds, sequences and tracks as they are, unless the sound itself is redesigned;codec7 (HCA), or 1 or 2; the game ships only HCA, and HCA without encryption (ciphtype 0) is what every bank has;channels,sample_rate,loop_start,loop_end,extra_data_sizeandstream_sizematching the new audio and its HCA header;- for a sound effect, the bank's sound count: the IDs in
SECommon.dat#0must stay inside it; - for a loose bank, its size in
FileTable.bin: the game reads only that many bytes, so a longer bank is cut off; - for a streaming bank, the part before the materials under 64 KiB, and one sound.
Leads
- Lay out the sounds, sequences and tracks (
SABSOUNDCHUNKHEADERand the readers near0x1405d8bb0), and check whether any of them holds a material's length, which a longer replacement would have to change. vgmstream reads a sound's name fromsnd(at+0x50for version ≤ 8). A track's u16+4is a material index and its u16+6a bank number (0x14062a370). - Confirm in game that a bank with new HCA audio, a changed
FileTable.binsize and nothing else changed plays (R7). - Which code opens
sound/bin/event/3013_wheel_of_fortune.sab(group 700, the Warren Report's), and how a scene picks its banks among itspakdentries (0x140350380with kind 8). - Where the game looks a voice name up in the
sound_ja.dat/sound_en.datmaps (the voiced-talk natives 283–315), and whathas_voice0 does there. - What
unk_0x01,unk_0x02andunk_0x04insound.dat#1andunk_0x01in#0mean, and the sound flagssound_flag_idnames; which objectDAT_140ce4e30is (the settings, or a save; the SoundManager's base class isTaco::Application::SystemSaveDataEventHandler, a hint only). has_voicerests on data only: in#0–#9it matches "a file named<name>.sabexists" except one row per language with 0 whose file exists; in#10it is 1 in every row, though 754 ja and 737 en names have no file of that name. No code reads it.- "Voice": sead's
Voice(the playing source) and the game's voice lines are different things; this doc says "sead voice" for the first. - Why voice and movie file IDs are all even; the file table may hold the odd ones for something else.
- What
unk_0x05bit 0 in the header does (sead reads it in0x1405b8070and0x1405db4f0), and whatuse_mixerregisters the voice with (0x140627a60). - The
mabf(music bank) path is linked but no game file uses it.