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