OpenKh

This is a project centralizes all the technical knowledge of Kingdom Hearts series in one place, providing documentation, tools, code libraries and the foundation for modding the commercial games.

View on GitHub

Kingdom Hearts DS Games - SDAT File Format

This document covers the SDAT file format. SDAT files contain music and sound effect data, either in the form of sequenced sound or as full audio samples. All information was referenced from this document

The Nitro Composer packs various types of sound files in a single file (*.sdat) for use in DS games.

Inside the SDAT you will find: SSEQ (Sequence), SSAR (Sequence Archive), SBNK (Sound Bank), SWAR (Wave Archive), STRM (Stream).

SSAR is a collection of SSEQ, while SWAR is a collection of SWAV.

The file has the following structure:

  1. Header
  2. Symbol Block
  3. Info Block
  4. File Allocation Table (FAT)
  5. File Block

The Header appears at offset 0 in the SDAT file.

Position Type Name Description
0x0 int8[4] type ‘SDAT’
0x4 uint32 magic 0x0100feff
0x8 uint32 nFileSize  
0xC uint16 nSize  
0xE uint16 nBlock usually 4, but some have 3 only ( Symbol Block omitted )
0x10 uint32 nSymbOffset offset of Symbol Block = 0x40
0x14 unit32 nSymbSize size of Symbol Block
0x18 uint32 nInfoOffset offset of Info Block
0x1C uint32 nInfoSize size of Info Block
0x20 uint32 nFatOffset offset of FAT
0x24 uint32 nFatSize size of FAT
0x28 uint32 nFileOffset offset of File Block
0x2C uint32 nFileSize size of File Block
0x30 uint8[16] reserved unused, 0s

Symbol Block

It appears at offset 0x40, right after the Header. It may be omitted. It contains the symbols (or “filenames”) of each sound file in the SDAT file. All offsets are relative to this block’s starting address (i.e. 0x40). NB. Some files doesn’t have Symbol Block. NB. The value of nSize of Symbol Block may not be 32-bit aligned. However, the value of nSymbSize in Header is.

Position Type Name Description
0x0 char[4] type ‘SYMB’
0x4 uint32 nSize size of this Symbol Block
0x8 uint32[8] nRecOffset offset of Records (note below)
0x28 u8[24] reserved unused, 0s

Symbol Block - Record

There are a total of 8 records in the Symbol Block. They are: | Record No. | Record Name | Description |————|————-|———— | 0 | SEQ | Sequence (for music) | 1 | SEQARC | Sequence Archive (for sound effect) | 2 | BANK | Sound Bank | 3 | WAVEARC | Wave Archive | 4 | PLAYER* | Player (Group-related) | 5 | GROUP | Group of SEQ/SEQARC/BANK/WAVEARC | 6 | PLAYER2* | Player2 (Stream-related) | 7 | STRM | Stream

All offsets are relative to Symbol block’s starting address (i.e. 0x40). Each record (except Record 1 “SEQARC”) has the following structure:

Position Type Name Description
0x0 uint32 nCount No of entries in this record
0x4 uint32 nEntryOffset Array of offsets of each entry

For Record 1 (SEQARC), it is a group which contains sub-records. The sub-record is of the same structure as SDATSYMBREC (above). Record 1 has the following structure:

Position Type Name Description
0x0 unit32 nCount No of entries in this record
0x4 uint32 nEntryOffset Offset of this Group’s symbol
0x8 uint32 nSubRecOffset Offset of the sub-record

Below is an example to access these records:

```SDATSYMB *symb; int i, j; char *szSymbol; … // access record 0 ‘SSEQ’ SDATSYMBREC *symb_rec = (SDATSYMBREC *) ( (u8 *)symb + symb->RecOffset[0] );

for ( i = 0; i < symb_rec->nCount; i++ ) { // print out the symbol szSymbol = (char *) ( (u8 *)symb + symb_rec->nEntryOffset[i] ); printf( “%s\n”, szSymbol ); } …

// access record 1 ‘SSAR’ SDATSYMBREC2 symb_rec2 = (SDATSYMBREC *)( (u8 *)symb + symb->RecOffset[1] );

for ( i = 0; i < symb_rec2->nCount; i++ ) { szSymbol = (char *) ( (u8 *)symb + symb_rec2->Group[ i ].nEntryOffset ); printf( “%s\n”, szSymbol );

SDATSYMBREC *symb_subrec = (SDATSYMBREC *) ( (u8 *)symb + symb_rec2->Group[i].nSubRecOffset );
for ( j = 0; j < symb_subrec->nCount; j++ )
{
	// print out sub record's symbols
	szSymbol = (char *) ( (u8 *)symb + symb_subrec->nEntryOffset[i] );
	printf( "%s\n", szSymbol );
} } ```

