/* 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. * */ /*************************************************************************** ** decomp.c ** ** Routines that deal with AGI version 3 specific features. ** The original LZW code is from DJJ, October 1989, p.86. ** It has been modified to handle AGI compression. ** ** (c) 1997 Lance Ewing ***************************************************************************/ #include "agi/agi.h" #include "agi/lzw.h" #include "common/textconsole.h" namespace Agi { class LZWDecoder { private: enum { MAXBITS = 12, TABLE_SIZE = 18041, // strange number START_BITS = 9 }; int32 BITS, MAX_VALUE, MAX_CODE; uint32 *prefixCode; uint8 *appendCharacter; uint8 *decodeStack; int32 inputBitCount; // Number of bits in input bit buffer uint32 inputBitBuffer; public: LZWDecoder(); ~LZWDecoder(); void lzwExpand(uint8 *in, uint8 *out, int32 len); private: int setBits(int32 value); uint8 *decodeString(uint8 *buffer, uint32 code); uint32 inputCode(uint8 **input); }; LZWDecoder::LZWDecoder() { decodeStack = (uint8 *)calloc(1, 8192); prefixCode = (uint32 *)malloc(TABLE_SIZE * sizeof(uint32)); appendCharacter = (uint8 *)malloc(TABLE_SIZE * sizeof(uint8)); inputBitCount = 0; // Number of bits in input bit buffer inputBitBuffer = 0L; BITS = MAX_VALUE = MAX_CODE = 0; } LZWDecoder::~LZWDecoder() { free(decodeStack); free(prefixCode); free(appendCharacter); } /** * Adjust the number of bits used to store codes to the value passed in. */ int LZWDecoder::setBits(int32 value) { if (value == MAXBITS) return true; BITS = value; MAX_VALUE = (1 << BITS) - 1; MAX_CODE = MAX_VALUE - 1; return false; } /** * Return the string that the code taken from the input buffer * represents. The string is returned as a stack, i.e. the characters are * in reverse order. */ uint8 *LZWDecoder::decodeString(uint8 *buffer, uint32 code) { uint32 i; for (i = 0; code > 255;) { *buffer++ = appendCharacter[code]; code = prefixCode[code]; if (i++ >= 4000) { error("lzw: error in code expansion"); } } *buffer = code; return buffer; } /** * Return the next code from the input buffer. */ uint32 LZWDecoder::inputCode(uint8 **input) { uint32 r; while (inputBitCount <= 24) { inputBitBuffer |= (uint32) * (*input)++ << inputBitCount; inputBitCount += 8; } r = (inputBitBuffer & 0x7FFF) % (1 << BITS); inputBitBuffer >>= BITS; inputBitCount -= BITS; return r; } /** * Uncompress the data contained in the input buffer and store * the result in the output buffer. The fileLength parameter says how * many bytes to uncompress. The compression itself is a form of LZW that * adjusts the number of bits that it represents its codes in as it fills * up the available codes. Two codes have special meaning: * * code 256 = start over * code 257 = end of data */ void LZWDecoder::lzwExpand(uint8 *in, uint8 *out, int32 len) { int32 c, lzwnext, lzwnew, lzwold; uint8 *s, *end; LZWDecoder d; setBits(START_BITS); // Starts at 9-bits lzwnext = 257; // Next available code to define end = (uint8 *)(out + (uint32)len); lzwold = inputCode(&in); // Read in the first code c = lzwold; lzwnew = inputCode(&in); while ((out < end) && (lzwnew != 0x101)) { if (lzwnew == 0x100) { // Code to "start over" lzwnext = 258; setBits(START_BITS); lzwold = inputCode(&in); c = lzwold; *out++ = (char)c; lzwnew = inputCode(&in); } else { if (lzwnew >= lzwnext) { // Handles special LZW scenario *decodeStack = c; s = decodeString(decodeStack + 1, lzwold); } else s = decodeString(decodeStack, lzwnew); // Reverse order of decoded string and // store in out buffer c = *s; while (s >= decodeStack) *out++ = *s--; if (lzwnext > MAX_CODE) setBits(BITS + 1); prefixCode[lzwnext] = lzwold; appendCharacter[lzwnext] = c; lzwnext++; lzwold = lzwnew; lzwnew = inputCode(&in); } } } void lzwExpand(uint8 *in, uint8 *out, int32 len) { LZWDecoder d; d.lzwExpand(in, out, len); } } // End of namespace Agi