diff options
-rw-r--r-- | dists/msvc8/sci.vcproj | 14 | ||||
-rw-r--r-- | engines/sci/module.mk | 5 | ||||
-rw-r--r-- | engines/sci/scicore/decompress0.cpp | 336 | ||||
-rw-r--r-- | engines/sci/scicore/decompress01.cpp | 601 | ||||
-rw-r--r-- | engines/sci/scicore/decompress1.cpp | 382 | ||||
-rw-r--r-- | engines/sci/scicore/decompress11.cpp | 146 | ||||
-rw-r--r-- | engines/sci/scicore/decompressor.cpp | 877 | ||||
-rw-r--r-- | engines/sci/scicore/decompressor.h | 179 | ||||
-rw-r--r-- | engines/sci/scicore/resource.cpp | 221 | ||||
-rw-r--r-- | engines/sci/scicore/resource.h | 69 |
10 files changed, 1253 insertions, 1577 deletions
diff --git a/dists/msvc8/sci.vcproj b/dists/msvc8/sci.vcproj index 9806b55b8f..96de5fa93f 100644 --- a/dists/msvc8/sci.vcproj +++ b/dists/msvc8/sci.vcproj @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="windows-1252"?> <VisualStudioProject ProjectType="Visual C++" - Version="8,00" + Version="8.00" Name="sci" ProjectGUID="{53F17B2B-0412-4EC3-A999-ED0537BB5223}" RootNamespace="sci" @@ -486,19 +486,11 @@ Name="scicore" > <File - RelativePath="..\..\engines\sci\scicore\decompress0.cpp" + RelativePath="..\..\engines\sci\scicore\decompressor.cpp" > </File> <File - RelativePath="..\..\engines\sci\scicore\decompress01.cpp" - > - </File> - <File - RelativePath="..\..\engines\sci\scicore\decompress1.cpp" - > - </File> - <File - RelativePath="..\..\engines\sci\scicore\decompress11.cpp" + RelativePath="..\..\engines\sci\scicore\decompressor.h" > </File> <File diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 62380f015b..35457a382b 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -55,10 +55,7 @@ MODULE_OBJS = \ gfx/resource/res_pic.o \ gfx/resource/res_view0.o \ gfx/resource/res_view1.o \ - scicore/decompress0.o \ - scicore/decompress01.o \ - scicore/decompress1.o \ - scicore/decompress11.o \ + scicore/decompressor.o \ scicore/resource.o \ scicore/sciconsole.o \ scicore/versions.o \ diff --git a/engines/sci/scicore/decompress0.cpp b/engines/sci/scicore/decompress0.cpp deleted file mode 100644 index fda253b31e..0000000000 --- a/engines/sci/scicore/decompress0.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* 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$ - * - */ - -/* Reads data from a resource file and stores the result in memory. -** This is for SCI version 0 style compression. -*/ - -#include "common/stream.h" -#include "common/endian.h" - -#include "sci/sci_memory.h" -#include "sci/scicore/resource.h" - -namespace Sci { - -//#define _SCI_DECOMPRESS_DEBUG - -// 9-12 bit LZW encoding -int unpackLZW(uint8 *dest, uint8 *src, int length, int complength) { - // Doesn't do length checking yet - /* Theory: Considering the input as a bit stream, we get a series of - ** 9 bit elements in the beginning. Every one of them is a 'token' - ** and either represents a literal (if < 0x100), or a link to a previous - ** token (tokens start at 0x102, because 0x101 is the end-of-stream - ** indicator and 0x100 is used to reset the bit stream decoder). - ** If it's a link, the indicated token and the character following it are - ** placed into the output stream. Note that the 'indicated token' may - ** very well consist of a link-token-plus-literal construct again, so - ** it's possible to represent strings longer than 2 recursively. - ** If the maximum number of tokens has been reached, the bit length is - ** increased by one, up to a maximum of 12 bits. - ** This implementation remembers the position each token was print to in - ** the output array, and the length of this token. This method should - ** be faster than the recursive approach. - */ - - 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; -} - -// Huffman-style token encoding -/***************************************************************************/ -/* This code was taken from Carl Muckenhoupt's sde.c, with some minor */ -/* modifications. */ -/***************************************************************************/ - -// unpackHuffman helper function -int16 getc2(uint8 *node, uint8 *src, uint16 *bytectr, uint16 *bitctr, int complength) { - uint16 next; - - while (node[1] != 0) { - int16 value = (src[*bytectr] << (*bitctr)); - (*bitctr)++; - if (*bitctr == 8) { - (*bitctr) = 0; - (*bytectr)++; - } - - if (value & 0x80) { - next = node[1] & 0x0f; // low 4 bits - if (next == 0) { - uint16 result = (src[*bytectr] << (*bitctr)); - - if (++(*bytectr) > complength) - return -1; - else if (*bytectr < complength) - result |= src[*bytectr] >> (8 - (*bitctr)); - - result &= 0x0ff; - return (result | 0x100); - } - } else { - next = node[1] >> 4; // high 4 bits - } - node += next << 1; - } - - return (int16)READ_LE_UINT16(node); -} - -// Huffman token decryptor -int unpackHuffman(uint8* dest, uint8* src, int length, int complength) { - // no complength checking atm */ - uint8 numnodes, terminator; - uint8 *nodes; - int16 c; - uint16 bitctr = 0, bytectr; - - numnodes = src[0]; - terminator = src[1]; - bytectr = 2 + (numnodes << 1); - nodes = src + 2; - - while (((c = getc2(nodes, src, &bytectr, &bitctr, complength)) != (0x0100 | terminator)) && (c >= 0)) { - if (length-- == 0) - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - - *dest = (uint8)c; - dest++; - } - - return (c == -1) ? SCI_ERROR_DECOMPRESSION_OVERFLOW : 0; - -} - -// Carl Muckenhoupt's decompression code ends here - -int sci0_get_compression_method(Common::ReadStream &stream) { - uint16 compressionMethod; - - stream.readUint16LE(); - stream.readUint16LE(); - stream.readUint16LE(); - compressionMethod = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - return compressionMethod; -} - -int decompress0(Resource *result, Common::ReadStream &stream, int sci_version) { - uint16 compressedLength; - uint16 compressionMethod; - uint8 *buffer; - uint8 type; - - result->id = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - result->number = result->id & 0x07ff; - type = result->id >> 11; - - result->type = (ResourceType)type; - - if ((result->number > sci_max_resource_nr[sci_version]) || (type > kResourceTypeInvalid)) - return SCI_ERROR_DECOMPRESSION_INSANE; - - compressedLength = stream.readUint16LE(); - result->size = stream.readUint16LE(); - compressionMethod = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - if (result->size > SCI_MAX_RESOURCE_SIZE) - return SCI_ERROR_RESOURCE_TOO_BIG; - - if (compressedLength > 4) - compressedLength -= 4; - else { // Object has size zero (e.g. view.000 in sq3) (does this really exist?) - result->data = 0; - result->status = SCI_STATUS_NOMALLOC; - return SCI_ERROR_EMPTY_OBJECT; - } - - buffer = (uint8 *)sci_malloc(compressedLength); - result->data = (unsigned char *)sci_malloc(result->size); - - if (stream.read(buffer, compressedLength) != compressedLength) { - free(result->data); - free(buffer); - result->data = 0; - return SCI_ERROR_IO_ERROR; - }; - - -#ifdef _SCI_DECOMPRESS_DEBUG - debug("Resource %s.%03hi encrypted with method %hi at %.2f%% ratio", - getResourceTypeName(result->type), result->number, compressionMethod, - (result->size == 0) ? -1.0 : - (100.0 * compressedLength / result->size)); - debug(" compressedLength = 0x%hx, actualLength=0x%hx", - compressedLength, result->size); -#endif - - bool overflow = false; - - switch (compressionMethod) { - case 0: // no compression - if (result->size != compressedLength) - overflow = true; - else - memcpy(result->data, buffer, compressedLength); - break; - - case 1: // LZW compression - if (unpackLZW(result->data, buffer, result->size, compressedLength)) - overflow = true; - break; - - case 2: // Some sort of Huffman encoding - if (unpackHuffman(result->data, buffer, result->size, compressedLength)) - overflow = true; - break; - - default: - warning("Resource %s.%03hi: Compression method %hi not supported", - getResourceTypeName(result->type), result->number, - compressionMethod); - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_UNKNOWN_COMPRESSION; - } - - if (overflow) { - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - - result->status = SCI_STATUS_ALLOCATED; - free(buffer); - return 0; -} - -} // End of namespace Sci diff --git a/engines/sci/scicore/decompress01.cpp b/engines/sci/scicore/decompress01.cpp deleted file mode 100644 index 9cdb02f9cb..0000000000 --- a/engines/sci/scicore/decompress01.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/* 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$ - * - */ - -// Reads data from a resource file and stores the result in memory - -#include "common/stream.h" -#include "common/endian.h" - -#include "sci/sci_memory.h" -#include "sci/scicore/resource.h" - -namespace Sci { - -// The following code was originally created by Carl Muckenhoupt for his -// SCI decoder. It has been ported to the FreeSCI environment by Sergey Lapin. - -// TODO: Clean up, re-organize, improve speed-wise */ - -struct tokenlist { - uint8 data; - int16 next; -} tokens[0x1004]; - -static int8 stak[0x1014] = {0}; -static int8 lastchar = 0; -static int16 stakptr = 0; -static uint16 s_numbits, s_bitstring, lastbits, decryptstart; -static int16 curtoken, endtoken; - -uint32 gbits(int numbits, uint8 * data, int dlen); - -int decrypt3(uint8 *dest, uint8 *src, int length, int complength) { - // Init - int i; - - lastchar = lastbits = s_bitstring = stakptr = 0; - s_numbits = 9; - curtoken = 0x102; - endtoken = 0x1ff; - decryptstart = 0; - gbits(0, 0, 0); - - for (i = 0;i < 0x1004;i++) { - tokens[i].next = 0; - tokens[i].data = 0; - } - - // Start decrypting - - static int16 token; - while (length != 0) { - switch (decryptstart) { - case 0: - case 1: - s_bitstring = gbits(s_numbits, src, complength); - if (s_bitstring == 0x101) { // found end-of-data signal - decryptstart = 4; - return 0; - } - if (decryptstart == 0) { // first char - decryptstart = 1; - lastbits = s_bitstring; - *(dest++) = lastchar = (s_bitstring & 0xff); - if (--length != 0) - continue; - return 0; - } - if (s_bitstring == 0x100) { // start-over signal - s_numbits = 9; - endtoken = 0x1ff; - curtoken = 0x102; - decryptstart = 0; - continue; - } - token = s_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; - case 2: - while (stakptr > 0) { // put stack in buffer - *(dest++) = stak[--stakptr]; - length--; - if (length == 0) { - decryptstart = 2; - return 0; - } - } - decryptstart = 1; - if (curtoken <= endtoken) { // put token into record - tokens[curtoken].data = lastchar; - tokens[curtoken].next = lastbits; - curtoken++; - if (curtoken == endtoken && s_numbits != 12) { - s_numbits++; - endtoken <<= 1; - endtoken++; - } - } - lastbits = s_bitstring; - continue; // When are "break" and "continue" synonymous? - case 4: - return 0; - } - } - - return 0; -} - -uint32 gbits(int numbits, uint8 * data, int dlen) { - int place; // indicates location within byte - uint32 bitstring; - static uint32 whichbit = 0; - int i; - - if (numbits == 0) { - whichbit = 0; - return 0; - } - - place = whichbit >> 3; - bitstring = 0; - for (i = (numbits >> 3) + 1;i >= 0;i--) { - if (i + place < dlen) - bitstring |= data[place+i] << (8 * (2 - i)); - } - //bitstring = data[place + 2] | (long)(data[place + 1]) << 8 | (long)(data[place]) << 16; - bitstring >>= 24 - (whichbit & 7) - numbits; - bitstring &= (0xffffffff >> (32 - numbits)); - // Okay, so this could be made faster with a table lookup. - // It doesn't matter. It's fast enough as it is. - whichbit += numbits; - - return bitstring; -} - -// Carl Muckenhoupt's code ends here - -enum { - PIC_OP_SET_COLOR = 0xf0, - PIC_OP_DISABLE_VISUAL = 0xf1, - PIC_OP_SET_PRIORITY = 0xf2, - PIC_OP_DISABLE_PRIORITY = 0xf3, - PIC_OP_SHORT_PATTERNS = 0xf4, - PIC_OP_MEDIUM_LINES = 0xf5, - PIC_OP_LONG_LINES = 0xf6, - PIC_OP_SHORT_LINES = 0xf7, - PIC_OP_FILL = 0xf8, - PIC_OP_SET_PATTERN = 0xf9, - PIC_OP_ABSOLUTE_PATTERN = 0xfa, - PIC_OP_SET_CONTROL = 0xfb, - PIC_OP_DISABLE_CONTROL = 0xfc, - PIC_OP_MEDIUM_PATTERNS = 0xfd, - PIC_OP_OPX = 0xfe, - PIC_OP_TERMINATE = 0xff -}; - -enum { - PIC_OPX_SET_PALETTE_ENTRIES = 0, - PIC_OPX_EMBEDDED_VIEW = 1, - PIC_OPX_SET_PALETTE = 2, - PIC_OPX_PRIORITY_TABLE_EQDIST = 3, - PIC_OPX_PRIORITY_TABLE_EXPLICIT = 4 -}; - -#define PAL_SIZE 1284 -#define CEL_HEADER_SIZE 7 -#define EXTRA_MAGIC_SIZE 15 - -static void 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. - */ -static int 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; -} - -byte *pic_reorder(byte *inbuffer, int dsize) { - byte *reorderBuffer; - int view_size; - int view_start; - int cdata_size; - int i; - byte *seeker = inbuffer; - byte *writer; - char viewdata[CEL_HEADER_SIZE]; - byte *cdata, *cdata_start; - - writer = reorderBuffer = (byte *) malloc(dsize); - - *(writer++) = PIC_OP_OPX; - *(writer++) = PIC_OPX_SET_PALETTE; - - for (i = 0;i < 256;i++) // Palette translation map - *(writer++) = i; - - WRITE_LE_UINT16(writer, 0); // Palette stamp - writer += 2; - WRITE_LE_UINT16(writer, 0); - writer += 2; - - view_size = READ_LE_UINT16(seeker); - seeker += 2; - view_start = READ_LE_UINT16(seeker); - seeker += 2; - cdata_size = READ_LE_UINT16(seeker); - seeker += 2; - - memcpy(viewdata, seeker, sizeof(viewdata)); - seeker += sizeof(viewdata); - - memcpy(writer, seeker, 4 * 256); // Palette - seeker += 4 * 256; - writer += 4 * 256; - - if (view_start != PAL_SIZE + 2) { // +2 for the opcode - memcpy(writer, seeker, view_start - PAL_SIZE - 2); - seeker += view_start - PAL_SIZE - 2; - writer += view_start - PAL_SIZE - 2; - } - - if (dsize != view_start + EXTRA_MAGIC_SIZE + view_size) { - memcpy(reorderBuffer + view_size + view_start + EXTRA_MAGIC_SIZE, seeker, - dsize - view_size - view_start - EXTRA_MAGIC_SIZE); - seeker += dsize - view_size - view_start - EXTRA_MAGIC_SIZE; - } - - cdata_start = cdata = (byte *)malloc(cdata_size); - memcpy(cdata, seeker, cdata_size); - seeker += cdata_size; - - writer = reorderBuffer + view_start; - *(writer++) = PIC_OP_OPX; - *(writer++) = PIC_OPX_EMBEDDED_VIEW; - *(writer++) = 0; - *(writer++) = 0; - *(writer++) = 0; - WRITE_LE_UINT16(writer, view_size + 8); - writer += 2; - - memcpy(writer, viewdata, sizeof(viewdata)); - writer += sizeof(viewdata); - - *(writer++) = 0; - - decode_rle(&seeker, &cdata, writer, view_size); - - free(cdata_start); - free(inbuffer); - - return reorderBuffer; -} - -#define VIEW_HEADER_COLORS_8BIT 0x80 - -static void build_cel_headers(byte **seeker, byte **writer, int celindex, int *cc_lengths, int max) { - int c, w; - - for (c = 0;c < max;c++) { - w = READ_LE_UINT16(*seeker); - WRITE_LE_UINT16(*writer, w); - *seeker += 2; - *writer += 2; - w = READ_LE_UINT16(*seeker); - WRITE_LE_UINT16(*writer, w); - *seeker += 2; - *writer += 2; - w = READ_LE_UINT16(*seeker); - WRITE_LE_UINT16(*writer, w); - *seeker += 2; - *writer += 2; - w = *((*seeker)++); - WRITE_LE_UINT16(*writer, w); // Zero extension - *writer += 2; - - *writer += cc_lengths[celindex]; - celindex ++; - } -} - -byte *view_reorder(byte *inbuffer, int dsize) { - 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 *outbuffer = (byte *)malloc(dsize); - 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("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 NULL; - } - - // 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); - free(inbuffer); - - return outbuffer; -} - -int decompress01(Resource *result, Common::ReadStream &stream, int sci_version) { - uint16 compressedLength; - uint16 compressionMethod; - uint8 *buffer; - uint8 type; - - result->id = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - result->number = result->id & 0x07ff; - type = result->id >> 11; - - result->type = (ResourceType)type; - - if ((result->number > sci_max_resource_nr[sci_version]) || (type > kResourceTypeInvalid)) - return SCI_ERROR_DECOMPRESSION_INSANE; - - compressedLength = stream.readUint16LE(); - result->size = stream.readUint16LE(); - compressionMethod = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - if (result->size > SCI_MAX_RESOURCE_SIZE) - return SCI_ERROR_RESOURCE_TOO_BIG; - - if (compressedLength > 4) - compressedLength -= 4; - else { // Object has size zero (e.g. view.000 in sq3) (does this really exist?) - result->data = 0; - result->status = SCI_STATUS_NOMALLOC; - return SCI_ERROR_EMPTY_OBJECT; - } - - buffer = (uint8 *)sci_malloc(compressedLength); - result->data = (unsigned char *)sci_malloc(result->size); - - if (stream.read(buffer, compressedLength) != compressedLength) { - free(result->data); - free(buffer); - result->data = 0; - return SCI_ERROR_IO_ERROR; - }; - - -#ifdef _SCI_DECOMPRESS_DEBUG - debug("Resource %s.%03hi encrypted with method SCI01/%hi at %.2f%% ratio", - sci_resource_types[result->type], result->number, compressionMethod, - (result->size == 0) ? -1.0 : - (100.0 * compressedLength / result->size)); - debug(" compressedLength = 0x%hx, actualLength=0x%hx", - compressedLength, result->size); -#endif - - bool overflow = false; - - switch (compressionMethod) { - case 0: // no compression - if (result->size != compressedLength) - overflow = true; - else - memcpy(result->data, buffer, compressedLength); - break; - - case 1: // Some huffman encoding - if (unpackHuffman(result->data, buffer, result->size, compressedLength)) - overflow = true; - break; - - case 2: - case 3: - case 4: - if (decrypt3(result->data, buffer, result->size, compressedLength)) { - overflow = true; - } else { - if (compressionMethod == 3) - result->data = view_reorder(result->data, result->size); - if (compressionMethod == 4) - result->data = pic_reorder(result->data, result->size); - } - break; - - default: - warning("Resource %s.%03hi: Compression method SCI1/%hi not supported", - getResourceTypeName(result->type), result->number, - compressionMethod); - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_UNKNOWN_COMPRESSION; - } - - if (overflow) { - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - - result->status = SCI_STATUS_ALLOCATED; - free(buffer); - return 0; -} - -} // End of namespace Sci diff --git a/engines/sci/scicore/decompress1.cpp b/engines/sci/scicore/decompress1.cpp deleted file mode 100644 index 06b7f05103..0000000000 --- a/engines/sci/scicore/decompress1.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/* 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$ - * - */ - -// Reads data from a resource file and stores the result in memory - -#include "common/debug.h" -#include "common/stream.h" -#include "common/util.h" - -#include "sci/sci.h" -#include "sci/sci_memory.h" -#include "sci/scicore/resource.h" - -namespace Sci { - -// DEFLATE-DCL -// Refer to the FreeSCI docs for a full description. - -#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; } - -static inline int getbits_msb_first(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 -SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - - 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; - - return result; -} - -static inline int 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 -SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - - 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; -} - -static int 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 unpackDCL(uint8* dest, uint8* src, 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; -} - -int decrypt3(uint8* dest, uint8* src, int length, int complength); - -int decompress1(Resource *result, Common::ReadStream &stream, int sci_version) { - uint16 compressedLength; - uint16 compressionMethod; - uint8 *buffer; - uint16 type; - - if (sci_version == SCI_VERSION_1_EARLY) { - result->id = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - result->number = result->id & 0x07ff; - type = result->id >> 11; - - result->type = (ResourceType)type; - } else { - result->id = stream.readByte(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - type = result->id & 0x7f; - result->number = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - result->type = (ResourceType)type; - } - - if ((result->number > sci_max_resource_nr[sci_version]) || (type > kResourceTypeInvalid)) - return SCI_ERROR_DECOMPRESSION_INSANE; - - compressedLength = stream.readUint16LE(); - result->size = stream.readUint16LE(); - compressionMethod = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - if (result->size > SCI_MAX_RESOURCE_SIZE) - return SCI_ERROR_RESOURCE_TOO_BIG; - - if (compressedLength > 4) - compressedLength -= 4; - else { // Object has size zero (e.g. view.000 in sq3) (does this really exist?) - result->data = 0; - result->status = SCI_STATUS_NOMALLOC; - return SCI_ERROR_EMPTY_OBJECT; - } - - buffer = (uint8 *)sci_malloc(compressedLength); - result->data = (unsigned char *)sci_malloc(result->size); - - if (stream.read(buffer, compressedLength) != compressedLength) { - free(result->data); - free(buffer); - result->data = 0; - return SCI_ERROR_IO_ERROR; - }; - - -#ifdef _SCI_DECOMPRESS_DEBUG - debug("Resource %i.%s encrypted with method SCI1%c/%hi at %.2f%% ratio", - result->number, sci_resource_type_suffixes[result->type], - early ? 'e' : 'l', - compressionMethod, - (result->size == 0) ? -1.0 : - (100.0 * compressedLength / result->size)); - debug(" compressedLength = 0x%hx, actualLength=0x%hx", - compressedLength, result->size); -#endif - - switch (compressionMethod) { - case 0: // no compression - if (result->size != compressedLength) { - free(result->data); - result->data = NULL; - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - memcpy(result->data, buffer, compressedLength); - result->status = SCI_STATUS_ALLOCATED; - break; - - case 1: // LZW - if (unpackHuffman(result->data, buffer, result->size, compressedLength)) { - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - result->status = SCI_STATUS_ALLOCATED; - break; - - case 2: // ??? - case 3: - case 4: - if (decrypt3(result->data, buffer, result->size, compressedLength)) { - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - - if (compressionMethod == 3) - result->data = view_reorder(result->data, result->size); - if (compressionMethod == 4) - result->data = pic_reorder(result->data, result->size); - result->status = SCI_STATUS_ALLOCATED; - break; - - default: - warning("Resource %s.%03hi: Compression method SCI1/%hi not supported", - getResourceTypeName(result->type), result->number, - compressionMethod); - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_UNKNOWN_COMPRESSION; - } - - free(buffer); - - return 0; -} - -} // End of namespace Sci diff --git a/engines/sci/scicore/decompress11.cpp b/engines/sci/scicore/decompress11.cpp deleted file mode 100644 index 114f965f64..0000000000 --- a/engines/sci/scicore/decompress11.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* 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$ - * - */ - -// Reads data from a resource file and stores the result in memory - -#include "common/stream.h" - -#include "sci/sci_memory.h" -#include "sci/scicore/resource.h" - -namespace Sci { - -int unpackDCL(uint8* dest, uint8* src, int length, int complength); - -int decompress11(Resource *result, Common::ReadStream &stream, int sci_version) { - uint16 compressedLength; - uint16 compressionMethod; - uint8 *buffer; - uint16 type; - - result->id = stream.readByte(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - type = result->id & 0x7f; - if (type > kResourceTypeInvalid) - return SCI_ERROR_DECOMPRESSION_INSANE; - - result->type = (ResourceType)type; - - result->number = stream.readUint16LE(); - compressedLength = stream.readUint16LE(); - result->size = stream.readUint16LE(); - compressionMethod = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - if (result->size > SCI_MAX_RESOURCE_SIZE) - return SCI_ERROR_RESOURCE_TOO_BIG; - - if (compressedLength > 0) - compressedLength -= 0; - else { // Object has size zero (e.g. view.000 in sq3) (does this really exist?) - result->data = 0; - result->status = SCI_STATUS_NOMALLOC; - return SCI_ERROR_EMPTY_OBJECT; - } - - buffer = (uint8 *)sci_malloc(compressedLength); - result->data = (unsigned char *)sci_malloc(result->size); - - if (stream.read(buffer, compressedLength) != compressedLength) { - free(result->data); - free(buffer); - result->data = 0; - return SCI_ERROR_IO_ERROR; - }; - - if (!(compressedLength & 1)) { // Align - stream.readByte(); - } - -#ifdef _SCI_DECOMPRESS_DEBUG - debug("Resource %i.%s encrypted with method SCI1.1/%hi at %.2f%% ratio", - result->number, getResourceTypeSuffix(result->type), - compressionMethod, - (result->size == 0) ? -1.0 : - (100.0 * compressedLength / result->size)); - debug(" compressedLength = 0x%hx, actualLength=0x%hx", - compressedLength, result->size); -#endif - - switch (compressionMethod) { - case 0: // no compression - if (result->size != compressedLength) { - free(result->data); - result->data = NULL; - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - memcpy(result->data, buffer, compressedLength); - result->status = SCI_STATUS_ALLOCATED; - break; - - case 18: - case 19: - case 20: - if (unpackDCL(result->data, buffer, result->size, compressedLength)) { - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_DECOMPRESSION_OVERFLOW; - } - result->status = SCI_STATUS_ALLOCATED; - break; - - case 3: - case 4: // NYI - warning("Resource %d.%s: Warning: compression type #%d not yet implemented", - result->number, getResourceTypeSuffix(result->type), compressionMethod); - free(result->data); - result->data = NULL; - result->status = SCI_STATUS_NOMALLOC; - break; - - default: - warning("Resource %d.%s: Compression method SCI1/%hi not supported", - result->number, getResourceTypeSuffix(result->type), - compressionMethod); - free(result->data); - result->data = 0; // So that we know that it didn't work - result->status = SCI_STATUS_NOMALLOC; - free(buffer); - return SCI_ERROR_UNKNOWN_COMPRESSION; - } - - free(buffer); - - return 0; -} - -} // End of namespace Sci diff --git a/engines/sci/scicore/decompressor.cpp b/engines/sci/scicore/decompressor.cpp new file mode 100644 index 0000000000..609698f9c9 --- /dev/null +++ b/engines/sci/scicore/decompressor.cpp @@ -0,0 +1,877 @@ +/* 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 <common/util.h> +#include <common/endian.h> +#include <common/debug.h> +#include "sci/scicore/decompressor.h" +#include "sci/sci.h" + +namespace Sci { + +int Decompressor::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked) { + init(src, dest, nPacked, nUnpacked); + byte buff[1024]; + uint32 chunk; + while (_szPacked && !_src->ioFailed() && !_dest->ioFailed()) { + chunk = MIN<uint32>(1024, _szPacked); + _src->read(buff, chunk); + _dest->write(buff, chunk); + _szPacked -= 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; + + switch (_compression) { + case kComp3: // Comp3 compression + return doUnpack(src, dest, nPacked, nUnpacked); + break; + case kComp3View: + case kComp3Pic: + buffer = new byte[nUnpacked]; + buffer2 = new byte[nUnpacked]; + pBuff = new Common::MemoryWriteStream(buffer, nUnpacked); + doUnpack(src, pBuff, nPacked, nUnpacked); + if (_compression == kComp3View) + view_reorder(buffer, buffer2); + else + pic_reorder(buffer, buffer2, nUnpacked); + dest->write(buffer2, nUnpacked); + delete[] buffer2; + delete[] buffer; + 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; +} + +enum { + PIC_OP_SET_COLOR = 0xf0, + PIC_OP_DISABLE_VISUAL = 0xf1, + PIC_OP_SET_PRIORITY = 0xf2, + PIC_OP_DISABLE_PRIORITY = 0xf3, + PIC_OP_SHORT_PATTERNS = 0xf4, + PIC_OP_MEDIUM_LINES = 0xf5, + PIC_OP_LONG_LINES = 0xf6, + PIC_OP_SHORT_LINES = 0xf7, + PIC_OP_FILL = 0xf8, + PIC_OP_SET_PATTERN = 0xf9, + PIC_OP_ABSOLUTE_PATTERN = 0xfa, + PIC_OP_SET_CONTROL = 0xfb, + PIC_OP_DISABLE_CONTROL = 0xfc, + PIC_OP_MEDIUM_PATTERNS = 0xfd, + PIC_OP_OPX = 0xfe, + PIC_OP_TERMINATE = 0xff +}; + +enum { + PIC_OPX_SET_PALETTE_ENTRIES = 0, + PIC_OPX_EMBEDDED_VIEW = 1, + PIC_OPX_SET_PALETTE = 2, + PIC_OPX_PRIORITY_TABLE_EQDIST = 3, + PIC_OPX_PRIORITY_TABLE_EXPLICIT = 4 +}; + +#define PAL_SIZE 1284 +#define CEL_HEADER_SIZE 7 +#define EXTRA_MAGIC_SIZE 15 + +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::pic_reorder(byte *inbuffer, byte *outbuffer, int dsize) { + int view_size; + int view_start; + int cdata_size; + int i; + byte *seeker = inbuffer; + byte *writer; + char viewdata[CEL_HEADER_SIZE]; + byte *cdata, *cdata_start; + + writer = outbuffer; + + *(writer++) = PIC_OP_OPX; + *(writer++) = PIC_OPX_SET_PALETTE; + + for (i = 0; i < 256; i++) /* Palette translation map */ + *(writer++) = i; + + WRITE_LE_UINT16(writer, 0); /* Palette stamp */ + writer += 2; + WRITE_LE_UINT16(writer, 0); + writer += 2; + + view_size = READ_LE_UINT16(seeker); + seeker += 2; + view_start = READ_LE_UINT16(seeker); + seeker += 2; + cdata_size = READ_LE_UINT16(seeker); + seeker += 2; + + memcpy(viewdata, seeker, sizeof(viewdata)); + seeker += sizeof(viewdata); + + memcpy(writer, seeker, 4*256); /* Palette */ + seeker += 4*256; + writer += 4*256; + + if (view_start != PAL_SIZE + 2) { /* +2 for the opcode */ + memcpy(writer, seeker, view_start-PAL_SIZE-2); + seeker += view_start - PAL_SIZE - 2; + writer += view_start - PAL_SIZE - 2; + } + + if (dsize != view_start+EXTRA_MAGIC_SIZE+view_size) { + memcpy(outbuffer+view_size+view_start+EXTRA_MAGIC_SIZE, seeker, + dsize-view_size-view_start-EXTRA_MAGIC_SIZE); + seeker += dsize-view_size-view_start-EXTRA_MAGIC_SIZE; + } + + cdata_start = cdata = (byte *)malloc(cdata_size); + memcpy(cdata, seeker, cdata_size); + seeker += cdata_size; + + writer = outbuffer + view_start; + *(writer++) = PIC_OP_OPX; + *(writer++) = PIC_OPX_EMBEDDED_VIEW; + *(writer++) = 0; + *(writer++) = 0; + *(writer++) = 0; + WRITE_LE_UINT16(writer, view_size + 8); + writer += 2; + + memcpy(writer, viewdata, sizeof(viewdata)); + writer += sizeof(viewdata); + + *(writer++) = 0; + + decode_rle(&seeker, &cdata, writer, view_size); + + free(cdata_start); +} + +#define VIEW_HEADER_COLORS_8BIT 0x80 + +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 diff --git a/engines/sci/scicore/decompressor.h b/engines/sci/scicore/decompressor.h new file mode 100644 index 0000000000..3f8bd147c3 --- /dev/null +++ b/engines/sci/scicore/decompressor.h @@ -0,0 +1,179 @@ +/* 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$ + * + */ + +#ifndef SCI_SCICORE_DECOMPRESSOR_H +#define SCI_SCICORE_DECOMPRESSOR_H + +#include "common/file.h" + +namespace Sci { +enum ResourceCompression { + kCompUnknown = -1, + kCompNone = 0, + kCompLZW, + kCompHuffman, + kComp3, // LZW-like compression used in SCI01 and SCI1 + kComp3View, // Comp3 + view Post-processing + kComp3Pic, // Comp3 + pic Post-processing + kCompDCL, + kCompSTACpack // ? Used in SCI32 +}; +//---------------------------------------------- +// Base class for decompressors +// Simply copies nPacked bytes from src to dest +//---------------------------------------------- +class Decompressor { +public: + Decompressor(){} + ~Decompressor(){} + + //! get a number of bits from _src stream + /** @param n - number of bits to get + @return (uint32) n-bits number + */ + virtual int unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); + +protected: + //! Initialize decompressor + /** @param src - source stream to read from + @param dest - destination stream to write to + @param nPacked - size of packed data + @param nUnpacket - size of unpacked data + @return (int) 0 on success, non-zero on error + */ + virtual void init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); //! get one bit from _src stream + /** @return (bool) bit; + */ + virtual bool getBit(); + //! get a number of bits from _src stream + /** @param n - number of bits to get + @return (uint32) n-bits number + */ + virtual uint32 getBits(int n); + //! put byte to _dest stream + /** @param b - byte to put + */ + virtual void putByte(byte b); + virtual void fetchBits(); + + uint32 _dwBits; + byte _nBits; + + uint32 _szPacked; + uint32 _szUnpacked; + uint32 _dwRead; + uint32 _dwWrote; + + Common::ReadStream *_src; + Common::WriteStream *_dest; +}; + +//---------------------------------------------- +// Huffman decompressor +//---------------------------------------------- +class DecompressorHuffman : public Decompressor { +public: + int unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); + +protected: + int16 getc2(); + + byte *_nodes; +}; + +//---------------------------------------------- +// LZW-like decompressor for SCI01/SCI1 +// TODO: Needs clean-up of post-processing fncs +//---------------------------------------------- +class DecompressorComp3 : public Decompressor { +public: + DecompressorComp3(int nCompression) { + _compression = nCompression; + } + void init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked); + int unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); + +protected: + // actual unpacking procedure + int doUnpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); + // functions to post-process view and pic resources + void decode_rle(byte **rledata, byte **pixeldata, byte *outbuffer, int size); + int rle_size(byte *rledata, int dsize); + void pic_reorder(byte *inbuffer, byte *outbuffer, int dsize); + void build_cel_headers(byte **seeker, byte **writer, int celindex, int *cc_lengths, int max); + void view_reorder(byte *inbuffer, byte *outbuffer); + + struct tokenlist { + byte data; + uint16 next; + } _tokens[0x1004]; + byte _stak[0x1014]; + byte _lastchar; + uint16 _stakptr; + uint16 _numbits, _lastbits; + uint16 _curtoken, _endtoken; + int _compression; +}; + +//---------------------------------------------- +// LZW 9-12 bits decompressor for SCI0 +// TODO : Needs clean-up of doUnpack() +//---------------------------------------------- +class DecompressorLZW : public Decompressor { +public: +// void init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked); + int unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); + +protected: + int doUnpack(byte *src, byte *dest, int length, int complength); + +}; + +//---------------------------------------------- +// DCL decompressor for SCI1.1 +// TODO : Needs clean-up of doUnpack() +//---------------------------------------------- +class DecompressorDCL : public Decompressor { +public: +// void init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked); + int unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, + uint32 nUnpacked); + +protected: + int unpackDCL(byte *src, byte *dest, int length, int complength); + int getbits(struct bit_read_struct *inp, int bits); + int huffman_lookup(struct bit_read_struct *inp, int *tree); + +}; + +} // End of namespace Sci + +#endif // SCI_SCICORE_DECOMPRESSOR_H
\ No newline at end of file diff --git a/engines/sci/scicore/resource.cpp b/engines/sci/scicore/resource.cpp index cfb7eeaa96..b027fb39e8 100644 --- a/engines/sci/scicore/resource.cpp +++ b/engines/sci/scicore/resource.cpp @@ -32,6 +32,7 @@ #include "sci/sci_memory.h" #include "sci/scicore/resource.h" #include "sci/scicore/vocabulary.h" +#include "sci/scicore/decompressor.h" namespace Sci { @@ -93,18 +94,6 @@ const char *getResourceTypeSuffix(ResourceType restype) { typedef int decomp_funct(Resource *result, Common::ReadStream &stream, int sci_version); typedef void patch_sprintf_funct(char *string, Resource *res); -static decomp_funct *decompressors[] = { - NULL, - &decompress0, - &decompress01, - &decompress01, - &decompress01, - &decompress1, - &decompress1, - &decompress11, - NULL -}; - //-- Resource main functions -- Resource::Resource() { data = NULL; @@ -113,7 +102,7 @@ Resource::Resource() { id = 0; size = 0; file_offset = 0; - status = SCI_STATUS_NOMALLOC; + status = kResStatusNoMalloc; lockers = 0; source = NULL; } @@ -127,7 +116,7 @@ Resource::~Resource() { void Resource::unalloc() { delete[] data; data = NULL; - status = SCI_STATUS_NOMALLOC; + status = kResStatusNoMalloc; } //-- Resmgr helper functions -- @@ -213,7 +202,7 @@ bool ResourceManager::loadFromPatchFile(Resource *res) { if (really_read != res->size) { error("Read %d bytes from %s but expected %d!", really_read, filename, res->size); } - res->status = SCI_STATUS_ALLOCATED; + res->status = kResStatusAllocated; return true; } @@ -233,17 +222,13 @@ void ResourceManager::loadResource(Resource *res) { } file.seek(res->file_offset, SEEK_SET); - // Check whether we support this at all - if (decompressors[_sciVersion] == NULL) - error("Resource manager's SCI version (%d) is invalid", _sciVersion); - // Decompress from regular resource file - int error = decompressors[_sciVersion](res, file, _sciVersion); - + int error = decompress(res, &file); if (error) { warning("Error %d occured while reading %s.%03d from resource file: %s\n", error, getResourceTypeName(res->type), res->number, sci_error_types[error]); res->unalloc(); } + } Resource *ResourceManager::testResource(ResourceType type, int number) { @@ -252,7 +237,18 @@ Resource *ResourceManager::testResource(ResourceType type, int number) { return NULL; } -int sci0_get_compression_method(Common::ReadStream &stream); +int sci0_get_compression_method(Common::ReadStream &stream) { + uint16 compressionMethod; + + stream.readUint16LE(); + stream.readUint16LE(); + stream.readUint16LE(); + compressionMethod = stream.readUint16LE(); + if (stream.err()) + return SCI_ERROR_IO_ERROR; + + return compressionMethod; +} int sci_test_view_type(ResourceManager *mgr) { Common::File file; @@ -421,7 +417,7 @@ ResourceManager::ResourceManager(int version, int maxMemory) { _sciVersion = version = SCI_VERSION_1_EARLY; loadResource(res); - if (res->status == SCI_STATUS_NOMALLOC) + if (res->status == kResStatusNoMalloc) version = SCI_VERSION_1_LATE; break; } @@ -469,17 +465,17 @@ ResourceManager::~ResourceManager() { } void ResourceManager::removeFromLRU(Resource *res) { - if (res->status != SCI_STATUS_ENQUEUED) { + if (res->status != kResStatusEnqueued) { sciprintf("Resmgr: Oops: trying to remove resource that isn't enqueued\n"); return; } _LRU.remove(res); _memoryLRU -= res->size; - res->status = SCI_STATUS_ALLOCATED; + res->status = kResStatusAllocated; } void ResourceManager::addToLRU(Resource *res) { - if (res->status != SCI_STATUS_ALLOCATED) { + if (res->status != kResStatusAllocated) { warning("Resmgr: Oops: trying to enqueue resource with state %d", res->status); return; } @@ -491,7 +487,7 @@ void ResourceManager::addToLRU(Resource *res) { mgr->_memoryLRU); #endif - res->status = SCI_STATUS_ENQUEUED; + res->status = kResStatusEnqueued; } void ResourceManager::printLRU() { @@ -547,26 +543,26 @@ Resource *ResourceManager::findResource(ResourceType type, int number, int lock) if (!retval->status) loadResource(retval); - else if (retval->status == SCI_STATUS_ENQUEUED) + else if (retval->status == kResStatusEnqueued) removeFromLRU(retval); // Unless an error occured, the resource is now either // locked or allocated, but never queued or freed. if (lock) { - if (retval->status == SCI_STATUS_ALLOCATED) { - retval->status = SCI_STATUS_LOCKED; + if (retval->status == kResStatusAllocated) { + retval->status = kResStatusLocked; retval->lockers = 0; _memoryLocked += retval->size; } ++retval->lockers; - } else if (retval->status != SCI_STATUS_LOCKED) { // Don't lock it - if (retval->status == SCI_STATUS_ALLOCATED) + } else if (retval->status != kResStatusLocked) { // Don't lock it + if (retval->status == kResStatusAllocated) addToLRU(retval); } - freeOldResources(retval->status == SCI_STATUS_ALLOCATED); + freeOldResources(retval->status == kResStatusAllocated); if (retval->data) return retval; @@ -585,14 +581,14 @@ void ResourceManager::unlockResource(Resource *res, int resnum, ResourceType res return; } - if (res->status != SCI_STATUS_LOCKED) { + if (res->status != kResStatusLocked) { sciprintf("Resmgr: Warning: Attempt to unlock unlocked resource %s.%03d\n", getResourceTypeName(res->type), res->number); return; } if (!--res->lockers) { // No more lockers? - res->status = SCI_STATUS_ALLOCATED; + res->status = kResStatusAllocated; _memoryLocked -= res->size; addToLRU(res); } @@ -677,8 +673,8 @@ int ResourceManager::detectVolVersion() { } // SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes // SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes - // SCI1.1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes - // SCI32 volume format : {bResType wResNumber dwPacked+4 dwUnpacked wCompression} = 13 bytes + // SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes + // SCI32 volume format : {bResType wResNumber dwPacked dwUnpacked wCompression} = 13 bytes // Try to parse volume with SCI0 scheme to see if it make sense // Checking 1MB of data should be enough to determine the version uint16 resId, wCompression; @@ -814,7 +810,7 @@ void ResourceManager::processPatch(ResourceSource *source, // Overwrite everything, because we're patching newrsc->id = resId; newrsc->number = resnumber; - newrsc->status = SCI_STATUS_NOMALLOC; + newrsc->status = kResStatusNoMalloc; newrsc->type = restype; newrsc->source = source; newrsc->size = fsize - patch_data_offset - 2; @@ -899,7 +895,7 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) { // adding a new resource if (_resMap.contains(resId) == false) { res = new Resource; - res->id = id; + res->id = resId;//id; res->file_offset = offset & (((~bMask) << 24) | 0xFFFFFF); res->number = number; res->type = type; @@ -953,7 +949,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vo _resMap.setVal(resId, res); res->type = (ResourceType)type; res->number = number; - res->id = res->number | (res->type << 16); + res->id = resId;//res->number | (res->type << 16); res->source = _mapVersion < SCI_VERSION_1_1 ? getVolume(map, off >> 28) : vol; if (_mapVersion < SCI_VERSION_32) res->file_offset = _mapVersion < SCI_VERSION_1_1 ? off & 0x0FFFFFFF : off << 1; @@ -965,4 +961,149 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map, ResourceSource *vo return 0; } +int ResourceManager::readResourceInfo(Resource *res, Common::File *file, + uint32&szPacked, ResourceCompression &compression) { + // SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes + // SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes + // SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes + // SCI32 volume format : {bResType wResNumber dwPacked dwUnpacked wCompression} = 13 bytes + uint16 w, number, szUnpacked; + uint32 wCompression; + ResourceType type; + + switch (_volVersion) { + case SCI_VERSION_0: + w = file->readUint16LE(); + type = (ResourceType)(w >> 11); + number = w & 0x7FF; + szPacked = file->readUint16LE() - 4; + szUnpacked = file->readUint16LE(); + wCompression = file->readUint16LE(); + break; + case SCI_VERSION_1: + type = (ResourceType)file->readByte(); + number = file->readUint16LE(); + szPacked = file->readUint16LE() - 4; + szUnpacked = file->readUint16LE(); + wCompression = file->readUint16LE(); + break; + case SCI_VERSION_1_1: + type = (ResourceType)file->readByte(); + number = file->readUint16LE(); + szPacked = file->readUint16LE(); + szUnpacked = file->readUint16LE(); + wCompression = file->readUint16LE(); + break; + case SCI_VERSION_32: + type = (ResourceType)file->readByte(); + number = file->readUint16LE(); + szPacked = file->readUint32LE(); + szUnpacked = file->readUint32LE(); + wCompression = file->readUint16LE(); + break; + default: + return SCI_ERROR_INVALID_RESMAP_ENTRY; + } + // check if there were errors while reading + if (file->ioFailed()) + return SCI_ERROR_IO_ERROR; + res->id = RESOURCE_HASH(type, number); + res->type = type; + res->number = number; + res->size = szUnpacked; + // checking compression method + if (wCompression == 0) + compression = kCompNone; + switch (_sciVersion) { + case SCI_VERSION_0: + if (wCompression == 1) + compression = kCompLZW; + else if (wCompression == 2) + compression = kCompHuffman; + break; + case SCI_VERSION_01: + case SCI_VERSION_01_VGA: + case SCI_VERSION_01_VGA_ODD: + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + if (wCompression == 1) + compression = kCompHuffman; + else if (wCompression == 2) + compression = kComp3; + else if (wCompression == 3) + compression = kComp3View; + else if (wCompression == 4) + compression = kComp3Pic; + break; + case SCI_VERSION_1_1: + if (wCompression >= 18 && wCompression <= 20) + compression = kCompDCL; + break; + case SCI_VERSION_32: + if (wCompression == 32) + compression = kCompSTACpack; + break; + default: + compression = kCompUnknown; + } + + return compression == kCompUnknown ? SCI_ERROR_UNKNOWN_COMPRESSION : 0; +} + +int ResourceManager::decompress(Resource *res, Common::File *file) { + int error; + uint32 szPacked = 0; + Common::MemoryWriteStream *pDest = NULL; + ResourceCompression compression = kCompUnknown; + + // fill resource info + error = readResourceInfo(res, file, szPacked, compression); + if (error) + return error; + // getting a decompressor + Decompressor *dec = NULL; + switch (compression) { + case kCompNone: + dec = new Decompressor; + break; + case kCompLZW: + dec = new DecompressorLZW; + break; + case kCompHuffman: + dec = new DecompressorHuffman; + break; + case kComp3: + case kComp3View: + case kComp3Pic: + dec = new DecompressorComp3(compression); + break; + case kCompDCL: + dec = new DecompressorDCL; + break; + default: + warning("Resource %s #%d: Compression method %d not supported", + getResourceTypeName(res->type), res->number, compression); + break; + } + + if (dec) { + res->data = new byte[res->size]; + pDest = new Common::MemoryWriteStream(res->data , res->size); + error = dec->unpack(file, pDest, szPacked, res->size); + } else + error = SCI_ERROR_UNKNOWN_COMPRESSION; + + if (!error) + res->status = kResStatusAllocated; + else { + delete res->data; + res->data = 0; + res->status = kResStatusNoMalloc; + } + delete dec; + delete pDest; + + return error; +} + } // End of namespace Sci diff --git a/engines/sci/scicore/resource.h b/engines/sci/scicore/resource.h index a44ff0f0fa..4cf0de3ad3 100644 --- a/engines/sci/scicore/resource.h +++ b/engines/sci/scicore/resource.h @@ -30,6 +30,8 @@ #include "common/file.h" #include "common/archive.h" +#include "sci/scicore/decompressor.h" + namespace Common { class ReadStream; } @@ -40,10 +42,12 @@ namespace Sci { #define SCI_MAX_RESOURCE_SIZE 0x0400000 /*** RESOURCE STATUS TYPES ***/ -#define SCI_STATUS_NOMALLOC 0 -#define SCI_STATUS_ALLOCATED 1 -#define SCI_STATUS_ENQUEUED 2 /* In the LRU queue */ -#define SCI_STATUS_LOCKED 3 /* Allocated and in use */ +enum ResourceStatus { + kResStatusNoMalloc=0, + kResStatusAllocated, + kResStatusEnqueued, /* In the LRU queue */ + kResStatusLocked /* Allocated and in use */ +}; /*** INITIALIZATION RESULT TYPES ***/ #define SCI_ERROR_IO_ERROR 1 @@ -163,12 +167,12 @@ public: byte *data; uint16 number; ResourceType type; - uint16 id; // contains number and type. + uint32 id; // contains number and type. // TODO: maybe use uint32 and set id = RESOURCE_HASH() // for all SCI versions unsigned int size; unsigned int file_offset; /* Offset in file */ - byte status; + ResourceStatus status; unsigned short lockers; /* Number of places where this resource was locked */ ResourceSource *source; }; @@ -267,6 +271,8 @@ protected: void loadResource(Resource *res); bool loadFromPatchFile(Resource *res); void freeOldResources(int last_invulnerable); + int decompress(Resource *res, Common::File *file); + int readResourceInfo(Resource *res, Common::File *file, uint32&szPacked, ResourceCompression &compression); /**--- Resource map decoding functions ---*/ int detectMapVersion(); @@ -295,57 +301,6 @@ protected: void removeFromLRU(Resource *res); }; - /**--- Decompression functions ---**/ - int decompress0(Resource *result, Common::ReadStream &stream, int sci_version); - /* Decrypts resource data and stores the result for SCI0-style compression. - ** Parameters : result: The Resource the decompressed data is stored in. - ** stream: Stream of the resource file - ** sci_version : Actual SCI resource version - ** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was - ** encountered. - */ - - int decompress01(Resource *result, Common::ReadStream &stream, int sci_version); - /* Decrypts resource data and stores the result for SCI01-style compression. - ** Parameters : result: The Resource the decompressed data is stored in. - ** stream: Stream of the resource file - ** sci_version : Actual SCI resource version - ** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was - ** encountered. - */ - - int decompress1(Resource *result, Common::ReadStream &stream, int sci_version); - /* Decrypts resource data and stores the result for SCI1.1-style compression. - ** Parameters : result: The Resource the decompressed data is stored in. - ** sci_version : Actual SCI resource version - ** stream: Stream of the resource file - ** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was - ** encountered. - */ - - int decompress11(Resource *result, Common::ReadStream &stream, int sci_version); - /* Decrypts resource data and stores the result for SCI1.1-style compression. - ** Parameters : result: The Resource the decompressed data is stored in. - ** sci_version : Actual SCI resource version - ** stream: Stream of the resource file - ** Returns : (int) 0 on success, one of SCI_ERROR_* if a problem was - ** encountered. - */ - - int unpackHuffman(uint8* dest, uint8* src, int length, int complength); - /* Huffman token decryptor - defined in decompress0.c and used in decompress01.c - */ - - int unpackDCL(uint8* dest, uint8* src, int length, int complength); - /* DCL inflate- implemented in decompress1.c - */ - - byte *view_reorder(byte *inbuffer, int dsize); - /* SCI1 style view compression */ - - byte *pic_reorder(byte *inbuffer, int dsize); - /* SCI1 style pic compression */ - } // End of namespace Sci #endif // SCI_SCICORE_RESOURCE_H |