BMV
- Extensions: bmv
- Company: Teeny Weeny Games
- Samples: http://samples.mplayerhq.hu/game-formats/bmv/
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:
- copy data from the given offset of the frame
- copy pixel values from input
- 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
or audio_max_size - audio_blob_size
if flag 0x40
is set.
Video
As stated in ScummVM Tinsel engine source code [1]:
Encoded frame can contain 3 operations:
- delta - copy previously written N pixels, either from previous line, previous frame, or an offset within previous frame
- raw - copy N values from source stream
- run - repeat last pixel value N times
Encoding is convoluted and relies on nibbles - lo nibble = 4 bottom bits of a byte, hi nibble = 4 upper bits of a byte.
Operations keep rotating, after delta operation there can follow RAW or RUN operation, after RAW only RUN or DELTA and after RUN only DELTA or RAW can follow.
Which operation will follow depends on lowest bit of a current nibble.
Length of each operation can be encoded either directly or via special variable length encoding. From a nibble two bottom bits are preprended to final value, then next nibble is processed, if a new nibble is needed next byte is read from the stream. Process ends if any of two upper bits are set, in that case two upper bits are prepended as well.
Which length is going to be used depends on an quite convoluted encoding and there are two ways how they are encoded (note that operations still keep switching).
In outer loop, if:
- hi nibble has any of 2 upper bits set then only byte is read in this iteration and if:
- lo nibble has any of 2 upper bits set then length is between 7-30
- lo nibble has none of 2 upper bits set then there are 2 operations encoded and each nibble represents value 1-6
- hi nibble has none of 2 upper bits set then multiple bytes can be read in this iterations and if:
- lo nibble has any of 2 upper bits set then length is 1-6
- lo nibble has none of 2 upper bits set then length is using variable length encoding.
- if there hi nibble left then there is another operation with length 1-6
- start of other encoding - inner loop
In inner loop, if:
- lo nibble has any of 2 upper bits set then length is between 7-30
- lo nibble has none of 2 upper bits set then length is using variable length encoding
- hi nibble has any of 2 upper bits set then length is between 1-6 and inner loop ends
- hi nibble has none of 2 upper bits set then the rest used to select next operation and partly encode next length of next operation
Frame image data is always written from left to right, from top to bottom.
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,