CRYO APC

From MultimediaWiki
Jump to navigation Jump to search

This covers the audio format for one of the versions of CRYO's video formats.


Credit

This file comes from wotsit.org and was originally written by Valery V. Anisimovsky.

APC Audio Files

The music, sfx, speech and video (.HNM) soundtracks in some CRYO Interactive games are .APC stand-alone files. APC file has the following header:

struct APCHeader
{
 char	szID[8];
 char	szVersion[4];
 DWORD dwOutSize;
 DWORD dwSampleRate;
 LONG	lSampleLeft;
 LONG	lSampleRight;
 DWORD dwStereo;
};

szID -- ID string, which is "CRYO_APC".

szVersion -- version ID string, all files in the mentioned games have this set to "1.20". Note that this field may, in principle, vary.

dwOutSize -- number of samples in the file. May be used for song length (in seconds) calculation.

dwSampleRate -- sample rate for the file.

lSampleLeft -- the initial value for the left sample (see below).

lSampleRight -- the initial value for the right sample (see below).

dwStereo -- this seems to be boolean stereo flag: if this is not zero, the audio stream in the file is stereo (music and partly video soundtracks), otherwise it's mono (sfx, speech).

The resolution is NOT specified in the header, so the default value (16-bit) should be used.

After the APCHeader IMA ADPCM compressed sound data comes. You may find IMA ADPCM decompression scheme description further in this document.

IMA ADPCM Decompression Algorithm

During the decompression four LONG variables must be maintained for stereo stream: lIndexLeft, lIndexRight, lCurSampleLeft, lCurSampleRight and two -- for mono stream: lIndex, lCurSample. At the beginning of the file you must initialize lCurSampleLeft/Right variables to the values from APCHeader while lIndexLeft/Right are initialized to zeroes. Note that LONG here is signed.

Here's the code which decompresses one byte of IMA ADPCM compressed stereo stream. Other bytes are processed in the same way.

BYTE Input; // current byte of compressed data
BYTE Code;
LONG Delta;

Code=HINIBBLE(Input); // get HIGHER 4-bit nibble

Delta=StepTable[lIndexLeft]>>3;
if (Code & 4)
  Delta+=StepTable[lIndexLeft];
if (Code & 2)
  Delta+=StepTable[lIndexLeft]>>1;
if (Code & 1)
  Delta+=StepTable[lIndexLeft]>>2;
if (Code & 8) // sign bit
  lCurSampleLeft-=Delta;
else
  lCurSampleLeft+=Delta;

// clip sample
if (lCurSampleLeft>32767)
  lCurSampleLeft=32767;
else if (lCurSampleLeft<-32768)
  lCurSampleLeft=-32768;

lIndexLeft+=IndexAdjust[Code]; // adjust index

// clip index
if (lIndexLeft<0)
  lIndexLeft=0;
else if (lIndexLeft>88)
  lIndexLeft=88;

Code=LONIBBLE(Input); // get LOWER 4-bit nibble

Delta=StepTable[lIndexRight]>>3;
if (Code & 4)
  Delta+=StepTable[lIndexRight];
if (Code & 2)
  Delta+=StepTable[lIndexRight]>>1;
if (Code & 1)
  Delta+=StepTable[lIndexRight]>>2;
if (Code & 8) // sign bit
  lCurSampleRight-=Delta;
else
  lCurSampleRight+=Delta;

// clip sample
if (lCurSampleRight>32767)
  lCurSampleRight=32767;
else if (lCurSampleRight<-32768)
  lCurSampleRight=-32768;

lIndexRight+=IndexAdjust[Code]; // adjust index

// clip index
if (lIndexRight<0)
  lIndexRight=0;
else if (lIndexRight>88)
  lIndexRight=88;

// Now we've got lCurSampleLeft and lCurSampleRight which form one stereo
// sample and all is set for the next input byte...
Output((SHORT)lCurSampleLeft,(SHORT)lCurSampleRight); // send the sample to output

HINIBBLE and LONIBBLE are higher and lower 4-bit nibbles:

#define HINIBBLE(byte) ((byte) >> 4)
#define LONIBBLE(byte) ((byte) & 0x0F)

Note that depending on your compiler you may need to use additional nibble separation in these defines, e.g. (((byte) >> 4) & 0x0F).

