/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ // Resource library #include #include #include #include "sci/scicore/decompressor.h" #include "sci/sci.h" namespace Sci { int Decompressor::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { return copyBytes(src, dest, nPacked); } int Decompressor::copyBytes(Common::ReadStream *src, Common::WriteStream *dest, uint32 nSize) { byte buff[1024]; uint32 chunk; while (nSize && !src->ioFailed() && !dest->ioFailed()) { chunk = MIN(1024, nSize); src->read(buff, chunk); dest->write(buff, chunk); nSize -= chunk; } return src->ioFailed() || dest->ioFailed() ? 1 : 0; } void Decompressor::init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { _src = src; _dest = dest; _szPacked = nPacked; _szUnpacked = nUnpacked; _nBits = 0; _dwRead = _dwWrote = 0; _dwBits = 0; } void Decompressor::fetchBits() { while (_nBits <= 24) { _dwBits |= ((uint32)_src->readByte()) << (24 - _nBits); _nBits += 8; _dwRead++; } } bool Decompressor::getBit() { // fetching more bits to _dwBits buffer if (_nBits == 0) fetchBits(); bool b = _dwBits & 0x80000000; _dwBits <<= 1; _nBits--; return b; } uint32 Decompressor::getBits(int n) { // fetching more data to buffer if needed if (_nBits < n) fetchBits(); uint32 ret = _dwBits >> (32 - n); _dwBits <<= n; _nBits -= n; return ret; } void Decompressor::putByte(byte b) { _dest->writeByte(b); _dwWrote++; } //------------------------------- // Huffman decompressor //------------------------------- int DecompressorHuffman::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { init(src, dest, nPacked, nUnpacked); byte numnodes; int16 c; uint16 terminator; numnodes = _src->readByte(); terminator = _src->readByte() | 0x100; _nodes = (byte *)malloc(numnodes << 1); _src->read(_nodes, numnodes << 1); while ((c = getc2()) != terminator && (c >= 0) && (_szUnpacked-- > 0)) putByte(c); free(_nodes); return _dwWrote ? 0 : 1; } int16 DecompressorHuffman::getc2() { byte *node = _nodes; int16 next; while (node[1]) { if (getBit()) { next = node[1] & 0x0F; // use lower 4 bits if (next == 0) return getBits(8) | 0x100; } else next = node[1] >> 4; // use higher 4 bits node += next << 1; } return (int16)(*node | (node[1] << 8)); } //------------------------------- // LZW-like Decompressor //------------------------------- void DecompressorComp3::init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { Decompressor::init(src, dest, nPacked, nUnpacked); _lastchar = _lastbits = _stakptr = 0; _numbits = 9; _curtoken = 0x102; _endtoken = 0x1ff; memset(_tokens, 0, sizeof(_tokens)); } int DecompressorComp3::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { byte *buffer = NULL; byte *buffer2 = NULL; Common::MemoryWriteStream *pBuff = NULL; Common::MemoryReadStream *pBuffIn = NULL; switch (_compression) { case kComp3: // Comp3 compression return doUnpack(src, dest, nPacked, nUnpacked); break; case kComp3View: case kComp3Pic: buffer = new byte[nUnpacked]; pBuff = new Common::MemoryWriteStream(buffer, nUnpacked); doUnpack(src, pBuff, nPacked, nUnpacked); if (_compression == kComp3View) { buffer2 = new byte[nUnpacked]; view_reorder(buffer, buffer2); dest->write(buffer2, nUnpacked); } else { pBuffIn = new Common::MemoryReadStream(buffer, nUnpacked); reorderPic(pBuffIn, dest, nUnpacked); } delete[] buffer2; delete[] buffer; delete pBuff; delete pBuffIn; break; } return 0; } int DecompressorComp3::doUnpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { init(src, dest, nPacked, nUnpacked); byte decryptstart = 0; uint16 bitstring; uint16 token; bool bExit = false; while (_szUnpacked && !bExit) { switch (decryptstart) { case 0: bitstring = getBits(_numbits); if (bitstring == 0x101) {// found end-of-data signal bExit = true; continue; } putByte(bitstring); _szUnpacked--; _lastbits = bitstring; _lastchar = (bitstring & 0xff); decryptstart = 1; break; case 1: bitstring = getBits(_numbits); if (bitstring == 0x101) { // found end-of-data signal bExit = true; continue; } if (bitstring == 0x100) { // start-over signal _numbits = 9; _curtoken = 0x102; _endtoken = 0x1ff; decryptstart = 0; continue; } token = bitstring; if (token >= _curtoken) { // index past current point token = _lastbits; _stak[_stakptr++] = _lastchar; } while ((token > 0xff) && (token < 0x1004)) { // follow links back in data _stak[_stakptr++] = _tokens[token].data; token = _tokens[token].next; } _lastchar = _stak[_stakptr++] = token & 0xff; // put stack in buffer while (_stakptr > 0) { putByte(_stak[--_stakptr]); if (--_szUnpacked == 0) { bExit = true; continue; } } // put token into record if (_curtoken <= _endtoken) { _tokens[_curtoken].data = _lastchar; _tokens[_curtoken].next = _lastbits; _curtoken++; if (_curtoken == _endtoken && _numbits != 12) { _numbits++; _endtoken = (_endtoken << 1) + 1; } } _lastbits = bitstring; break; } } return _dwWrote ? 0 : 1; } #define PAL_SIZE 1284 #define EXTRA_MAGIC_SIZE 15 #define VIEW_HEADER_COLORS_8BIT 0x80 void DecompressorComp3::decodeRLE(Common::ReadStream *src, Common::WriteStream *dest, byte *pixeldata, uint16 size) { int pos = 0; byte nextbyte; while (pos < size) { nextbyte = src->readByte(); dest->writeByte(nextbyte); pos ++; switch (nextbyte & 0xC0) { case 0x40 : case 0x00 : dest->write(pixeldata, nextbyte); pixeldata += nextbyte; pos += nextbyte; break; case 0xC0 : break; case 0x80 : dest->writeByte(*pixeldata++); break; } } } void DecompressorComp3::decode_rle(byte **rledata, byte **pixeldata, byte *outbuffer, int size) { int pos = 0; char nextbyte; byte *rd = *rledata; byte *ob = outbuffer; byte *pd = *pixeldata; while (pos < size) { nextbyte = *(rd++); *(ob++) = nextbyte; pos++; switch (nextbyte&0xC0) { case 0x40 : case 0x00 : memcpy(ob, pd, nextbyte); pd += nextbyte; ob += nextbyte; pos += nextbyte; break; case 0xC0 : break; case 0x80 : nextbyte = *(pd++); *(ob++) = nextbyte; pos++; break; } } *rledata = rd; *pixeldata = pd; } /* * Does the same this as above, only to determine the length of the compressed * source data. * * Yes, this is inefficient. */ int DecompressorComp3::rle_size(byte *rledata, int dsize) { int pos = 0; char nextbyte; int size = 0; while (pos < dsize) { nextbyte = *(rledata++); pos++; size++; switch (nextbyte & 0xC0) { case 0x40 : case 0x00 : pos += nextbyte; break; case 0xC0 : break; case 0x80 : pos++; break; } } return size; } void DecompressorComp3::reorderPic(Common::ReadStream *src, Common::WriteStream *dest, int dsize) { int view_size, view_start, cdata_size; byte viewdata[7]; byte *cdata = NULL; byte *extra = NULL; // Setting palette dest->writeByte(PIC_OP_OPX); dest->writeByte(PIC_OPX_SET_PALETTE); for (int i = 0; i < 256; i++) // Palette translation map dest->writeByte(i); dest->writeUint32LE(0); //Palette timestamp view_size = src->readUint16LE(); view_start = src->readUint16LE(); cdata_size = src->readUint16LE(); src->read(viewdata, sizeof(viewdata)); // Copy palette colors copyBytes(src, dest, 1024); // copy drawing opcodes if (view_start != PAL_SIZE + 2) copyBytes(src, dest, view_start - PAL_SIZE - 2); // storing extra opcodes to be pasted after the cel if (dsize != view_start + EXTRA_MAGIC_SIZE + view_size) { extra = new byte[dsize - view_size - view_start - EXTRA_MAGIC_SIZE]; src->read(extra, dsize - view_size - view_start - EXTRA_MAGIC_SIZE); } // Writing picture cel opcode and header dest->writeByte(PIC_OP_OPX); dest->writeByte(PIC_OPX_EMBEDDED_VIEW); dest->writeByte(0); dest->writeUint16LE(0); dest->writeUint16LE(view_size + 8); dest->write(viewdata, sizeof(viewdata)); dest->writeByte(0); // Unpacking RLE cel data cdata = new byte[cdata_size]; src->read(cdata, cdata_size); decodeRLE(src, dest, cdata, view_size); // writing stored extra opcodes if (extra) dest->write(extra, dsize - view_size - view_start - EXTRA_MAGIC_SIZE); delete[] extra; delete[] cdata; } void DecompressorComp3::build_cel_headers(byte **seeker, byte **writer, int celindex, int *cc_lengths, int max) { for (int c = 0; c < max; c++) { memcpy(*writer, *seeker, 6); *seeker += 6; *writer += 6; int w = *((*seeker)++); WRITE_LE_UINT16(*writer, w); /* Zero extension */ *writer += 2; *writer += cc_lengths[celindex]; celindex++; } } void DecompressorComp3::view_reorder(byte *inbuffer, byte *outbuffer) { byte *cellengths; int loopheaders; int lh_present; int lh_mask; int pal_offset; int cel_total; int unknown; byte *seeker = inbuffer; char celcounts[100]; byte *writer = outbuffer; byte *lh_ptr; byte *rle_ptr, *pix_ptr; int l, lb, c, celindex, lh_last = -1; int chptr; int w; int *cc_lengths; byte **cc_pos; /* Parse the main header */ cellengths = inbuffer + READ_LE_UINT16(seeker) + 2; seeker += 2; loopheaders = *(seeker++); lh_present = *(seeker++); lh_mask = READ_LE_UINT16(seeker); seeker += 2; unknown = READ_LE_UINT16(seeker); seeker += 2; pal_offset = READ_LE_UINT16(seeker); seeker += 2; cel_total = READ_LE_UINT16(seeker); seeker += 2; cc_pos = (byte **) malloc(sizeof(byte *) * cel_total); cc_lengths = (int *) malloc(sizeof(int) * cel_total); for (c = 0; c < cel_total; c++) cc_lengths[c] = READ_LE_UINT16(cellengths + 2 * c); *writer++ = loopheaders; *writer++ = VIEW_HEADER_COLORS_8BIT; WRITE_LE_UINT16(writer, lh_mask); writer += 2; WRITE_LE_UINT16(writer, unknown); writer += 2; WRITE_LE_UINT16(writer, pal_offset); writer += 2; lh_ptr = writer; writer += 2 * loopheaders; /* Make room for the loop offset table */ pix_ptr = writer; memcpy(celcounts, seeker, lh_present); seeker += lh_present; lb = 1; celindex = 0; rle_ptr = pix_ptr = cellengths + (2 * cel_total); w = 0; for (l = 0; l < loopheaders; l++) { if (lh_mask & lb) { /* The loop is _not_ present */ if (lh_last == -1) { warning("Error: While reordering view: Loop not present, but can't re-use last loop"); lh_last = 0; } WRITE_LE_UINT16(lh_ptr, lh_last); lh_ptr += 2; } else { lh_last = writer - outbuffer; WRITE_LE_UINT16(lh_ptr, lh_last); lh_ptr += 2; WRITE_LE_UINT16(writer, celcounts[w]); writer += 2; WRITE_LE_UINT16(writer, 0); writer += 2; /* Now, build the cel offset table */ chptr = (writer - outbuffer) + (2 * celcounts[w]); for (c = 0; c < celcounts[w]; c++) { WRITE_LE_UINT16(writer, chptr); writer += 2; cc_pos[celindex+c] = outbuffer + chptr; chptr += 8 + READ_LE_UINT16(cellengths + 2 * (celindex + c)); } build_cel_headers(&seeker, &writer, celindex, cc_lengths, celcounts[w]); celindex += celcounts[w]; w++; } lb = lb << 1; } if (celindex < cel_total) { warning("View decompression generated too few (%d / %d) headers", celindex, cel_total); return; } /* Figure out where the pixel data begins. */ for (c = 0; c < cel_total; c++) pix_ptr += rle_size(pix_ptr, cc_lengths[c]); rle_ptr = cellengths + (2 * cel_total); for (c = 0; c < cel_total; c++) decode_rle(&rle_ptr, &pix_ptr, cc_pos[c] + 8, cc_lengths[c]); *writer++ = 'P'; *writer++ = 'A'; *writer++ = 'L'; for (c = 0; c < 256; c++) *writer++ = c; seeker -= 4; /* The missing four. Don't ask why. */ memcpy(writer, seeker, 4*256 + 4); free(cc_pos); free(cc_lengths); } //---------------------------------------------- // LZW 9-12 bits decompressor for SCI0 //---------------------------------------------- int DecompressorLZW::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { init(src, dest, nPacked, nUnpacked); byte *buffin = new byte[nPacked]; byte *buffout = new byte[nUnpacked]; src->read(buffin, nPacked); doUnpack(buffin, buffout, nUnpacked, nPacked); dest->write(buffout, nUnpacked); delete[] buffin; delete[] buffout; return 0; } int DecompressorLZW::doUnpack(byte *src, byte *dest, int length, int complength) { uint16 bitlen = 9; // no. of bits to read (max. 12) uint16 bitmask = 0x01ff; uint16 bitctr = 0; // current bit position uint16 bytectr = 0; // current byte position uint16 token; // The last received value uint16 maxtoken = 0x200; // The biggest token uint16 tokenlist[4096]; // pointers to dest[] uint16 tokenlengthlist[4096]; // char length of each token uint16 tokenctr = 0x102; // no. of registered tokens (starts here) uint16 tokenlastlength = 0; uint16 destctr = 0; while (bytectr < complength) { uint32 tokenmaker = src[bytectr++] >> bitctr; if (bytectr < complength) tokenmaker |= (src[bytectr] << (8 - bitctr)); if (bytectr + 1 < complength) tokenmaker |= (src[bytectr+1] << (16 - bitctr)); token = tokenmaker & bitmask; bitctr += bitlen - 8; while (bitctr >= 8) { bitctr -= 8; bytectr++; } if (token == 0x101) return 0; // terminator if (token == 0x100) { // reset command maxtoken = 0x200; bitlen = 9; bitmask = 0x01ff; tokenctr = 0x0102; } else { { int i; if (token > 0xff) { if (token >= tokenctr) { #ifdef _SCI_DECOMPRESS_DEBUG warning("unpackLZW: Bad token %x", token); #endif // Well this is really bad // May be it should throw something like SCI_ERROR_DECOMPRESSION_INSANE } else { tokenlastlength = tokenlengthlist[token] + 1; if (destctr + tokenlastlength > length) { #ifdef _SCI_DECOMPRESS_DEBUG // For me this seems a normal situation, It's necessary to handle it warning("unpackLZW: Trying to write beyond the end of array(len=%d, destctr=%d, tok_len=%d)", length, destctr, tokenlastlength); #endif i = 0; for (; destctr < length; destctr++) { dest[destctr++] = dest [tokenlist[token] + i]; i++; } } else for (i = 0; i < tokenlastlength; i++) { dest[destctr++] = dest[tokenlist[token] + i]; } } } else { tokenlastlength = 1; if (destctr >= length) { #ifdef _SCI_DECOMPRESS_DEBUG warning("unpackLZW: Try to write single byte beyond end of array"); #endif } else dest[destctr++] = (byte)token; } } if (tokenctr == maxtoken) { if (bitlen < 12) { bitlen++; bitmask <<= 1; bitmask |= 1; maxtoken <<= 1; } else continue; // no further tokens allowed } tokenlist[tokenctr] = destctr - tokenlastlength; tokenlengthlist[tokenctr++] = tokenlastlength; } } return 0; } //---------------------------------------------- // DCL decompressor for SCI1.1 //---------------------------------------------- #define HUFFMAN_LEAF 0x40000000 struct bit_read_struct { int length; int bitpos; int bytepos; byte *data; }; #define BRANCH_SHIFT 12 #define BRANCH_NODE(pos, left, right) ((left << BRANCH_SHIFT) | (right)), #define LEAF_NODE(pos, value) ((value) | HUFFMAN_LEAF), static int length_tree[] = { #include "treedef.1" 0 // We need something witout a comma at the end }; static int distance_tree[] = { #include "treedef.2" 0 // We need something witout a comma at the end }; static int ascii_tree[] = { #include "treedef.3" 0 // We need something witout a comma at the end }; #define CALLC(x) { if ((x) == -SCI_ERROR_DECOMPRESSION_OVERFLOW) return -SCI_ERROR_DECOMPRESSION_OVERFLOW; } int DecompressorDCL::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) { init(src, dest, nPacked, nUnpacked); byte *buffin = new byte[nPacked]; byte *buffout = new byte[nUnpacked]; src->read(buffin, nPacked); unpackDCL(buffin, buffout, nUnpacked, nPacked); dest->write(buffout, nUnpacked); delete[] buffin; delete[] buffout; return 0; } int DecompressorDCL::getbits(struct bit_read_struct *inp, int bits) { int morebytes = (bits + inp->bitpos - 1) >> 3; int result = 0; int i; if (inp->bytepos + morebytes >= inp->length) { warning("read out-of-bounds with bytepos %d + morebytes %d >= length %d", inp->bytepos, morebytes, inp->length); return -7; } for (i = 0; i <= morebytes; i++) result |= (inp->data[inp->bytepos + i]) << (i << 3); result >>= inp->bitpos; result &= ~((~0) << bits); inp->bitpos += bits - (morebytes << 3); inp->bytepos += morebytes; debugC(kDebugLevelDclInflate, "(%d:%04x)", bits, result); return result; } int DecompressorDCL::huffman_lookup(struct bit_read_struct *inp, int *tree) { int pos = 0; int bit; while (!(tree[pos] & HUFFMAN_LEAF)) { CALLC(bit = getbits(inp, 1)); debugC(kDebugLevelDclInflate, "[%d]:%d->", pos, bit); if (bit) pos = tree[pos] & ~(~0 << BRANCH_SHIFT); else pos = tree[pos] >> BRANCH_SHIFT; } debugC(kDebugLevelDclInflate, "=%02x\n", tree[pos] & 0xffff); return tree[pos] & 0xffff; } #define VALUE_M(i) ((i == 0)? 7 : (VALUE_M(i - 1) + 2**i)); #define DCL_ASCII_MODE 1 int DecompressorDCL::unpackDCL(uint8* src, uint8* dest, int length, int complength) { int mode, length_param, value, val_length, val_distance; int write_pos = 0; struct bit_read_struct reader; reader.length = complength; reader.bitpos = 0; reader.bytepos = 0; reader.data = src; CALLC(mode = getbits(&reader, 8)); CALLC(length_param = getbits(&reader, 8)); if (mode == DCL_ASCII_MODE) { warning("DCL-INFLATE: Decompressing ASCII mode (untested)"); } else if (mode) { warning("DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01\n", mode); return -1; } if (Common::isDebugChannelEnabled(kDebugLevelDclInflate)) { for (int i = 0; i < reader.length; i++) { debugC(kDebugLevelDclInflate, "%02x ", reader.data[i]); if (!((i + 1) & 0x1f)) debugC(kDebugLevelDclInflate, "\n"); } debugC(kDebugLevelDclInflate, "\n---\n"); } if (length_param < 3 || length_param > 6) warning("Unexpected length_param value %d (expected in [3,6])\n", length_param); while (write_pos < length) { CALLC(value = getbits(&reader, 1)); if (value) { // (length,distance) pair CALLC(value = huffman_lookup(&reader, length_tree)); if (value < 8) val_length = value + 2; else { int length_bonus; val_length = (1 << (value - 7)) + 8; CALLC(length_bonus = getbits(&reader, value - 7)); val_length += length_bonus; } debugC(kDebugLevelDclInflate, " | "); CALLC(value = huffman_lookup(&reader, distance_tree)); if (val_length == 2) { val_distance = value << 2; CALLC(value = getbits(&reader, 2)); val_distance |= value; } else { val_distance = value << length_param; CALLC(value = getbits(&reader, length_param)); val_distance |= value; } ++val_distance; debugC(kDebugLevelDclInflate, "\nCOPY(%d from %d)\n", val_length, val_distance); if (val_length + write_pos > length) { warning("DCL-INFLATE Error: Write out of bounds while copying %d bytes", val_length); return SCI_ERROR_DECOMPRESSION_OVERFLOW; } if (write_pos < val_distance) { warning("DCL-INFLATE Error: Attempt to copy from before beginning of input stream"); return SCI_ERROR_DECOMPRESSION_INSANE; } while (val_length) { int copy_length = (val_length > val_distance) ? val_distance : val_length; memcpy(dest + write_pos, dest + write_pos - val_distance, copy_length); if (Common::isDebugChannelEnabled(kDebugLevelDclInflate)) { for (int i = 0; i < copy_length; i++) debugC(kDebugLevelDclInflate, "\33[32;31m%02x\33[37;37m ", dest[write_pos + i]); debugC(kDebugLevelDclInflate, "\n"); } val_length -= copy_length; val_distance += copy_length; write_pos += copy_length; } } else { // Copy byte verbatim if (mode == DCL_ASCII_MODE) { CALLC(value = huffman_lookup(&reader, ascii_tree)); } else { CALLC(value = getbits(&reader, 8)); } dest[write_pos++] = value; debugC(kDebugLevelDclInflate, "\33[32;31m%02x \33[37;37m", value); } } return 0; } } // End of namespace Sci