Escape 124
- Company: Eidos Technologies
- Samples: http://samples.mplayerhq.hu/game-formats/rpl/escape124/
ESCAPE 124 is a slightly enhanced version of Eidos Technologies' previous Escape 122 codec. It is usually found encapsulated in ARMovie files bearing the extension RPL. The most important difference is native 16-bits (15-bits, actually) color support. While general principles remain the same, a few new techniques have been added. For example, instead of coding each color triplet, they all are stored in several lookup tables (think about VQ or huge palette).
Each frame chunks begins with a new 8-bytes header:
dword frame_flags - frame flags, must be passed to the decoder dword frame_size - size of the frame chunk, including this header
In the available sample files, frames are usually grouped together in one-second bundles followed by sound stream (i.e., 15 fps movie will have 15 frames in a row, then 1 sec of sound data.) Original decoder throws away any frames whose frame_flags has no bits 2, 4 or 8 set ( & 0x114 ).
Video portion
At first, you need to check if any of the bits 23..26 of frame_flags is set. If none of them is set, just leave the last decoded frame unchanged. (These bits are specific to 124 decoder, unlike bits 2, 4, 8, described above).
Next, unpack 3 codebooks. Each codebook contains a variable number of 2x2 macroblocks. You should maintain all three codebooks between frames.
if bit 17 of frame_flags is set read 4 bits of codebook 1 index depth from the stream codebook 1 size is 2^depth (power of) codebook1 = Unpack_Codebook() if bit 18 of frame_flags is set read 4 bits of codebook 2 index depth from the stream codebook 2 size is 2^depth (power of) * total number of superblocks in the frame codebook2 = Unpack_Codebook() if bit 19 of frame_flags is set read 20 bits of codebook 3 size from the stream evaluate codebook 3 index depth as the 1-based index of most significant bit of (size - 1) codebook3 = Unpack_Codebook()
Proceed the frame decoding:
initialize codebook_index to zero repeat skip_value = RICE_Decode() skip skip_value number of 8x8 superblocks (keep them unchanged from the last frame) if all blocks drawn/skipped finish multi_mask = 0 while next bit of the stream is zero new_block = Decode_MacroBlock() mask = read 4*4 (number of macroblocks in superblock) bits multi_mask = multi_mask or'ed with mask for each macroblock of superblock (left to right, top to bottom) if corresponding bit of mask (see below) is set macroblock = new_block if next bit of the stream is zero inv_mask = read 4 bits from the stream for each bit of inv_mask (from low to high) if bit is set invert next 4 bits of multi_mask else XOR next 4 bits of multi_mask by next 4 bits of the stream for each macroblock of superblock (left to right, top to bottom) if corresponding bit of multi_mask (see below) is set macroblock = Decode_MacroBlock() else if bit 16 of frame_flags is set while next bit of the stream is zero new_block = Decode_MacroBlock() block_index = read next 4 bits from the stream set macroblock block_index of superblock to new_block advance to the next superblock
Superblock's macroblock bit mask matrix (in hex):
---- ---- ---- ---- | 1| 2| 10| 20| ---- ---- ---- ---- | 4| 8| 40| 80| ---- ---- ---- ---- | 100| 200|1000|2000| ---- ---- ---- ---- | 400| 800|4000|8000| ---- ---- ---- ----
Decode_Macroblock selects 2x2 macroblock from one of codebooks:
if next bit of stream is set if next bit of stream is set decrement codebook_index else increment codebook_index wrap codebook_index within 0..2 range if codebook_index is 0 macroblock = codebook2[superblock_index * 2^depth + next codebook 2 depth bits of the stream] else if codebook_index is 1 macroblock = codebook1[next codebook 1 depth bits of the stream] else macroblock = codebook3[next codebook 3 depth bits of the stream]
Unpack_Codebook loads macroblocks codebook:
for each macroblock of codebook read 2*2 (number of pixels in macroblock) mask bits from the stream read 5+5+5 bits of color0 RGB triplet read 5+5+5 bits of color1 RGB triplet for each pixel of macroblock (left to right, top to bottom) if next bit of mask (from low to high) is set pixel = color1 else pixel = color0