Xilam DERF

From MultimediaWiki
Jump to navigation Jump to search
  • Extensions: adp, vdo, vds
  • Company: Xilam Animation
  • Samples:

DERF is a name given to both video and audio format in at least one game by Xilam Animation since they both start with that magic word. Audio is DPCM-based and video files employ 8x8 DCT blocks for image coding and the same DPCM audio coding.

All values in the files are little-endian.

Audio format

File header:

 bytes 0-3  "DERF"
 bytes 4-7  number of channels (1 or 2)
 bytes 8-11 data size

Sampling rate is always 22050 Hz.

Audio is DPCM-coded with one byte for delta, stereo audio has deltas interleaved. Top bit means delta sign, low 7 bits are index in the delta table. Delta is added to the previous 16-bit sample value with overflow.

Delta table:

    0x0,    0x1,    0x2,    0x3,    0x4,    0x5,    0x6,    0x7,
    0x8,    0x9,    0xA,    0xB,    0xC,    0xD,    0xE,   0x10,
   0x11,   0x13,   0x15,   0x17,   0x19,   0x1C,   0x1F,   0x22,
   0x25,   0x29,   0x2D,   0x32,   0x37,   0x3C,   0x42,   0x49,
   0x50,   0x58,   0x61,   0x6B,   0x76,   0x82,   0x8F,   0x9D,
   0xAD,   0xBE,   0xD1,   0xE6,   0xFD,  0x117,  0x133,  0x151,
  0x173,  0x198,  0x1C1,  0x1EE,  0x220,  0x256,  0x292,  0x2D4,
  0x31C,  0x36C,  0x3C3,  0x424,  0x48E,  0x502,  0x583,  0x610,
  0x6AB,  0x756,  0x812,  0x8E0,  0x9C3,  0xABD,  0xBD0,  0xCFF,
  0xE4C,  0xFBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, 0x1BDC,
 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, 0x3BB9,
 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF

Video format

Video files also start with DERF magic but they can be distinguished from audio files by the fact that the next field is total data size which should be greater than maximum channel count stored for audio files there.

File structure

File header:

 bytes 0-3   "DERF"
 bytes 4-7   data size
 bytes 8-9   number of chunks in the file
 bytes 10-11 width
 bytes 12-13 height
 bytes 14-35 unknown/unused

After the file header a specified number of chunks follows.

Chunk header:

 bytes 0-1  chunk type
 bytes 2-3  unknown
 bytes 4-7  chunk size including header
 bytes 8-15 unknown

Known chunk types:

  • FK - keyframe chunk
  • KB - interframe chunk
  • CM - mono audio data (DPCM-encoded in the same way as audio file)
  • CS - stereo audio data

Video frame encoding

Video frames are coded as 8x8 blocks that may be split further down to 2x2 blocks for motion compensation or use DCT for coding block contents.

Frame structure:

 bytes 0-3  block flags data offset
 bytes 4-7  coefficients data offset
 bytes 8-.. motion vector and extended coefficients data
 [block flags data]
 [coefficients data]

Block flags tell how to split block for motion compensation. They are grouped into 32-bit words MSB.

Coefficients data tell how to decode DCT coefficients and code small coefficient values. They are coded as nibbles packed into 32-bit words with low nibble first.

Block decoding proces:

 void decode_block(int x, int y, int w, int h) {
   if ((w == 2 && h == 2) || get_flag()) {
     decode motion data or DCT
   } else if ((h == w * 2) || ((w == h) && get_flag())) {
     decode_block(x, y,         w, h / 2);
     decode_block(x, y + h / 2, w, h / 2);
   } else {
     decode_block(x,         y, w / 2, h);
     decode_block(x + w / 2, y, w / 2, h);
   }
 }

Motion data

Motion data is stored as 16-bit word with the following bit fields:

 bits  13-15  motion mode
 bits  7-12   motion vector y component plus 31
 bits  1-6    motion vector x component plus 31
 bit   0      reference source (0 = previous frame, 1 = current frame)

Motion modes:

  • 0 - copy block data
  • 1 - copy block data flipped upside-down
  • 2 - copy block data flipped right-left
  • 3 - copy block data flipped both upside-down and right-left
  • 4 - copy block data transposed
  • 5 - copy block data flipped around antidiagonal
  • 6 - copy block data transposed and flipped
  • 7 - 8x8 block is DCT-coded

DCT block data

DCT-coded block has three 8x8 blocks with their coefficients coded interleaved and in zig-zag ordering (i.e. y0 u0 v0 y1 u1 v1 y8 u8 v8 ...) using the following scheme (which is essentially "group zero/non-zero coefficients together and use nibbles to signal which to read and using which data size"):

 dst_end = dst + 64 * 3;
 flags = get_nibble(); // nibbles come from coefficients data
 for (i = 0; i < 4; i++) {
   if (flags & (1 << idx))
     *dst++ = get_byte_signed(); // comes from MV and ext coeffs data
   else
     *dst++ = get_16bit_signed(); // comes from MV and ext coeffs data
 }
 while (dst < dst_end) {
   mode = get_nibble(); // nibbles come from coefficients data
   switch (mode) {
   case 0: *dst++ = get_byte_signed(); break; // comes from MV and ext coeffs data
   case 1: *dst++ = get_nibble_signed(); break;
   case 2:
     while ((run = get_byte()) != 0) {
       while (run--)
         *dst++ = 0;
       *dst++ = get_byte_signed();
     }
     break;
   case 3:
     while ((run = get_byte()) != 0) {
       while (run--)
         *dst++ = 0;
       *dst++ = get_nibble_signed();
     }
     break;
   case 4:
     while ((run = get_nibble()) != 0) {
       while (run--)
         *dst++ = 0;
       *dst++ = get_byte_signed();
     }
     break;
   case 5:
     while ((run = get_nibble()) != 0) {
       while (run--)
         *dst++ = 0;
       *dst++ = get_nibble_signed();
     }
     break;
   case 6:
     len = get_nibble();
     for (i = 0; i < (len >> 2) + 1; i++)
       *dst++ = 0;
     for (i = 0; i < (len & 3) + 1; i++)
       *dst++ = get_nibble_signed();
     break;
   case 7:
     len = get_nibble();
     for (i = 0; i < (len >> 1) + 1; i++)
       *dst++ = 0;
     for (i = 0; i < (len & 1) + 1; i++)
       *dst++ = get_nibble_signed();
     break;
   case 8:
     len = get_nibble();
     for (i = 0; i < (len >> 3) + 1; i++)
       *dst++ = 0;
     for (i = 0; i < (len & 7) + 1; i++)
       *dst++ = get_nibble_signed();
     break;
   case 9:
     len = get_nibble();
     for (i = 0; i < len; i++)
       *dst++ = get_nibble_signed();
     break;
   case 10:
     while ((len = get_nibble()) != 0) {
       for (i = 0; i < len; i++)
         *dst++ = 0;
       *dst++ = get_16bit_signed();
     }
     break;
   case 11:
     while ((len = get_byte()) != 0) {
       for (i = 0; i < len; i++)
         *dst++ = 0;
       *dst++ = get_16bit_signed();
     }
     break;
   case 12: *dst++ = get_16bit_signed(); break;
   case 13-15: not present
   }
 }

Block is reconstructed by applying default IJG dequantisation, IDCT and YUV to RGB conversion.

Games using DERF formats

Stupid Invaders