scummvm/engines/dm/lzw.cpp
2016-09-04 00:13:19 +02:00

188 lines
6.3 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.
*
*/
/*
* Based on the Reverse Engineering work of Christophe Fontanel,
* maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/)
*/
#include "common/memstream.h"
#include "dm/lzw.h"
namespace DM {
LZWdecompressor::LZWdecompressor() {
_repetitionEnabled = false;
_codeBitCount = 0;
_currentMaximumCode = 0;
_absoluteMaximumCode = 4096;
for (int i = 0; i < 12; ++i)
_inputBuffer[i] = 0;
_dictNextAvailableCode = 0;
_dictFlushed = false;
byte leastSignificantBitmasks[9] = {0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF};
for (uint16 i = 0; i < 9; ++i)
_leastSignificantBitmasks[i] = leastSignificantBitmasks[i];
_inputBufferBitIndex = 0;
_inputBufferBitCount = 0;
_charToRepeat = 0;
_tempBuffer = new byte[5004];
_prefixCode = new int16[5003];
_appendCharacter = new byte[5226];
}
LZWdecompressor::~LZWdecompressor() {
delete[] _appendCharacter;
delete[] _prefixCode;
delete[] _tempBuffer;
}
int16 LZWdecompressor::getNextInputCode(Common::MemoryReadStream &inputStream, int32 *inputByteCount) {
byte *inputBuffer = _inputBuffer;
if (_dictFlushed || (_inputBufferBitIndex >= _inputBufferBitCount) || (_dictNextAvailableCode > _currentMaximumCode)) {
if (_dictNextAvailableCode > _currentMaximumCode) {
_codeBitCount++;
if (_codeBitCount == 12) {
_currentMaximumCode = _absoluteMaximumCode;
} else {
_currentMaximumCode = (1 << _codeBitCount) - 1;
}
}
if (_dictFlushed) {
_currentMaximumCode = (1 << (_codeBitCount = 9)) - 1;
_dictFlushed = false;
}
if (*inputByteCount > _codeBitCount) {
_inputBufferBitCount = _codeBitCount;
} else {
_inputBufferBitCount = *inputByteCount;
}
if (_inputBufferBitCount > 0) {
inputStream.read(_inputBuffer, _inputBufferBitCount);
*inputByteCount -= _inputBufferBitCount;
} else {
return -1;
}
_inputBufferBitIndex = 0;
_inputBufferBitCount = (_inputBufferBitCount << 3) - (_codeBitCount - 1);
}
int16 bitIndex = _inputBufferBitIndex;
int16 requiredInputBitCount = _codeBitCount;
inputBuffer += bitIndex >> 3; /* Address of byte in input buffer containing current bit */
bitIndex &= 0x0007; /* Bit index of the current bit in the byte */
int16 nextInputCode = *inputBuffer++ >> bitIndex; /* Get the first bits of the next input code from the input buffer byte */
requiredInputBitCount -= 8 - bitIndex; /* Remaining number of bits to get for a complete input code */
bitIndex = 8 - bitIndex;
if (requiredInputBitCount >= 8) {
nextInputCode |= *inputBuffer++ << bitIndex;
bitIndex += 8;
requiredInputBitCount -= 8;
}
nextInputCode |= (*inputBuffer & _leastSignificantBitmasks[requiredInputBitCount]) << bitIndex;
_inputBufferBitIndex += _codeBitCount;
return nextInputCode;
}
void LZWdecompressor::outputCharacter(byte character, byte **out) {
byte *output = *out;
if (false == _repetitionEnabled) {
if (character == 0x90)
_repetitionEnabled = true;
else
*output++ = _charToRepeat = character;
} else {
if (character) { /* If character following 0x90 is not 0x00 then it is the repeat count */
while (--character)
*output++ = _charToRepeat;
} else /* else output a 0x90 character */
*output++ = 0x90;
_repetitionEnabled = false;
}
*out = output;
return;
}
int32 LZWdecompressor::decompress(Common::MemoryReadStream &inStream, int32 inputByteCount, byte *out) {
byte *reversedDecodedStringStart;
byte *reversedDecodedStringEnd = reversedDecodedStringStart = _tempBuffer;
byte *originalOut = out;
_repetitionEnabled = false;
_codeBitCount = 9;
_dictFlushed = false;
_currentMaximumCode = (1 << (_codeBitCount = 9)) - 1;
for (int16 code = 255; code >= 0; code--) {
_prefixCode[code] = 0;
_appendCharacter[code] = code;
}
_dictNextAvailableCode = 257;
int16 oldCode;
int16 character = oldCode = getNextInputCode(inStream, &inputByteCount);
if (oldCode == -1) {
return -1L;
}
outputCharacter(character, &out);
int16 code;
while ((code = getNextInputCode(inStream, &inputByteCount)) > -1) {
if (code == 256) { /* This code is used to flush the dictionary */
for (int i = 0; i < 256; ++i)
_prefixCode[i] = 0;
_dictFlushed = true;
_dictNextAvailableCode = 256;
if ((code = getNextInputCode(inStream, &inputByteCount)) == -1) {
break;
}
}
/* This code checks for the special STRING+CHARACTER+STRING+CHARACTER+STRING case which generates an undefined code.
It handles it by decoding the last code, adding a single character to the end of the decoded string */
int16 newCode = code;
if (code >= _dictNextAvailableCode) { /* If code is not defined yet */
*reversedDecodedStringEnd++ = character;
code = oldCode;
}
/* Use the string table to decode the string corresponding to the code and store the string in the temporary buffer */
while (code >= 256) {
*reversedDecodedStringEnd++ = _appendCharacter[code];
code = _prefixCode[code];
}
*reversedDecodedStringEnd++ = (character = _appendCharacter[code]);
/* Output the decoded string in reverse order */
do {
outputCharacter(*(--reversedDecodedStringEnd), &out);
} while (reversedDecodedStringEnd > reversedDecodedStringStart);
/* If possible, add a new code to the string table */
if ((code = _dictNextAvailableCode) < _absoluteMaximumCode) {
_prefixCode[code] = oldCode;
_appendCharacter[code] = character;
_dictNextAvailableCode = code + 1;
}
oldCode = newCode;
}
return out - originalOut; /* Byte count of decompressed data */
}
}