EvilEngine/Sound Format
This page contains information on how sounds and music are stored in EvilEngine games.
In Scooby-Doo! Night of 100 Frights and SpongeBob SquarePants: Battle for Bikini Bottom sounds are usually contained in SND and SNDS assets, with the SNDI asset containing information about them. Each HOP file (sometimes the HIP) contains multiple SND and SNDS assets and one single SNDI asset. There doesn't seem to be an internal difference between SND and SNDS other than being stored as different assets; sound effects are usually in SND and voice lines and music (stored in mnu5.HIP in BFBB) are usually in SNDS.
In The SpongeBob SquarePants Movie, The Incredibles and The Incredibles: Rise of the Underminer, SND and SNDS assets are empty, and instead, all of the sounds are contained entirely in the SNDI asset.
Xbox and the FMOD GameCube games are the only platform which support Stereo. PS2 and GameCube (Scooby/BFBB) only support Mono due to its sound format (VAG and DSP).
SND/SNDS Format
SND/SNDS | |
---|---|
Sound/Streaming Sound | |
Type | Binary |
Games used | Night of 100 Frights Battle for Bikini Bottom |
GameCube (Night of 100 Frights/Battle for Bikini Bottom)
The audio format on the GameCube version of Scooby-Doo and BFBB is a Nintendo DSP-ADPCM audio file stripped of its 0x60 bytes *.dsp header: all of a level's sound headers are kept separate in the single SNDI asset.
GameCube (The SpongeBob SquarePants Movie - Ratatouille Prototype)
Those games on GameCube are the only EvilEngine games which utilize the FMOD audio engine. This allows stereo support although it still uses the same Nintendo non-stereo codec like Scooby-Doo and BFBB by interleaving two mono tracks. SND/SNDS assets are empty; all sounds are contained entirely in SNDI assets within "FSB3" (FMOD Sample Bank) files.
Xbox
The audio format on the Xbox version of all 5 games is either Xbox ADPCM, or raw 16-bit Linear PCM stripped of its *.wav header: all of a level's sound headers are kept separate in the single SNDI asset.
PlayStation 2
The audio format on the PS2 version of all 5 games is a PS2 VAG sound file stripped of its 0x30 bytes header (except in Scooby-Doo): all of a level's sound headers are kept separate in the single SNDI asset. The first 0x10 bytes are usually null to initialize the SPU.
CSSS Format
CSSS | |
---|---|
Cutscene Streaming Sound | |
Type | Binary |
Games used | The SpongeBob SquarePants Movie Rise of the Underminer |
Same as SND/SNDS in Movie and The Incredibles: CSSS assets are empty and only used to "link" the sound header in SNDI with the sound data stored in cutscenes.
SNDI Format
SNDI | |
---|---|
Sound Info | |
Type | Binary |
Games used | Night of 100 Frights Battle for Bikini Bottom |
GameCube (Night of 100 Frights/Battle for Bikini Bottom)
The SNDI has a 0xC or 0x10 bytes big-endian header:
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | u32 | num_effects | Amount of headers for SND assets |
0x04 | u32 | - | Padding "0xCDCDCDCD" |
0x08 | u32 | num_streams | Amount of headers for SNDS assets |
0x0C | u32 | num_cutscenes | Amount of headers used in cutscene assets. (BFBB only) |
After that, there are three consecutive arrays of SNDI entries: one for the SND, one for the SNDS and one for cutscene headers. Each SNDI entry is 0x64 bytes long, big-endian and follows this structure:
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | u32 | num_samples | Amount of raw samples |
0x04 | u32 | num_adpcm_nibbles | Amount of ADPCM nibbles (including frame headers) |
0x08 | u32 | sample_rate | Sample rate in Hz |
0x0C | u16 | loop_flag | 0 = Not Looped, 1 = Looped |
0x0E | u16 | format | Always 0 |
0x10 | u32 | sa | Loop start offset, in nibbles |
0x14 | u32 | ea | Loop end offset, in nibbles |
0x18 | u32 | ca | Initial offset value, always 2 |
0x1C | s16[16] | coef | Decoder coefficients |
0x3C | u16 | gain | Gain factor. Always 0 |
0x3E | u16 | pred_scale | Predictor and scale. This will be initialized to the predictor and scale value of the sample’s first frame. |
0x40 | u16 | yn1 | History data; used to maintain decoder state during sample playback |
0x42 | u16 | yn2 | |
0x44 | u16 | loop_pred_scale | Predictor/scale for the loop point frame. If the sample does not loop, this value is zero |
0x46 | u16 | loop_yn1 | History data for the loop point. If the sample does not loop, this value is zero. |
0x48 | u16 | loop_yn2 | |
0x4A | u8[22] | pad | Unknown values |
0x60 | AssetID | uAssetID | Asset ID of corresponding SND/SNDS asset |
GameCube (The SpongeBob SquarePants Movie - Ratatouille Prototype)
In those games, the SNDI contains not only the sound headers but also the entirety of the sound data themselves.
SNDI Header
The SNDI itself has a 0x20 byte big-endian header:
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | AssetID | uSoundInfoID | Asset ID of this asset |
0x04 | u32 | uSumFSBSize / nOffsetFinalFSBEnd | Total size of all FSB3 files, also offset to the Footer section (relative to end of this header, add 0x20 to get the absolute offset). |
0x08 | s32 | pFMusicMod | null |
0x0C | s32 | pFSBFileArray | null |
0x10 | s32 | pWavInfoArray | null |
0x14 | s32 | pCutsceneAudioHeaders | null |
0x18 | u16 | nWavFiles | Total amount of sounds |
0x1A | u16 | nSounds | Amount of sounds in the first FSB3 of the SNDI |
0x1C | u16 | nStreams | Amount of sounds combining all FSB3's which are not the first of the SNDI |
0x1E | u8 | nFSBFiles | Amount of FSB3 files |
0x1F | u8 | nCutsceneAudioHeaders | Amount of cutscene headers |
FSB3 Format
All sounds are contained inside FSB3 files. The first FSB3 is loaded into RAM, thus it has a ~8MB size limit, but can contain multiple sounds. Every subsequent FSB3 is streamed, thus it has no size limit but can only contain 1 sound. Read FMOD for more information about FMOD and the FSB3 format. Note: SNDI header and the Footer section are stored in big-endian while FSB's are little-endian.
This is a section which occurs at the end of the SNDI. The offset to this is set in the SNDI header. This is its structure:
FSB3 Offsets
Offset | Size | Description |
---|---|---|
0x00 | 4[nFSBFiles] | Array of ints. The amount of ints is the same amount of FSB3 files, as set in the SNDI header. Each int is an offset to the start of each FSB3 file, relative to the end of the SNDI header. Add 0x20 to each value to get the absolute offset. |
After this, we have nWavFiles times the following structure, which refers to each sound in the SNDI:
gcWavInfo
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | AssetID | uAssetID | Asset ID of sound. |
0x04 | u8 | uFlags | Loop and format flag.
|
0x05 | u8 | uAudioSampleIndex | Zero-based index of sound inside its FSB3 file |
0x06 | u8 | uFSBIndex | Zero-based index of the FSB3 file containing this sound (index to the FSB3 offsets array) |
0x07 | u8 | uSoundInfoIndex |
Cutscene Header
At the end of the SNDI are the cutscene header. They are a 0x60 bytes dsp header + asset id of corresponding CSSS asset (same format as header in Scooby/BFBB).
Xbox
Stored in little-endian. The SNDI has a 0xC byte header:
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | u32 | num_effects | Amount of headers for SND assets |
0x04 | u32 | num_streams | Amount of headers for SNDS assets |
0x08 | u32 | num_cutscenes | Amount of headers used in cutscenes assets |
After that, there are three consecutive arrays of entries: one for the SND, one for the SNDS and one for cutscene headers. Each entry is 0x2C bytes long and follows this structure:
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | u16 | wFormatTag | 1 if PCM, 0x69 (105) if Xbox ADPCM |
0x02 | u16 | nChannels | Number of audio Channels. Always 1 (Mono), but support for 2 (Stereo) |
0x04 | u32 | nSamplesPerSec | Sampling rate in Hz |
0x08 | u32 | nAvgBytesPerSec | Used as an estimate for bytes per second. Can be ignored |
0x0C | u16 | nBlockAlign | Block size in bytes. 2 if PCM, 36 (0x24) if Xbox ADPCM |
0x0E | u16 | wBitsPerSample | Number of bits used per sample. 16 (0x10) if PCM, 4 if Xbox ADPCM |
0x10 | u16 | cbSize | Amount of extra bytes. Always 0 if PCM, 2 if Xbox ADPCM (NibblesPerBlock) |
0x12 | u16 | NibblesPerBlock | ADPCM nibbles per block. 64 (0x40) if Xbox ADPCM, 0 if PCM |
0x14 | u32 | Datasize | Size of corresponding sound data. |
0x18 | AssetID | Sound Asset ID | Asset ID of corresponding SND or SNDS asset |
0x1C | u32 | flag/loop | 0 = Not Looped, 1 = Looped, 2 = Unknown |
0x20 | u8[12] | Padding | null |
More information about Xbox ADPCM.
PlayStation 2
The SNDI has a 0x8 or 0xC bytes little-endian header:
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | AssetID | assetID | AssetID of SNDI (Only present in Incredibles and ROTU) |
0x04/0x00 | uint | num_effects | Amount of headers for SND assets |
0x08/0x04 | uint | num_streams | Amount of headers for SNDS assets |
After that, there are two consecutive arrays of SNDI entries: one for the SND and one for the SNDS. Each SNDI entry is 0x30 bytes long and is a Sony VAG header.
Scooby-Doo: Night of 100 Frights
Stored in big-endian, except reserved and probably also reserved2 which are in little-endian.
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | char[4] | id | "VAGp" |
0x04 | u32 | version | Always 3, 4 or 32 |
0x08 | u32 | reserved | Asset ID of corresponding SND/SNDS |
0x0C | u32 | data_size | Size of corresponding SND/SNDS asset |
0x10 | u32 | frequency | Sampling rate in Hz |
0x14 | u32[3] | reserved2 |
|
0x20 | char[16] | name | Old filename, can be ignored |
There are two 0x30 bytes "RIFF" header with a different format, one in I001.HIP and in S005.HIP. However, they don't seem to work ingame.
Battle for Bikini Bottom - Rise of the Underminer
Stored in little-endian.
Offset | Type | Variable | Description |
---|---|---|---|
0x00 | char[4] | id | "VAGp" |
0x04 | u32 | version | 4 or 32 in BFBB, always 32 in every other game |
0x08 | u32 | assetID | Asset ID of corresponding SND/SNDS |
0x0C | u32 | data_size | Size of corresponding SND/SNDS asset |
0x10 | u32 | frequency | Sampling rate in Hz |
0x14 | u32 | streamInterleaveSize | |
0x18 | u32 | streamInterleaveCount | |
0x1C | u32 | reserved2 | |
0x20 | char[16] | name | Old filename, can be ignored |