CRH
- extension: .crh
- company: Kalisto Entertainment SA
CRH is an FMV format used at least in two games by Kalisto Entertainment. It features 15-bit RGB video with LZ-like compression and block correlation and DPCM-coded audio.
Container format
CRH files start with 16-bit little-endian version field (should be always set to one) followed by 48-byte audio header and 4-byte video header (16-bit number of frames and 16-bit frames per second which should always be 15).
Audio header:
4 bytes - sample rate 4 bytes - probably full audio stream size 2 bytes - unknown 2 bytes - unknown 2 bytes - unknown 2 bytes - number of channels 4 bytes - unknown 4 bytes - probably raw audio bytes per second 2 bytes - probably raw audio bits per sample 22 bytes - unknown
Frames consist of fixed-size audio part and variable-size video part (it starts with 32-bit size).
Audio part of frame is calculated as sample_rate * channels / fps + 2
.
Video coding
Video frames start with four 32-bit numbers: full frame size, component 1 size, component 2 size, component 3 size. Then the data for the components follows. Finally there's tile data prefixed with 32-bit size.
Video compression works by splitting frames into tiles, decorrelating tile components, interleaving them and coding each component separately using LZ-like coding.
Component plane reconstruction
Each component plane has fixed size (320x200 or 320x240 depending on the game) and uses the following bitstream (LSB first):
if !get_bit() { // RLE value = get_bits(5); length = get_bits(8); output value x length to dst } else { // LZ source_id = get_bit(); offset = get_bits(16 or 17); // depends on game length = get_bits(8); source = (!source_id ? dst : prev_plane) + offset; copy length bytes from source to dst }
Deinterleaving
For some reason (maybe in order to reconstruct downscaled video) data is stored in interleaved order, i.e. for each pair of lines component plane stored first line data at even positions and second line data at odd positions. Since the plane buffers may be used as the reference during the next plane decoding, this deinterleaving process is done during data read in tile reconstruction.
Tile reconstruction
After component plane reconstruction is done, it is split into tiles and reconstructed. Tile data is stored as bitstream LSB first and read bit-by-bit MSB first (e.g. eight-bit values in the tile header are stored bit-reversed).
tile_width = get_bits(8); // should be 20 tile_height = get_bits(8); // should also be 20 for each tile { tile_mode = get_bits(2); step1 = get_bits(4) * 4; step2 = get_bits(4) * 4; offset1 = get_bits(4) * 2; offset2 = get_bits(4) * 2; }
For each tile first two translation tables are generated using the corresponding offset
and step
values using the following formula:
for (i = 0; i < 32; i++) tab[i] = i * step / 32 + offset - 16;
And finally depending on the tile_mode
the RGB components are restored from c0
, c1
and c2
(components are listed in the order they're coded, clip5()
is a function that clips the output value to [0; 31]
range):
switch tile_mode { case 0: r = clip5(c0 + tab2[c2]); g = clip5(c1 + tab1[c2]); b = c2; break; case 1: r = clip5(c1 + tab1[c2]); g = c2; b = clip5(c0 + tab2[c2]); break; case 2: r = c2; g = clip5(c0 + tab2[c2]); b = clip5(c1 + tab1[c2]); break; } // mode 3 seems to be not handled
Audio coding
The format uses 16-bit audio with 8-bit deltas. In the beginning of the audio block there is full 16-bit sample (or two in case of stereo mode) followed by 8-bit signed deltas. If low bit of the delta is set, it should be multiplied by 512, otherwise just by 16 and added to the previous sample. In case of stereo mode deltas for different channels are interleaved.
Games using this format
- Dark Earth (320x200 and 16-bit offsets)
- Nightmare Creatures (320x240 and 17-bit offsets)