* EIDOS ESCAPE Codec 130 This document describes the binary format of the video frames decodeable with the dec130.dll supplied by EIDOS. ** Presentation In This Document Lsb is the least significant bit, msb the most significant bit. Numbers are represented as in C-code Ex: 0x12ab, 104 Bit streams are represented with the lsb to the right, as used for writing numbers. Ex: 0011001 If there may be more bits with higher significance in the stream, "..." is prepended. Ex: ...0011001 There can be placeholders in bit streams, denoted by a single character per bit. Ex: ...00aaab1 Multi byte data types are construced from "", with being either "LE" for little endian or "BE" for big endian and being the number of bits used. Ex: LE16 (little endian 16bit/2 byte) ** General Overview Each frame is split into 2x2 pixel blocks, line by line from top to bottom and block by block from left to right. A block can be encoded as a single color with small fixed variance in the 4 pixels, as a single color with per pixel adjustable brightness, or not at all, thus reusing the pixels from the previous frame. Colors are encoded in a format similar to YCbCr, basing on the same YPbPr colorspace. The color information can be given in absolute values, or as a difference to the previous block in the image(which may be of an older frame). The brightness offset per pixel is always given in absolute values. ** Color Space The usual bit depth used in this codec is 6 bit for Y' and 5 bit each for Pb' and Pr'. The lsb of Y' is omitted in some blockcodes. RGB values are mapped to YPbPr values as follows: R', G', B' in 0.0..1.0 Y in 0.0..1.0 Pb, Pr in -0.5..0.5 Y = Kr * R' + (1 - Kr - Kb) * G' + Kb * B' Pb = 0.5 * (B' - Y) / (1 - Kb) Pr = 0.5 * (R' - Y) / (1 - Kr) As in YCbCr, Kb = 0.114 and Kr = 0.299 are used. For mapping the decimal y, pb and pr to the integer Y, Pb and Pr, a linear mapping can be used: Y' = Y * 64 Pb' = Pb * 32 + 16 Pr' = Pr * 32 + 16 Another option is a non-linear mapping for Pb and Pr, using this table: +----------+------------+ ! Pb', Pr' ! Pb, Pr ! +----------+------------+ ! 0 ! -0.421875 ! ! 1 ! -0.390625 ! ! 2 ! -0.359375 ! ! 3 ! -0.328125 ! ! 4 ! -0.296875 ! ! 5 ! -0.265625 ! ! 6 ! -0.234375 ! ! 7 ! -0.203125 ! ! 8 ! -0.171875 ! ! 9 ! -0.140625 ! ! 10 ! -0.109375 ! ! 11 ! -0.0859375 ! ! 12 ! -0.0625 ! ! 13 ! -0.046875 ! ! 14 ! -0.03125 ! ! 15 ! -0.015625 ! ! 16 ! 0.0 ! ! 17 ! 0.015625 ! ! 18 ! 0.03125 ! ! 19 ! 0.046875 ! ! 20 ! 0.0625 ! ! 21 ! 0.0859375 ! ! 22 ! 0.109375 ! ! 23 ! 0.140625 ! ! 24 ! 0.171875 ! ! 25 ! 0.203125 ! ! 26 ! 0.234375 ! ! 27 ! 0.265625 ! ! 28 ! 0.296875 ! ! 29 ! 0.328125 ! ! 30 ! 0.359375 ! ! 31 ! 0.390625 ! +----------+------------+ ** Single Color Block A single color block uses the color described by YPbPr after conversion to RGB, then adds a small value to each component in each pixel. The mapping is visible in the next figure: +----------------+----------------+ ! r' = r + 0/128 ! r' = r + 3/128 ! ! g' = g + 3/128 ! g' = g + 0/128 ! ! b' = b + 0/128 ! b' = b + 3/128 ! +----------------+----------------+ ! r' = r + 1/128 ! r' = r + 2/128 ! ! g' = g + 2/128 ! g' = g + 1/128 ! ! b' = b + 1/128 ! b' = b + 2/128 ! +----------------+----------------+ ** Four Brightness Block A four brightness block uses the color described by YPbPr, a table containing offsets to the Y part, a 6 bit number S to select if the offset should be applied, and if so, the sign of the offset, and a 2 bit number D that selects the amount of difference. To get the four sign factors LT(left top), RT(right top), LB(left bottom), RB(right bottom) from the 6 bit S, the following table is used: +--------+------+------+------+------+ ! S ! LT ! RT ! LB ! RB ! +--------+------+------+------+------+ ! 000000 ! 0.0 ! 0.0 ! 0.0 ! 0.0 ! ! 000001 ! -1.0 ! 1.0 ! 0.0 ! 0.0 ! ! 000010 ! 1.0 ! -1.0 ! 0.0 ! 0.0 ! ! 000011 ! -1.0 ! 0.0 ! 1.0 ! 0.0 ! ! 000100 ! -1.0 ! 1.0 ! 1.0 ! 0.0 ! ! 000101 ! 0.0 ! -1.0 ! 1.0 ! 0.0 ! ! 000110 ! 1.0 ! -1.0 ! 1.0 ! 0.0 ! ! 000111 ! -1.0 ! -1.0 ! 1.0 ! 0.0 ! ! 001000 ! 1.0 ! 0.0 ! -1.0 ! 0.0 ! ! 001001 ! 0.0 ! 1.0 ! -1.0 ! 0.0 ! ! 001010 ! 1.0 ! 1.0 ! -1.0 ! 0.0 ! ! 001011 ! -1.0 ! 1.0 ! -1.0 ! 0.0 ! ! 001100 ! 1.0 ! -1.0 ! -1.0 ! 0.0 ! ! 001101 ! -1.0 ! 0.0 ! 0.0 ! 1.0 ! ! 001110 ! -1.0 ! 1.0 ! 0.0 ! 1.0 ! ! 001111 ! 0.0 ! -1.0 ! 0.0 ! 1.0 ! +--------+------+------+------+------+ ! 010000 ! 0.0 ! 0.0 ! 0.0 ! 0.0 ! ! 010001 ! 1.0 ! -1.0 ! 0.0 ! 1.0 ! ! 010010 ! -1.0 ! -1.0 ! 0.0 ! 1.0 ! ! 010011 ! -1.0 ! 0.0 ! 1.0 ! 1.0 ! ! 010100 ! -1.0 ! 1.0 ! 1.0 ! 1.0 ! ! 010101 ! 0.0 ! -1.0 ! 1.0 ! 1.0 ! ! 010110 ! 1.0 ! -1.0 ! 1.0 ! 1.0 ! ! 010111 ! -1.0 ! -1.0 ! 1.0 ! 1.0 ! ! 011000 ! 0.0 ! 0.0 ! -1.0 ! 1.0 ! ! 011001 ! 1.0 ! 0.0 ! -1.0 ! 1.0 ! ! 011010 ! -1.0 ! 0.0 ! -1.0 ! 1.0 ! ! 011011 ! 0.0 ! 1.0 ! -1.0 ! 1.0 ! ! 011100 ! 1.0 ! 1.0 ! -1.0 ! 1.0 ! ! 011101 ! -1.0 ! 1.0 ! -1.0 ! 1.0 ! ! 011110 ! 0.0 ! -1.0 ! -1.0 ! 1.0 ! ! 011111 ! 1.0 ! -1.0 ! -1.0 ! 1.0 ! +--------+------+------+------+------+ ! 100000 ! 0.0 ! 0.0 ! 0.0 ! 0.0 ! ! 100001 ! -1.0 ! -1.0 ! -1.0 ! 1.0 ! ! 100010 ! 1.0 ! 0.0 ! 0.0 ! -1.0 ! ! 100011 ! 0.0 ! 1.0 ! 0.0 ! -1.0 ! ! 100100 ! 1.0 ! 1.0 ! 0.0 ! -1.0 ! ! 100101 ! -1.0 ! 1.0 ! 0.0 ! -1.0 ! ! 100110 ! 1.0 ! -1.0 ! 0.0 ! -1.0 ! ! 100111 ! 0.0 ! 0.0 ! 1.0 ! -1.0 ! ! 101000 ! 1.0 ! 0.0 ! 1.0 ! -1.0 ! ! 101001 ! -1.0 ! 0.0 ! 1.0 ! -1.0 ! ! 101010 ! 0.0 ! 1.0 ! 1.0 ! -1.0 ! ! 101011 ! 1.0 ! 1.0 ! 1.0 ! -1.0 ! ! 101100 ! -1.0 ! 1.0 ! 1.0 ! -1.0 ! ! 101101 ! 0.0 ! -1.0 ! 1.0 ! -1.0 ! ! 101110 ! 1.0 ! -1.0 ! 1.0 ! -1.0 ! ! 101111 ! -1.0 ! -1.0 ! 1.0 ! -1.0 ! +--------+------+------+------+------+ ! 110000 ! 0.0 ! 0.0 ! 0.0 ! 0.0 ! ! 110001 ! 1.0 ! 0.0 ! -1.0 ! -1.0 ! ! 110010 ! 0.0 ! 1.0 ! -1.0 ! -1.0 ! ! 110011 ! 1.0 ! 1.0 ! -1.0 ! -1.0 ! ! 110100 ! -1.0 ! 1.0 ! -1.0 ! -1.0 ! ! 110101 ! 1.0 ! -1.0 ! -1.0 ! -1.0 ! +--------+------+------+------+------+ The actual offset can be obtained from the next table: +----+----------+ ! D ! Y offset ! +----+----------+ ! 00 ! 0.03125 ! ! 01 ! 0.0625 ! ! 10 ! 0.15625 ! ! 11 ! 0.3125 ! +----+----------+ This way, each block can have two distinct extreme brightnesses, as well as the middle brightness. ** Y/Pb/Pr Adjustments The block codes may adjust the Y, Pb and Pr components, using 3 bit values. Y adjustment can be determined from the following table: +------+-----------+ ! Bits ! Y Offset ! +------+-----------+ ! 000 ! -0.0625 ! ! 001 ! -0.046875 ! ! 010 ! -0.03125 ! ! 011 ! -0.015625 ! ! 100 ! 0.015625 ! ! 101 ! 0.03125 ! ! 110 ! 0.046875 ! ! 111 ! 0.0625 ! +------+-----------+ Pb and Pr are always adjusted together: +------+-----------+-----------+ ! Bits ! Pb Offset ! Pr Offset ! +------+-----------+-----------+ ! 000 ! 0.03125 ! 0.0 ! ! 001 ! 0.03125 ! 0.03125 ! ! 010 ! 0.0 ! 0.03125 ! ! 011 ! -0.03125 ! 0.0 ! ! 100 ! -0.03125 ! -0.03125 ! ! 101 ! -0.03125 ! -0.0625 ! ! 110 ! 0.0 ! -0.03125 ! ! 111 ! 0.03125 ! -0.03125 ! +------+-----------+-----------+ ** Persistent Storage The YPbPr information of each block in the frame must be preserved over frame boundaries. They are initialized to 0, resp. to -0.5 before decoding the first frame. ** Binary Representation The binary representation consists of a header and a bit stream, which in turn consists of alternating block skips and block codes. The header is shown in the next figure: +---------+----------------------------------------------------------+ ! Type ! Meaning ! +---------+----------------------------------------------------------+ ! LE16 ! Codec magic(0x0130) ! ! LE16 ! Unknown(0x8001 every about 15th frame, 0x0001 otherwise) ! ! LE32 ! Size of this bit stream, including header ! ! BYTE[8] ! Zeroed ! +---------+----------------------------------------------------------+ The bits in the bitstream are extracted from the bytestream lsb first. Multi bit numbers are lsb first. ** Block Skips The number of Blocks skipped before each block code is encoded in a variable length format: +--------------------------------+---------+------------+ ! Bit Pattern ! Skipped ! Available ! ! ! Blocks ! Range ! +--------------------------------+---------+------------+ ! ...1 ! 1 ! 1 ! ! ...vvv0 ! v + 1 ! 2..8 ! ! ...vvvvvvvv0000 ! v + 8 ! 9..263 ! ! ...vvvvvvvvvvvvvvv000000000000 ! v + 263 ! 264..33030 ! +--------------------------------+---------+------------+ The v must be non-zero. The first block skip in a frame is decremented by 1 to be able to address the first block in a frame. The dissected implementation retries the last two options until it either finds a non-zero v, or a zero v from a single 32 bit word in the last option. In the latter case, the block skip is 1. I guess this behaviour is an attempt to recover gracefully from a bad stream, but the format is not really suitable to recover from any bad bits. There may be better reactions to this problem. ** Block Codes There are two groups of block codes: single color codes and four brightness(Y) codes. A block code my refer to the previous block, which is the block directly before the current block The block pointer is not automatically incremented after interpreting the block code. This happens using block skips. The following table gives an overview over all block codes: +-------------------------------+----------------------+------+ ! Bit Pattern ! Short name used ! Four ! ! ! in this document ! Y ! +-------------------------------+----------------------+------+ ! ...000 ! Reuse previous block ! No ! ! ...aaa0100 ! Adjust Pb/Pr ! No ! ! ...bbbbbaaaaa1100 ! Set Pb/Pr ! No ! ! ...0aaa010 ! Adjust Y ! No ! ! ...bbb01aaa010 ! Adjust Y/Pb/Pr ! No ! ! ...cccccbbbbb11aaa010 ! Adjust Y, Set Pb/Pr ! No ! ! ...0aaaaaa110 ! Set Y ! No ! ! ...bbb01aaaaaa110 ! Set Y, Adjust Pb/Pr ! No ! ! ...cccccbbbbb11aaaaaa110 ! Set Y/Pb/Pr ! No ! +-------------------------------+----------------------+------+ ! ...0cccccbbaaaaaa1 ! Set Y ! Yes ! ! ...ddd01cccccbbaaaaaa1 ! Adjust Pb/Pr ! Yes ! ! ...eeeeeddddd11cccccbbaaaaaa1 ! Set Y/Pb/Pr ! Yes ! +-------------------------------+----------------------+------+ ** Single Color block codes *** Reuse previous block +--------+ ! ...000 ! +--------+ Reuse the previous block, effectively cloning that block. *** Adjust Pb/Pr +------------+ ! ...aaa0100 ! +------------+ aaa => Pb/Pr adjustment as described above, against previous block Y is reused from previous block. *** Set Pb/Pr +-------------------+ ! ...bbbbbaaaaa1100 ! +-------------------+ aaaaa => New Pb'. bbbbb => New Pr'. Y is reused from previous block. *** Adjust Y +------------+ ! ...0aaa010 ! +------------+ aaa => Y adjustment as described above, against previous block. Pb/Pr are reused from previous block. *** Adjust Y/Pb/Pr +----------------+ ! ...bbb01aaa010 ! +----------------+ aaa => Y adjustment as described above, against previous block. bbb => Pb/Pr adjustment as described above, against previous block. *** Adjust Y, Set Pb/Pr +-----------------------+ ! ...cccccbbbbb11aaa010 ! +-----------------------+ aaa => Y adjustment as described above, against previous block. bbbbb => New Pb'. ccccc => New Pr'. *** Set Y +---------------+ ! ...0aaaaaa110 ! +---------------+ aaaaaa => New Y'. Pb/Pr are reused from previous block. *** Set Y, Adjust Pb/Pr +-------------------+ ! ...bbb01aaaaaa110 ! +-------------------+ aaaaaa => New Y'. bbb => Pb/Pr adjustment as described above, against previous block. *** Set Y/Pb/Pr +--------------------------+ ! ...cccccbbbbb11aaaaaa110 ! +--------------------------+ aaaaaa => New Y'. bbbbb => New Pb'. ccccc => New Pr'. ** Four Brightness Block Codes *** Set Y +--------------------+ ! ...0cccccbbaaaaaa1 ! +--------------------+ aaaaaa => Sign selector, as described above. bb => Difference selector, as described above. ccccc => New Y'. Pb/Pr are reused from previous block. *** Set Y, Adjust Pb/Pr +------------------------+ ! ...ddd01cccccbbaaaaaa1 ! +------------------------+ aaaaaa => Sign selector, as described above. bb => Difference selector, as described above. ccccc0 => New Y'. (Note the trailing 0) ddd => Pb/Pr adjustment as described above, against previous block. *** Set Y/Pb/Pr +-------------------------------+ ! ...eeeeeddddd11cccccbbaaaaaa1 ! +-------------------------------+ aaaaaa => Sign selector, as described above. bb => Difference selector, as described above. ccccc0 => New Y'. (Note the trailing 0) ddddd => New Pb'. eeeee => New Pr'.