============================================================================= Sierra On-Line Audio File Format Description 25-08-1999 ============================================================================= By Asatur V. Nazarian (samael@avn.mccme.ru) In this document I'll try to describe audio file format used in many Sierra On-Line games. In most games these files are contained within .SFX and .AUD resource files (usually named RESOURCE.SFX and RESOURCE.AUD). When encountered as stand-alone files, they usually have extension .AUD (but it has nothing to do with Westwood's AUD audio file format!). The games using this format include: King's Quest 6, Leisure Suit Larry 6, Pepper's Adventures in Time, Quest For Glory 3, Space Quest 5, Torin's Passage, Phantasmagoria, Phantasmagoria II: The Puzzle Of Flesh, Gabriel Knight, Gabriel Knight II, King's Quest 7, King's Quest 8. The files this document deals with have extensions: .SFX, .AUD. Throughout this document I use C-like notation. =================== 1. AUD File Header =================== The AUD file has the following header: struct AUDHeader { BYTE bID; BYTE bShift; char szID[4]; WORD wSampleRate; BYTE bFlags; DWORD dwDataSize; }; bID -- is equal to 0x8D in all Sierra On-Line games I've seen, except for King's Quest 8, where it equals to 0x0D. bShift -- defines where audio data starts: (bShift+2) is the starting position of the audio data relative to the file start (NOT to the start of RESOURCE.SFX/RESOURCE.AUD containing this file). szID -- always "SOL\0". Note that there're four bytes including terminating zero! wSampleRate -- sample rate for the file. bFlags -- bit-mapped flags: bit 0 -- if set, audio data is compressed (otherwise it's PCM), bit 1 -- ??? (I've never seen it set), bit 2 -- if set, audio data is 16-bit (8-bit otherwise), bit 3 -- if set, audio data is in signed format (unsigned otherwise): 16-bit sound is signed and 8-bit is unsigned, bit 4 -- if set, sound is stereo (mono otherwise). dwDataSize -- size of the audio data (in bytes). ================= 2. AUD File Data ================= Starting at (bShift+2) from the file start, comes AUD audio data. If bit 0 of bFlags is not set, it's just PCM: 8-bit or 16-bit, signed or unsigned. Otherwise it's compressed with the algorithm, which I refer to as SOL ADPCM. SOL ADPCM has two types: 8-bit (for 8-bit sound) and 16-bit (for 16-bit sound). =========================================== 3. 8-bit SOL ADPCM Decompression Algorithm =========================================== Let's CurSample be current sample value and InputBuffer contains SOL ADPCM compressed data: SHORT CurSample; BYTE InputBuffer[InputBufferSize]; BYTE code; DWORD i; // index into InputBuffer CurSample=0x80; // unsigned 8-bit for (i=0;i> 4) #define LONIBBLE(byte) ((byte) & 0x0F) Output() is just a placeholder for any action you would like to perform for decompressed sample value. SOLTable3bit is the delta table given near the end of this document. INDEX4(code) is really tricky thing. In some games (mostly older ones) it should be the following: #define INDEX4(code) (0xF-(code)) While in some other games it's the following: #define INDEX4(code) ((code) & 7) "Old" INDEX4 is used, for example, in King's Quest 6, Quest For Glory 3, Gabriel Knight. "New" INDEX4 is used in Torin's Passage, maybe in other games. I do not know the reliable way to figure out which of those you should use for certain file, but currently I use the simplest technique: I just decode first, say, 1Kb of data using both approaches and look if one of them results in the output stream which is far from reasonable 8-bit unsigned sound (that is, it's mean sample value is far from 0x80). Clip8BitSample is quite evident: SHORT Clip8BitSample(SHORT sample) { if (sample>255) return 255; else if (sample<0) return 0; else return sample; } Note that the HIGHER nibble is processed first. ============================================ 4. 16-bit SOL ADPCM Decompression Algorithm ============================================ It's just analoguous to the 8-bit decompression scheme: LONG CurSample; BYTE InputBuffer[InputBufferSize]; BYTE code; DWORD i; CurSample=0x0000; // signed 16-bit for (i=0;i32767) return 32767; else if (sample<-32768) return (-32768); else return sample; } Note that the decompression schemes are given ONLY for unsigned 8-bit sound and signed 16-bit sound. I've never seen signed 8-bit or unsigned 16-bit sound in AUD format, but to support these you should only support the correspondent clipping (-128..127 for signed 8-bit and 0..65535 for unsigned 16-bit) and make additional conversion before outputting the sample value: signed->unsigned for 8-bit sound or unsigned->signed for 16-bit sound, provided that you've initialized CurSample to the correspondent value: 0x00 for signed 8-bit and 0x8000 for unsigned 16-bit. Also, those algorithms are ONLY for mono sound, but their improvement for stereo is simple: for 8-bit sound left channel is in HIGHER nibble and right is in LOWER one, while for 16-bit sound left channel is first byte and right chennel is second one. Note that you should maintain two different CurSample variables for left and right channels: CurSampleLeft and CurSampleRight. Of course, both decompression routines described above may be greatly optimized. ==================== 5. SOL ADPCM Tables ==================== BYTE SOLTable3bit[]= { 0, 1, 2, 3, 6, 0xA, 0xF, 0x15 }; WORD SOLTable7bit[]= { 0x0, 0x8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 0x160, 0x170, 0x180, 0x190, 0x1A0, 0x1B0, 0x1C0, 0x1D0, 0x1E0, 0x1F0, 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230, 0x238, 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, 0x278, 0x280, 0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0, 0x2B8, 0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, 0x2F0, 0x2F8, 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, 0x338, 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370, 0x378, 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, 0x3B0, 0x3B8, 0x3C0, 0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, 0x3F0, 0x3F8, 0x400, 0x440, 0x480, 0x4C0, 0x500, 0x540, 0x580, 0x5C0, 0x600, 0x640, 0x680, 0x6C0, 0x700, 0x740, 0x780, 0x7C0, 0x800, 0x900, 0xA00, 0xB00, 0xC00, 0xD00, 0xE00, 0xF00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 }; ================================================ 6. AUD Resources: RESOURCE.AUD and RESOURCE.SFX ================================================ When stored in .SFX/.AUD resources, the audio files are stored "as is", without compression (unlike other Sierra On-Line resource files) or encryption. That means if you want to play/extract AUD file from the RESOURCE.SFX/.AUD resource you just need to search for szID id-string ("SOL\0") and read AUDHeader starting at the position two bytes before found id-string. This will give you starting point of the file and the size of the file will be (dwDataSize+bShift+2). =========== 7. Credits =========== Anthony Larme (larme@bit.net.au) http://www.bit.net.au/~larme/ [Phantasmagoria Memorial Websites] It was just him who inspired me to explore this format deeper and helped me much with the AUDs from Sierra's games I had no access to. It was also him who tested my Game Audio Player on many Sierra's games and reported me results. ---------------------------------------- Asatur V. Nazarian (samael@avn.mccme.ru) http://anx.da.ru http://www.fortunecity.com/campus/electrical/81/samael.html Here you can find my GAP program which can search for SOL audio files in .SFX/.AUD resources, extract them, convert them to WAV and play them back. There's also complete source code for GAP and all its plug-ins there, including SOL plug-in, which could be used for further details on how you may deal with this format.