scummvm/engines/sci/scicore/decompress01.cpp
Eugene Sandulenko 87e7f85410 Update headers. scicore/
svn-id: r38411
2009-02-17 15:09:09 +00:00

636 lines
16 KiB
C++

/* 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 "sci/include/sci_memory.h"
#include "sci/include/sciresource.h"
/***************************************************************************
* 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 {
guint8 data;
gint16 next;
} tokens[0x1004];
static gint8 stak[0x1014] = {0};
static gint8 lastchar = 0;
static gint16 stakptr = 0;
static guint16 numbits, s_bitstring, lastbits, decryptstart;
static gint16 curtoken, endtoken;
guint32 gbits(int numbits, guint8 * data, int dlen);
void decryptinit3(void) {
int i;
lastchar = lastbits = s_bitstring = stakptr = 0;
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;
}
}
int decrypt3(guint8 *dest, guint8 *src, int length, int complength) {
static gint16 token;
while (length != 0) {
switch (decryptstart) {
case 0:
case 1:
s_bitstring = gbits(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 */
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 && numbits != 12) {
numbits++;
endtoken <<= 1;
endtoken++;
}
}
lastbits = s_bitstring;
continue; /* When are "break" and "continue" synonymous? */
case 4:
return 0;
}
}
return 0; /* [DJ] shut up compiler warning */
}
guint32 gbits(int numbits, guint8 * data, int dlen) {
int place; /* indicates location within byte */
guint32 bitstring;
static guint32 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;
putInt16(writer, 0); /* Palette stamp */
writer += 2;
putInt16(writer, 0);
writer += 2;
view_size = getUInt16(seeker);
seeker += 2;
view_start = getUInt16(seeker);
seeker += 2;
cdata_size = getUInt16(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;
putInt16(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 = getUInt16(*seeker);
putInt16(*writer, w);
*seeker += 2;
*writer += 2;
w = getUInt16(*seeker);
putInt16(*writer, w);
*seeker += 2;
*writer += 2;
w = getUInt16(*seeker);
putInt16(*writer, w);
*seeker += 2;
*writer += 2;
w = *((*seeker)++);
putInt16(*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 + getUInt16(seeker) + 2;
seeker += 2;
loopheaders = *(seeker++);
lh_present = *(seeker++);
lh_mask = getUInt16(seeker);
seeker += 2;
unknown = getUInt16(seeker);
seeker += 2;
pal_offset = getUInt16(seeker);
seeker += 2;
cel_total = getUInt16(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] = getUInt16(cellengths + 2 * c);
*(writer++) = loopheaders;
*(writer++) = VIEW_HEADER_COLORS_8BIT;
putInt16(writer, lh_mask);
writer += 2;
putInt16(writer, unknown);
writer += 2;
putInt16(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) {
fprintf(stderr, "Error: While reordering view: Loop not present, but can't re-use last loop!\n");
lh_last = 0;
}
putInt16(lh_ptr, lh_last);
lh_ptr += 2;
} else {
lh_last = writer - outbuffer;
putInt16(lh_ptr, lh_last);
lh_ptr += 2;
putInt16(writer, celcounts[w]);
writer += 2;
putInt16(writer, 0);
writer += 2;
/* Now, build the cel offset table */
chptr = (writer - outbuffer) + (2 * celcounts[w]);
for (c = 0;c < celcounts[w];c++) {
putInt16(writer, chptr);
writer += 2;
cc_pos[celindex+c] = outbuffer + chptr;
chptr += 8 + getUInt16(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) {
fprintf(stderr, "View decompression generated too few (%d / %d) headers!\n", 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_t *result, int resh, int sci_version) {
guint16 compressedLength, result_size;
guint16 compressionMethod;
guint8 *buffer;
if (read(resh, &(result->id), 2) != 2)
return SCI_ERROR_IO_ERROR;
#ifdef WORDS_BIGENDIAN
result->id = GUINT16_SWAP_LE_BE_CONSTANT(result->id);
#endif
result->number = result->id & 0x07ff;
result->type = result->id >> 11;
if ((result->number > sci_max_resource_nr[sci_version] || (result->type > sci_invalid_resource)))
return SCI_ERROR_DECOMPRESSION_INSANE;
if ((read(resh, &compressedLength, 2) != 2) ||
(read(resh, &result_size, 2) != 2) ||
(read(resh, &compressionMethod, 2) != 2))
return SCI_ERROR_IO_ERROR;
#ifdef WORDS_BIGENDIAN
compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength);
result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size);
compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod);
#endif
result->size = result_size;
/* if ((result->size < 0) || (compressedLength < 0))
return SCI_ERROR_DECOMPRESSION_INSANE; */
/* This return will never happen in SCI0 or SCI1 (does it have any use?) */
if ((result->size > SCI_MAX_RESOURCE_SIZE) ||
(compressedLength > 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 = (guint8*)sci_malloc(compressedLength);
result->data = (unsigned char*)sci_malloc(result->size);
if (read(resh, buffer, compressedLength) != compressedLength) {
free(result->data);
free(buffer);
return SCI_ERROR_IO_ERROR;
};
#ifdef _SCI_DECOMPRESS_DEBUG
fprintf(stderr, "Resource %s.%03hi encrypted with method SCI01/%hi at %.2f%%"
" ratio\n",
sci_resource_types[result->type], result->number, compressionMethod,
(result->size == 0) ? -1.0 :
(100.0 * compressedLength / result->size));
fprintf(stderr, " compressedLength = 0x%hx, actualLength=0x%hx\n",
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: /* Some huffman encoding */
if (decrypt2(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: /* ??? */
decryptinit3();
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;
}
result->status = SCI_STATUS_ALLOCATED;
break;
case 3:
decryptinit3();
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;
}
result->data = view_reorder(result->data, result->size);
result->status = SCI_STATUS_ALLOCATED;
break;
case 4:
decryptinit3();
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;
}
result->data = pic_reorder(result->data, result->size);
result->status = SCI_STATUS_ALLOCATED;
break;
default:
fprintf(stderr, "Resource %s.%03hi: Compression method SCI1/%hi not "
"supported!\n", sci_resource_types[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;
}