Symbol Block - Entry

EXCEPT for Record 1 “SEQARC”, an Entry in the record is a null terminated string. This corresponds to the “filename” of a sound file in the SDAT file.

For Record 1 “SEQARC”, since a SEQARC file is a collection of Sequence files, therefore this record contains a sub-record. And this sub-record contains the symbols (“filenames”) of each of the archived SEQ files.

Info Block

The Info Block appears just after the Symbol Block. It contains some information of each sound file in the SDAT file. All offsets are relative to this block’s starting address.

Position Type Name Description
0x0 char[4] type ‘INFO’
0x4 uint32 nSize size of this Info Block
0x8 uint32[8] nRecOffset offset of a Record
0x48 uint8[24] reserved unused, 0s

Info Block - Record

There are a total of 8 records in the Info Block. The Record Names in 1.2.1 above applies here as well. All offsets are relative to Info block’s starting address. With modifications, the code example above could be used to access the Info records and entries.

Position Type Name Description
0x0 uint32 nCount No of entries in this record
0x4 uint32 nEntryOffset array of offsets of each entry

Info Block - Entry

Record 0 “SEQ” - The Info Entry for SEQ contains playback information.

Position Type Name Description
0x0 u16 fileID for accessing this file
0x2 u16 unknown  
0x4 u16 bnk Associated BANK
0x6 u8 vol Volume
0x7 u8 cpr  
0x8 u8 ppr  
0x9 u8 ply  
0xA u8[2] unknown  

Record 1 “SEQARC”

Position Type Name Description
0x0 uint16 fileID  
0x2 uint16 unknown  

Remarks: no info is available for SEQARC files. The info of each archived SEQ is stored in that SEQARC file.

Record 2 “BANK”

Position Type Name Description
0x0 uint16 fileID  
0x2 uint16 unknown  
0x4 uint16[4] wa Associated WAVEARC. 0xffff if not in use

Remarks: Each bank can links to up to 4 WAVEARC files. The wa[4] stores the WAVEARC entry number.

Record 3 “WAVEARC”

Position Type Name Description
0x0 uint16 fileID  
0x2 uint16 unknown  

Remarks: This is not a new structure. It is the same as SDATINFOSSAR above for Record 1.

Record 4 “PLAYER”

Position Type Name Description
0x0 uint8 unknown  
0x1 uint8[3] padding  
0x4 uint32 unknown2  

Remarks: None

Record 5 “GROUP”

Position Type Name Description
0x0 uint32 nCount number of sub-records
0x4 uint32 type  
0x8 uint32 nEntry  

Remarks: SDATINFOPlayer::Group::type can be one of the following values. nEntry is the entry number in the relevant Record (SEQ/SEQARC/BANK/WAVEARC).

Value Type
0x0700 SEQ
0x0803 SEQARC
0x0601 BANK
0x0402 WAVEARC

Record 6 “PLAYER2”

Position Type Name Description
0x0 uint8 nCount  
0x1 uint8[16] v 0xff if not in use
0x11 uint8[7] reserved padding, 0s

Remarks: The use is unknown. The first byte states how many of the v[16] is used (non 0xff).

Record 7 “STRM”

Position Type Name Description
0x0 uint16 fileID for accessing the file
0x2 uint16 unknown  
0x4 uint8 vol volume
0x5 uint8 pri  
0x6 uint8 ply  
0x7 uint8[5] reserved  

Remarks: ‘ply’ means play?, ‘pri’ means priority?

FAT

The FAT appears just after the Info Block. It contains the records of offset and size of each sound file in the SDAT file.

Position Type Name Description
0x0 char[4] type ‘FAT ‘
0x4 uint32 nSize size of the FAT
0x8 uint32 nCount Number of FAT records
0xC SDATFATREC[1] Rec Arrays of FAT records

FAT - Record (SDATFATREC)

It contains the offset and size of the sound file. All the offsets are relative to the SDAT Header structure’s beginning address.

Position Type Name Description
0x0 uint32 nOffset offset of the sound file
0x4 uint32 nSize size of the Sound file
0x8 uint32[2] reserved always 0s, for storing data in runtime.

File Block

The File Block is the last block and appears just after the FAT. It has a small header (the structure below) which contains the total size and number of sound of files. All the sound files are stored after this structure.

Position Type Name Description
0x0 char[4] type[4] ‘FILE’
0x4 uint32 nSize size of this block
0x8 uint32 nCount Mumber of sound files
0xC uint32 reserved always 0