CNM: Difference between revisions
m (→Container format: small fix) |
(→Video compression: fix video description) |
||
Line 42: | Line 42: | ||
== Video compression == | == Video compression == | ||
Each frame is an independently compressed image (in bottoms-up format) split into | Each frame is an independently compressed image (in bottoms-up format) split into tiles. | ||
Frame header: | Frame header: | ||
4 bytes - payload size (not counting the header) | 4 bytes - payload size (not counting the header) | ||
4 bytes - offset to the colour data | 4 bytes - offset to the colour data | ||
2 bytes - number of tiles | 2 bytes - number of tiles | ||
2 bytes - tile size | 2 bytes - tile data size | ||
4 bytes - width | 4 bytes - width | ||
4 bytes - height | 4 bytes - height | ||
Line 54: | Line 54: | ||
3 bytes - unused? | 3 bytes - unused? | ||
Colour data | Colour data may contain either raw tile pixels (32-bit BGR0) or it may be packed. In that case tile data size is set to 4 or 2 and deltas stored right after it. Overall tile restoration algorithm is the following: | ||
copy 16 bytes (4x1 tile) from the stream | |||
for (tile = 1; tile < num_tiles; tile++) { | |||
tile_data[tile] = tile_data[tile - 1]; | |||
bits = get_bits(3) + 1; //the same bit reading as below, bits=8 should not happen | |||
for (i = 0; i < 16; i++) { | |||
delta = get_bits(bits); | |||
if (delta && get_bit()) | |||
delta = -delta; | |||
tile_data[tile][i] += delta; | |||
} | |||
} | |||
Tile control data is compressed using variable amount of bits, bits are stored MSB first. Tile index is read depending on the number of tiles: if it can fit into 10 bits then it's ten bits, if it can fit into 11 bits then it's 11 bits, otherwise it's 12 bits. | Tile control data is compressed using variable amount of bits, bits are stored MSB first. Tile index is read depending on the number of tiles: if it can fit into 10 bits then it's ten bits, if it can fit into 11 bits then it's 11 bits, otherwise it's 12 bits. | ||
Line 91: | Line 104: | ||
001111 - 0,-5 | 001111 - 0,-5 | ||
Actual image may be interlaced, i.e. only half of the lines are decoded. | |||
[[Category:Game Formats]] | [[Category:Game Formats]] |
Revision as of 10:31, 11 November 2022
- Company: Arxel Tribe
- Extension: cnm
- Samples: http://samples.mplayerhq.hu/game-formats/ring-cnm/
CNM is a multimedia format used in the computer game Ring: The Legend of the Nibelungen.
Container format
Container has the following structure:
- magic
CNM UNR\0
- header
- frame offsets table (video and audio interleaved, audio offsets are zero when audio is not present)
- frames
Header format (all values are little-endian):
4 bytes - number of frames 4 bytes - unknown 1 byte - unknown 4 bytes - image width 4 bytes - image height 2 bytes - unknown 1 byte - number of audio tracks 4 bytes - number of video frames? 4 bytes - number of frames repeated? 4 bytes - size of offsets table 152 bytes - always zero? when audio is present for each track: 1 byte - number of channels 1 bytes - bits per sample 4 bytes - audio rate 10 bytes - unused?
Each frame is prefixed by a byte containing its type. Known frame types:
- 0x41 - audio data
- 0x42 - audio data
- 0x53 - image
- 0x54 - some control marker
- 0x5A - audio data
Audio data is PCM prefixed by 32-bit data size, video frames are reviewed below.
Video compression
Each frame is an independently compressed image (in bottoms-up format) split into tiles. Frame header:
4 bytes - payload size (not counting the header) 4 bytes - offset to the colour data 2 bytes - number of tiles 2 bytes - tile data size 4 bytes - width 4 bytes - height 4 bytes - unknown 4 bytes - unknown 3 bytes - unused?
Colour data may contain either raw tile pixels (32-bit BGR0) or it may be packed. In that case tile data size is set to 4 or 2 and deltas stored right after it. Overall tile restoration algorithm is the following:
copy 16 bytes (4x1 tile) from the stream for (tile = 1; tile < num_tiles; tile++) { tile_data[tile] = tile_data[tile - 1]; bits = get_bits(3) + 1; //the same bit reading as below, bits=8 should not happen for (i = 0; i < 16; i++) { delta = get_bits(bits); if (delta && get_bit()) delta = -delta; tile_data[tile][i] += delta; } }
Tile control data is compressed using variable amount of bits, bits are stored MSB first. Tile index is read depending on the number of tiles: if it can fit into 10 bits then it's ten bits, if it can fit into 11 bits then it's 11 bits, otherwise it's 12 bits.
Single tile decoding flow:
if (getbit()) { offset = get_bits(tile_index_bits); copy tile data from the colour data using offset*16 } else { // copy existing tile decode motion vector, copy tile to which it points to (e.g. -1,0 means previous tile and 0,-1 means top tile) }
Motion vector codebook:
1 - 0,-1 0100 - -1, 0 0101 - -1,-1 0110 - 1,-1 0111 - 0,-2 000000 - -2,-3 000001 - 2,-3 000010 - -1,-4 000011 - 1,-4 000100 - -1,-2 000101 - 1,-2 000110 - 0,-3 000111 - 0,-4 001000 - -2, 0 001001 - -2,-1 001010 - 1,-1 001011 - -2,-2 001100 - 2,-2 001101 - -1,-3 001110 - 1,-3 001111 - 0,-5
Actual image may be interlaced, i.e. only half of the lines are decoded.