scummvm/engines/sci/scicore/decompressor.cpp

855 lines
21 KiB
C++
Raw Normal View History

/* 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) {
return copyBytes(src, dest, nPacked);
}
int Decompressor::copyBytes(Common::ReadStream *src, Common::WriteStream *dest, uint32 nSize) {
byte buff[1024];
uint32 chunk;
while (nSize && !src->ioFailed() && !dest->ioFailed()) {
chunk = MIN<uint32>(1024, nSize);
src->read(buff, chunk);
dest->write(buff, chunk);
nSize -= chunk;
}
return src->ioFailed() || dest->ioFailed() ? 1 : 0;
}
2009-03-10 22:18:16 +00:00
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
//-------------------------------
2009-03-10 22:18:16 +00:00
void DecompressorComp3::init(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked, uint32 nUnpacked) {
Decompressor::init(src, dest, nPacked, nUnpacked);
_lastchar = _lastbits = _stakptr = 0;
_numbits = 9;
_curtoken = 0x102;
_endtoken = 0x1ff;
memset(_tokens, 0, sizeof(_tokens));
}
int DecompressorComp3::unpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked,
uint32 nUnpacked) {
byte *buffer = NULL;
byte *buffer2 = NULL;
Common::MemoryWriteStream *pBuff = NULL;
Common::MemoryReadStream *pBuffIn = NULL;
switch (_compression) {
case kComp3: // Comp3 compression
return doUnpack(src, dest, nPacked, nUnpacked);
break;
case kComp3View:
case kComp3Pic:
buffer = new byte[nUnpacked];
pBuff = new Common::MemoryWriteStream(buffer, nUnpacked);
doUnpack(src, pBuff, nPacked, nUnpacked);
if (_compression == kComp3View) {
buffer2 = new byte[nUnpacked];
view_reorder(buffer, buffer2);
dest->write(buffer2, nUnpacked);
} else {
pBuffIn = new Common::MemoryReadStream(buffer, nUnpacked);
reorderPic(pBuffIn, dest, nUnpacked);
}
delete[] buffer2;
delete[] buffer;
delete pBuff;
delete pBuffIn;
break;
}
return 0;
}
int DecompressorComp3::doUnpack(Common::ReadStream *src, Common::WriteStream *dest, uint32 nPacked,
uint32 nUnpacked) {
init(src, dest, nPacked, nUnpacked);
byte decryptstart = 0;
uint16 bitstring;
uint16 token;
bool bExit = false;
while (_szUnpacked && !bExit) {
switch (decryptstart) {
case 0:
bitstring = getBits(_numbits);
if (bitstring == 0x101) {// found end-of-data signal
bExit = true;
continue;
}
putByte(bitstring);
_szUnpacked--;
_lastbits = bitstring;
_lastchar = (bitstring & 0xff);
decryptstart = 1;
break;
case 1:
bitstring = getBits(_numbits);
if (bitstring == 0x101) { // found end-of-data signal
bExit = true;
continue;
}
if (bitstring == 0x100) { // start-over signal
_numbits = 9;
_curtoken = 0x102;
_endtoken = 0x1ff;
decryptstart = 0;
continue;
}
token = bitstring;
if (token >= _curtoken) { // index past current point
token = _lastbits;
_stak[_stakptr++] = _lastchar;
}
while ((token > 0xff) && (token < 0x1004)) { // follow links back in data
_stak[_stakptr++] = _tokens[token].data;
token = _tokens[token].next;
}
_lastchar = _stak[_stakptr++] = token & 0xff;
// put stack in buffer
while (_stakptr > 0) {
putByte(_stak[--_stakptr]);
if (--_szUnpacked == 0) {
bExit = true;
continue;
}
}
// put token into record
if (_curtoken <= _endtoken) {
_tokens[_curtoken].data = _lastchar;
_tokens[_curtoken].next = _lastbits;
_curtoken++;
if (_curtoken == _endtoken && _numbits != 12) {
_numbits++;
_endtoken = (_endtoken << 1) + 1;
}
}
_lastbits = bitstring;
break;
}
}
return _dwWrote ? 0 : 1;
}
#define PAL_SIZE 1284
#define EXTRA_MAGIC_SIZE 15
#define VIEW_HEADER_COLORS_8BIT 0x80
void DecompressorComp3::decodeRLE(Common::ReadStream *src, Common::WriteStream *dest, byte *pixeldata, uint16 size) {
int pos = 0;
byte nextbyte;
while (pos < size) {
nextbyte = src->readByte();
dest->writeByte(nextbyte);
pos ++;
switch (nextbyte & 0xC0) {
case 0x40 :
case 0x00 :
dest->write(pixeldata, nextbyte);
pixeldata += nextbyte;
pos += nextbyte;
break;
case 0xC0 :
break;
case 0x80 :
dest->writeByte(*pixeldata++);
break;
}
}
}
void DecompressorComp3::decode_rle(byte **rledata, byte **pixeldata, byte *outbuffer, int size) {
int pos = 0;
char nextbyte;
byte *rd = *rledata;
byte *ob = outbuffer;
byte *pd = *pixeldata;
while (pos < size) {
nextbyte = *(rd++);
*(ob++) = nextbyte;
pos++;
switch (nextbyte&0xC0) {
case 0x40 :
case 0x00 :
memcpy(ob, pd, nextbyte);
pd += nextbyte;
ob += nextbyte;
pos += nextbyte;
break;
case 0xC0 :
break;
case 0x80 :
nextbyte = *(pd++);
*(ob++) = nextbyte;
pos++;
break;
}
}
*rledata = rd;
*pixeldata = pd;
}
/*
* Does the same this as above, only to determine the length of the compressed
* source data.
*
* Yes, this is inefficient.
*/
int DecompressorComp3::rle_size(byte *rledata, int dsize) {
int pos = 0;
char nextbyte;
int size = 0;
while (pos < dsize) {
nextbyte = *(rledata++);
pos++;
size++;
2009-03-10 22:18:16 +00:00
switch (nextbyte & 0xC0) {
case 0x40 :
case 0x00 :
pos += nextbyte;
break;
case 0xC0 :
break;
case 0x80 :
2009-03-10 22:18:16 +00:00
pos++;
break;
}
}
return size;
}
void DecompressorComp3::reorderPic(Common::ReadStream *src, Common::WriteStream *dest, int dsize) {
int view_size, view_start, cdata_size;
byte viewdata[7];
byte *cdata = NULL;
byte *extra = NULL;
// Setting palette
dest->writeByte(PIC_OP_OPX);
dest->writeByte(PIC_OPX_SET_PALETTE);
for (int i = 0; i < 256; i++) // Palette translation map
dest->writeByte(i);
dest->writeUint32LE(0); //Palette timestamp
view_size = src->readUint16LE();
view_start = src->readUint16LE();
cdata_size = src->readUint16LE();
src->read(viewdata, sizeof(viewdata));
// Copy palette colors
copyBytes(src, dest, 1024);
// copy drawing opcodes
if (view_start != PAL_SIZE + 2)
copyBytes(src, dest, view_start - PAL_SIZE - 2);
// storing extra opcodes to be pasted after the cel
if (dsize != view_start + EXTRA_MAGIC_SIZE + view_size) {
extra = new byte[dsize - view_size - view_start - EXTRA_MAGIC_SIZE];
src->read(extra, dsize - view_size - view_start - EXTRA_MAGIC_SIZE);
}
// Writing picture cel opcode and header
dest->writeByte(PIC_OP_OPX);
dest->writeByte(PIC_OPX_EMBEDDED_VIEW);
dest->writeByte(0);
dest->writeUint16LE(0);
dest->writeUint16LE(view_size + 8);
dest->write(viewdata, sizeof(viewdata));
dest->writeByte(0);
// Unpacking RLE cel data
cdata = new byte[cdata_size];
src->read(cdata, cdata_size);
decodeRLE(src, dest, cdata, view_size);
// writing stored extra opcodes
if (extra)
dest->write(extra, dsize - view_size - view_start - EXTRA_MAGIC_SIZE);
delete[] extra;
delete[] cdata;
}
void DecompressorComp3::build_cel_headers(byte **seeker, byte **writer, int celindex, int *cc_lengths, int max) {
for (int c = 0; c < max; c++) {
memcpy(*writer, *seeker, 6);
*seeker += 6;
*writer += 6;
int w = *((*seeker)++);
WRITE_LE_UINT16(*writer, w); /* Zero extension */
*writer += 2;
*writer += cc_lengths[celindex];
celindex++;
}
}
void DecompressorComp3::view_reorder(byte *inbuffer, byte *outbuffer) {
byte *cellengths;
int loopheaders;
int lh_present;
int lh_mask;
int pal_offset;
int cel_total;
int unknown;
byte *seeker = inbuffer;
char celcounts[100];
byte *writer = outbuffer;
byte *lh_ptr;
byte *rle_ptr, *pix_ptr;
int l, lb, c, celindex, lh_last = -1;
int chptr;
int w;
int *cc_lengths;
byte **cc_pos;
/* Parse the main header */
cellengths = inbuffer + READ_LE_UINT16(seeker) + 2;
seeker += 2;
loopheaders = *(seeker++);
lh_present = *(seeker++);
lh_mask = READ_LE_UINT16(seeker);
seeker += 2;
unknown = READ_LE_UINT16(seeker);
seeker += 2;
pal_offset = READ_LE_UINT16(seeker);
seeker += 2;
cel_total = READ_LE_UINT16(seeker);
seeker += 2;
cc_pos = (byte **) malloc(sizeof(byte *) * cel_total);
cc_lengths = (int *) malloc(sizeof(int) * cel_total);
for (c = 0; c < cel_total; c++)
cc_lengths[c] = READ_LE_UINT16(cellengths + 2 * c);
2009-03-10 22:18:16 +00:00
*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]);
2009-03-10 22:18:16 +00:00
*writer++ = 'P';
*writer++ = 'A';
*writer++ = 'L';
for (c = 0; c < 256; c++)
2009-03-10 22:18:16 +00:00
*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