StepTable and IndexAdjust are the tables given in the next section of this document.

Output() is just a placeholder for any action you would like to perform for decompressed sample value.

Of course, this decompression routine may be greatly optimized.

As to mono sound, it's just analoguous:

Code=HINIBBLE(Input); // get HIGHER 4-bit nibble

Delta=StepTable[lIndex]>>3;
if (Code & 4)
  Delta+=StepTable[lIndex];
if (Code & 2)
  Delta+=StepTable[lIndex]>>1;
if (Code & 1)
  Delta+=StepTable[lIndex]>>2;
if (Code & 8) // sign bit
  lCurSample-=Delta;
else
  lCurSample+=Delta;

// clip sample
if (lCurSample>32767)
  lCurSample=32767;
else if (lCurSample<-32768)
  lCurSample=-32768;

lIndex+=IndexAdjust[Code]; // adjust index

// clip index
if (lIndex<0)
  lIndex=0;
else if (lIndex>88)
  lIndex=88;

Output((SHORT)lCurSample); // send the sample to output

Code=LONIBBLE(Input); // get LOWER 4-bit nibble
// ...just the same as above for lower nibble

Note that HIGHER nibble is processed first for mono sound and corresponds to LEFT channel for stereo.

IMA ADPCM Tables

LONG IndexAdjust[]=
{
   -1,
   -1,
   -1,
   -1,
    2,
    4,
    6,
    8,
   -1,
   -1,
   -1,
   -1,
    2,
    4,
    6,
    8
};

LONG StepTable[]=
{
   7,	   8,	  9,	 10,	11,    12,     13,    14,    16,
   17,    19,	  21,	 23,	25,    28,     31,    34,    37,
   41,    45,	  50,	 55,	60,    66,     73,    80,    88,
   97,    107,   118,	 130,	143,   157,    173,   190,   209,
   230,   253,   279,	 307,	337,   371,    408,   449,   494,
   544,   598,   658,	 724,	796,   876,    963,   1060,  1166,
   1282,  1411,  1552,  1707,	1878,  2066,   2272,  2499,  2749,
   3024,  3327,  3660,  4026,	4428,  4871,   5358,  5894,  6484,
   7132,  7845,  8630,  9493,	10442, 11487,  12635, 13899, 15289,
   16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767
};

HNM Movie Soundtracks

There are several structural variants on the HNM6 format when in reference to its dependency on APC audio. The most common of them all is the .HNM A/V multiplex, which is not directly playable [externally] via the game's own DirectShow HNM core filter .dlls, since the APC audio - as a whole - is spanned across multiple blocks, and presents stuttering and agitated sound when decoded through directshow. This particular HNM6 variant occurs in Cryo games such as 'Egypt Kids' and 'Deo Gratias'.


The second most common variant is the 'stand-alone' HNM6 video format. Unlike the aforementioned A/V mux; this variant incorporates no APC within the HNM file itself but rather, it refers to external and individual APC files which, as a rule, have the same file title.


Rarely seen; there is another variant on the HNM6. Perhaps the most logical adaptation of Cryo's video format, it is a simple dub of APC over HNM. It does not span over different blocks, thus via directshow, it plays verbatim as it would in-game. This can be found in 'China: The Forbidden City' as .HNS files, as well as in 'UBIK' as .UBB files.

Whether or not UBIK's .UBB are in any/many way(s) similar to the .UBB video used in Megarace 2, is yet to be documented.

APC Audio Files in BF Archives

When stored in .BF resources, APC audio files are stored "as is", without compression or encryption. That means if you want to play/extract APC file from the BF resource you just need to search for (szID) id-string ("CRYO_APC") and read APC header starting at the beginning position of found id-string. This will give you starting point of the file and the size of the file will be the sum of APCHeader size and the compressed audio stream size correspondent to (dwOutSize) header field (that is, (dwOutSize) for stereo stream and (dwOutSize)/2 for mono stream).

ZIK Audio Files

In the game Chine music is in ZIK files. Most of them are simple RAW files: 16-bit signed stereo 22050 Hz (no header), except for only one which is a regular WAV file. So, you can just load and play those ZIKs as RAWs (except for one which is WAV).

PC Games Using CRYO APC