BMV

From MultimediaWiki
Jump to: navigation, search

BMV is the file extension for FMV used in Discworld 2 and Discworld 3 games, actual format differs for them.

Discworld 2 BMV

Container format

File has no header and consists of series of packets. Each packet starts with the byte showing its type and except NOP and END packets all other packets have 24-bit little-endian length field.

Packet type meaning:

 0x00    NOP
 0x01    END
 0x02    Inter frame
 0x03    Intra frame
 
Frame types can additionally have a set of flags:
 0x04   packet contains scroll offset
 0x08   palette is included
 0x10   commands (e.g. for subtitles)
 0x20   audio is present
 0x40   extended mode
 0x80   text is present (for command)

Data is stored in this order:

 audio (3675 bytes for old audio block, 1 + blobs*65 for newer one)
 commands
 palette (768 bytes)
 screen offset (2 bytes)
 picture data

Since text string are loaded from game resources, it is not described here.

Video

Video packing is rather exotic: frame data may be decoded in forward or backward direction. In the latter case input data is read from the end to beginning and frame updated the same way (right to left, bottom to top).

Video decoder cycles between three states corresponding to the decoding modes:

  1. copy data from the given offset of the frame
  2. copy pixel values from input
  3. fill some segment with the left (or right for backwards decoding) neighbour value

The length of segment is defined by code which is read before performing operation. The lowest bit of that code specifies whether the next mode should be skipped or not.

Length code is coded in unusual way too: each nibble provide next bits of the code. If any of two upper bits of the nibble are set that means that code decoding is complete, otherwise only two low bits of the nibble are prepended to the code. Code nibbles are packed into bytes, so one byte can actually contain codes for current and next operation.

Audio

Audio is decoded per blobs. Each blob contains a byte with multipliers and actual data. Two multipliers are stored in a single byte which is cyclically rotated left by one

 mult_table[16] = { 16512, 8256, 4128, 2064, 1032, 516, 258, 192, 129, 88, 64, 56, 48, 40, 36, 32 }
 t = get_byte();
 t = (t >> 1) | ((t & 0x1) << 7);
 mul1 = mult_table[t & 0xF];
 mul2 = mult_table[t >> 4];

Audio is decoded by fetching byte, multiplying it by mul1 or mul2 (even samples - by mul1, odd samples - by mul2) and dividing by 32:

 int8_t *src;
 for(i = 0; i < 64; i += 2){
   outsamples[i  ] = (*src++ * mul1) >> 5;
   outsamples[i+1] = (*src++ * mul2) >> 5;
   blobsize--;
 }

Discworld 3 BMV

Container

Container consists of two chunks - header and data. Each chunk starts with magic ('BMVi' or 'DATA') and payload size.


Header data (little-endian):

 bytes  0- 3   'BMVi'
 bytes  4- 7   payload size
 bytes  8-11   slot size
 bytes 12-15   number of frames
 bytes 16-17   prefetch slots
 bytes 18-19   some cache size
 bytes 20-21   frames per 256 seconds
 bytes 22-23   audio max size
 bytes 24-25   audio blob size (41 for new audio coding, otherwise old scheme is used)
 byte  26      audio ID
 byte  27      video ID
 bytes 28-29   frame width
 bytes 30-31   frame height

Frames stored in data chunk seems to have the same structure as v2, except that palette must not be present and audio is stored in fixed size per chunk audio_max_size<code> or <code>audio_max_size - audio_blob_size if flag 0x40 is set.

Audio

