diff options
Diffstat (limited to 'engines/cine/unpack.cpp')
-rw-r--r-- | engines/cine/unpack.cpp | 137 |
1 files changed, 72 insertions, 65 deletions
diff --git a/engines/cine/unpack.cpp b/engines/cine/unpack.cpp index 4409af4b07..364331e439 100644 --- a/engines/cine/unpack.cpp +++ b/engines/cine/unpack.cpp @@ -30,94 +30,101 @@ namespace Cine { -struct UnpackCtx { - int size, datasize; - uint32 crc; - uint32 chk; - byte *dst; - const byte *src; -}; +uint32 CineUnpacker::readSource() { + uint32 value = READ_BE_UINT32(_src); + _src -= 4; + return value; +} -static int rcr(UnpackCtx *uc, int CF) { - int rCF = (uc->chk & 1); - uc->chk >>= 1; - if (CF) { - uc->chk |= 0x80000000; +int CineUnpacker::rcr(int inputCarry) { + int outputCarry = (_chunk32b & 1); + _chunk32b >>= 1; + if (inputCarry) { + _chunk32b |= 0x80000000; } - return rCF; + return outputCarry; } -static int nextChunk(UnpackCtx *uc) { - int CF = rcr(uc, 0); - if (uc->chk == 0) { - uc->chk = READ_BE_UINT32(uc->src); uc->src -= 4; - uc->crc ^= uc->chk; - CF = rcr(uc, 1); +int CineUnpacker::nextBit() { + int carry = rcr(0); + // Normally if the chunk becomes zero then the carry is one as + // the end of chunk marker is always the last to be shifted out. + if (_chunk32b == 0) { + _chunk32b = readSource(); + _crc ^= _chunk32b; + carry = rcr(1); // Put the end of chunk marker in the most significant bit } - return CF; + return carry; } -static uint16 getCode(UnpackCtx *uc, byte numChunks) { +uint16 CineUnpacker::getBits(byte numBits) { uint16 c = 0; - while (numChunks--) { + while (numBits--) { c <<= 1; - if (nextChunk(uc)) { - c |= 1; - } + c |= nextBit(); } return c; } -static void unpackHelper1(UnpackCtx *uc, byte numChunks, byte addCount) { - uint16 count = getCode(uc, numChunks) + addCount + 1; - uc->datasize -= count; - while (count--) { - *uc->dst = (byte)getCode(uc, 8); - --uc->dst; +void CineUnpacker::unpackRawBytes(uint16 numBytes) { + _datasize -= numBytes; + while (numBytes--) { + *_dst = (byte)getBits(8); + --_dst; } } -static void unpackHelper2(UnpackCtx *uc, byte numChunks) { - uint16 i = getCode(uc, numChunks); - uint16 count = uc->size + 1; - uc->datasize -= count; - while (count--) { - *uc->dst = *(uc->dst + i); - --uc->dst; +void CineUnpacker::copyRelocatedBytes(uint16 offset, uint16 numBytes) { + _datasize -= numBytes; + while (numBytes--) { + *_dst = *(_dst + offset); + --_dst; } } -bool delphineUnpack(byte *dst, const byte *src, int len) { - UnpackCtx uc; - uc.src = src + len - 4; - uc.datasize = READ_BE_UINT32(uc.src); uc.src -= 4; - uc.dst = dst + uc.datasize - 1; - uc.size = 0; - uc.crc = READ_BE_UINT32(uc.src); uc.src -= 4; - uc.chk = READ_BE_UINT32(uc.src); uc.src -= 4; - uc.crc ^= uc.chk; +bool CineUnpacker::unpack(byte *dst, const byte *src, int srcLen) { + _src = src + srcLen - 4; + _datasize = readSource(); // Unpacked length in bytes + _dst = dst + _datasize - 1; + _crc = readSource(); + _chunk32b = readSource(); + _crc ^= _chunk32b; do { - if (!nextChunk(&uc)) { - uc.size = 1; - if (!nextChunk(&uc)) { - unpackHelper1(&uc, 3, 0); - } else { - unpackHelper2(&uc, 8); + /* + Bits => Action: + 0 0 => unpackRawBytes(3 bits + 1) i.e. unpackRawBytes(1..9) + 1 1 1 => unpackRawBytes(8 bits + 9) i.e. unpackRawBytes(9..264) + 0 1 => copyRelocatedBytes(8 bits, 2) i.e. copyRelocatedBytes(0..255, 2) + 1 0 0 => copyRelocatedBytes(9 bits, 3) i.e. copyRelocatedBytes(0..511, 3) + 1 0 1 => copyRelocatedBytes(10 bits, 4) i.e. copyRelocatedBytes(0..1023, 4) + 1 1 0 => copyRelocatedBytes(12 bits, 8 bits + 1) i.e. copyRelocatedBytes(0..4095, 1..256) + */ + if (!nextBit()) { // 0... + if (!nextBit()) { // 0 0 + uint16 numBytes = getBits(3) + 1; + unpackRawBytes(numBytes); + } else { // 0 1 + uint16 numBytes = 2; + uint16 offset = getBits(8); + copyRelocatedBytes(offset, numBytes); } - } else { - uint16 c = getCode(&uc, 2); - if (c == 3) { - unpackHelper1(&uc, 8, 8); - } else if (c < 2) { - uc.size = c + 2; - unpackHelper2(&uc, c + 9); - } else { - uc.size = getCode(&uc, 8); - unpackHelper2(&uc, 12); + } else { // 1... + uint16 c = getBits(2); + if (c == 3) { // 1 1 1 + uint16 numBytes = getBits(8) + 9; + unpackRawBytes(numBytes); + } else if (c < 2) { // 1 0 x + uint16 numBytes = c + 3; + uint16 offset = getBits(c + 9); + copyRelocatedBytes(offset, numBytes); + } else { // 1 1 0 + uint16 numBytes = getBits(8) + 1; + uint16 offset = getBits(12); + copyRelocatedBytes(offset, numBytes); } } - } while (uc.datasize > 0 && uc.src >= src - 4); - return uc.crc == 0; + } while (_datasize > 0 && _src >= src - 4); + return _crc == 0; } } // End of namespace Cine |