New audio coding stores 64 samples in 41 bytes. Packets for left and right channel are interleaved, coding mode is the last byte in a packet for the left channel and the first one for the right channel. Compression is done by using delta coding and quantising delta values into 5 bits. Mode byte consists of two nibbles selecting which subpart of the table should be used (high nibble for the first 32 samples). Samples are packed into 16-bit words, top bits form codeword for two last values (MSB), the rest is divided into three 5-bit groups.

   for (i = 0; i < 10; i++) {
       code = get_16bits();
       last_cw = (last_cw << 1) | (code >> 15);
       pred += quant_table[mode][(code >> 10) & 0x1F];
       *dst++ = pred;
       pred += quant_table[mode][(code >>  5) & 0x1F];
       *dst++ = pred;
       pred += quant_table[mode][ code        & 0x1F];
       *dst++ = pred;
   }
   pred += quant_table[mode][(last_cw >> 5) & 0x1F];
   *dst++ = pred;
   pred += quant_table[mode][ last_cw       & 0x1F];
   *dst++ = pred;


Quantisation table

   // mode 0x0
   0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00,
   0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
   0xC000, 0xC400, 0xC800, 0xCC00, 0xD000, 0xD400, 0xD800, 0xDC00,
   0xE000, 0xE400, 0xE800, 0xEC00, 0xF000, 0xF400, 0xF800, 0xFC00,
 
   // mode 0x1
   0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0A00, 0x0C00, 0x0E00,
   0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1A00, 0x1C00, 0x1E00,
   0xE000, 0xE200, 0xE400, 0xE600, 0xE800, 0xEA00, 0xEC00, 0xEE00,
   0xF000, 0xF200, 0xF400, 0xF600, 0xF800, 0xFA00, 0xFC00, 0xFE00,
 
   // mode 0x2
   0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700,
   0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, 0x0F00,
   0xF000, 0xF100, 0xF200, 0xF300, 0xF400, 0xF500, 0xF600, 0xF700,
   0xF800, 0xF900, 0xFA00, 0xFB00, 0xFC00, 0xFD00, 0xFE00, 0xFF00,
 
   // mode 0x3
   0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380,
   0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
   0xF800, 0xF880, 0xF900, 0xF980, 0xFA00, 0xFA80, 0xFB00, 0xFB80,
   0xFC00, 0xFC80, 0xFD00, 0xFD80, 0xFE00, 0xFE80, 0xFF00, 0xFF80,
 
   // mode 0x4
   0x0000, 0x0048, 0x0090, 0x00D8, 0x0120, 0x0168, 0x01B0, 0x01F8,
   0x0240, 0x0288, 0x02D0, 0x0318, 0x0360, 0x03A8, 0x03F0, 0x0438,
   0xFB80, 0xFBC8, 0xFC10, 0xFC58, 0xFCA0, 0xFCE8, 0xFD30, 0xFD78,
   0xFDC0, 0xFE08, 0xFE50, 0xFE98, 0xFEE0, 0xFF28, 0xFF70, 0xFFB8,
 
   // mode 0x5
   0x0000, 0x0030, 0x0060, 0x0090, 0x00C0, 0x00F0, 0x0120, 0x0150,
   0x0180, 0x01B0, 0x01E0, 0x0210, 0x0240, 0x0270, 0x02A0, 0x02D0,
   0xFD00, 0xFD30, 0xFD60, 0xFD90, 0xFDC0, 0xFDF0, 0xFE20, 0xFE50,
   0xFE80, 0xFEB0, 0xFEE0, 0xFF10, 0xFF40, 0xFF70, 0xFFA0, 0xFFD0,
 
   // mode 0x6
   0x0000, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0, 0x00C0, 0x00E0,
   0x0100, 0x0120, 0x0140, 0x0160, 0x0180, 0x01A0, 0x01C0, 0x01E0,
   0xFE00, 0xFE20, 0xFE40, 0xFE60, 0xFE80, 0xFEA0, 0xFEC0, 0xFEE0,
   0xFF00, 0xFF20, 0xFF40, 0xFF60, 0xFF80, 0xFFA0, 0xFFC0, 0xFFE0,
 
   // mode 0x7
   0x0000, 0x0016, 0x002C, 0x0042, 0x0058, 0x006E, 0x0084, 0x009A,
   0x00B0, 0x00C6, 0x00DC, 0x00F2, 0x0108, 0x011E, 0x0134, 0x014A,
   0xFEA0, 0xFEB6, 0xFECC, 0xFEE2, 0xFEF8, 0xFF0E, 0xFF24, 0xFF3A,
   0xFF50, 0xFF66, 0xFF7C, 0xFF92, 0xFFA8, 0xFFBE, 0xFFD4, 0xFFEA,
 
   // mode 0x8
   0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070,
   0x0080, 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0,
   0xFF00, 0xFF10, 0xFF20, 0xFF30, 0xFF40, 0xFF50, 0xFF60, 0xFF70,
   0xFF80, 0xFF90, 0xFFA0, 0xFFB0, 0xFFC0, 0xFFD0, 0xFFE0, 0xFFF0,
 
   // mode 0x9
   0x0000, 0x000B, 0x0016, 0x0021, 0x002C, 0x0037, 0x0042, 0x004D,
   0x0058, 0x0063, 0x006E, 0x0079, 0x0084, 0x008F, 0x009A, 0x00A5,
   0xFF50, 0xFF5B, 0xFF66, 0xFF71, 0xFF7C, 0xFF87, 0xFF92, 0xFF9D,
   0xFFA8, 0xFFB3, 0xFFBE, 0xFFC9, 0xFFD4, 0xFFDF, 0xFFEA, 0xFFF5,
 
   // mode 0xA
   0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038,
   0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078,
   0xFF80, 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8,
   0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8,
 
   // mode 0xB
   0x0000, 0x0006, 0x000C, 0x0012, 0x0018, 0x001E, 0x0024, 0x002A,
   0x0030, 0x0036, 0x003C, 0x0042, 0x0048, 0x004E, 0x0054, 0x005A,
   0xFFA0, 0xFFA6, 0xFFAC, 0xFFB2, 0xFFB8, 0xFFBE, 0xFFC4, 0xFFCA,
   0xFFD0, 0xFFD6, 0xFFDC, 0xFFE2, 0xFFE8, 0xFFEE, 0xFFF4, 0xFFFA,
 
   // mode 0xC
   0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C,
   0x0020, 0x0024, 0x0028, 0x002C, 0x0030, 0x0034, 0x0038, 0x003C,
   0xFFC0, 0xFFC4, 0xFFC8, 0xFFCC, 0xFFD0, 0xFFD4, 0xFFD8, 0xFFDC,
   0xFFE0, 0xFFE4, 0xFFE8, 0xFFEC, 0xFFF0, 0xFFF4, 0xFFF8, 0xFFFC,
 
   // mode 0xD
   0x0000, 0x0002, 0x0005, 0x0008, 0x000B, 0x000D, 0x0010, 0x0013,
   0x0016, 0x0018, 0x001B, 0x001E, 0x0021, 0x0023, 0x0026, 0x0029,
   0xFFD4, 0xFFD6, 0xFFD9, 0xFFDC, 0xFFDF, 0xFFE1, 0xFFE4, 0xFFE7,
   0xFFEA, 0xFFEC, 0xFFEF, 0xFFF2, 0xFFF5, 0xFFF7, 0xFFFA, 0xFFFD,
 
   // mode 0xE
   0x0000, 0x0001, 0x0003, 0x0005, 0x0007, 0x0008, 0x000A, 0x000C,
   0x000E, 0x000F, 0x0011, 0x0013, 0x0015, 0x0016, 0x0018, 0x001A,
   0xFFE4, 0xFFE5, 0xFFE7, 0xFFE9, 0xFFEB, 0xFFEC, 0xFFEE, 0xFFF0,
   0xFFF2, 0xFFF3, 0xFFF5, 0xFFF7, 0xFFF9, 0xFFFA, 0xFFFC, 0xFFFE,
 
   // mode 0xF
   0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
   0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
   0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
   0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,