Files
scummvm-cursorfix/engines/dgds/decompress.cpp
2026-02-02 04:50:13 +01:00

239 lines
5.1 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "dgds/decompress.h"
namespace Dgds {
uint32 RleDecompressor::decompress(byte *dest, uint32 sz, Common::SeekableReadStream &input) {
uint32 left = sz;
uint32 lenR = 0, lenW = 0;
while (left > 0 && !input.eos()) {
lenR = input.readByte();
if (lenR == 128) {
lenW = 0;
} else if (lenR <= 127) {
lenW = MIN(lenR, left);
for (uint32 j = 0; j < lenW; j++) {
*dest++ = input.readByte();
}
for (; lenR > lenW; lenR--) {
input.readByte();
}
} else {
lenW = MIN(lenR & 0x7F, left);
byte val = input.readByte();
memset(dest, val, lenW);
dest += lenW;
}
left -= lenW;
}
return sz - left;
}
LzwDecompressor::LzwDecompressor() {
reset();
}
void LzwDecompressor::reset() {
memset(&_codeTable, 0, sizeof(_codeTable));
for (uint32 code = 0; code < 256; code++) {
_codeTable[code].len = 1;
_codeTable[code].str[0] = code;
}
_tableSize = 0x101;
_tableMax = 0x200;
_tableFull = false;
_codeSize = 9;
_codeLen = 0;
_cacheBits = 0;
_bitsData = 0;
_bitsSize = 0;
}
uint32 LzwDecompressor::decompress(byte *dest, uint32 sz, Common::SeekableReadStream &input) {
_bitsData = 0;
_bitsSize = 0;
reset();
uint32 idx;
idx = 0;
_cacheBits = 0;
do {
uint32 code;
code = getCode(_codeSize, input);
if (code == 0xFFFFFFFF) break;
_cacheBits += _codeSize;
if (_cacheBits >= _codeSize * 8) {
_cacheBits -= _codeSize * 8;
}
if (code == 0x100) {
if (_cacheBits > 0)
getCode(_codeSize * 8 - _cacheBits, input);
reset();
} else {
if (code >= _tableSize && !_tableFull) {
_codeCur[_codeLen++] = _codeCur[0];
for (uint32 i = 0; i < _codeLen; i++) {
if (idx >= sz)
break;
dest[idx++] = _codeCur[i];
}
} else {
for (uint32 i = 0; i < _codeTable[code].len; i++) {
if (idx >= sz)
break;
dest[idx++] = _codeTable[code].str[i];
}
_codeCur[_codeLen++] = _codeTable[code].str[0];
}
if (_codeLen >= 2) {
if (!_tableFull) {
uint32 i;
if (_tableSize == _tableMax && _codeSize == 12) {
_tableFull = true;
i = _tableSize;
} else {
i = _tableSize++;
_cacheBits = 0;
}
if (_tableSize == _tableMax && _codeSize < 12) {
_codeSize++;
_tableMax <<= 1;
}
for (uint32 j = 0; j < _codeLen; j++) {
_codeTable[i].str[j] = _codeCur[j];
_codeTable[i].len++;
}
}
for (uint32 i = 0; i < _codeTable[code].len; i++)
_codeCur[i] = _codeTable[code].str[i];
_codeLen = _codeTable[code].len;
}
}
} while (idx < sz);
return idx;
}
uint32 LzwDecompressor::getCode(uint32 totalBits, Common::SeekableReadStream &input) {
const byte bitMasks[9] = {
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF
};
uint32 numBits = totalBits;
uint32 useBits = numBits;
uint32 result = 0;
while (numBits > 0) {
if (input.pos() >= input.size())
return 0xFFFFFFFF;
if (_bitsSize == 0) {
_bitsSize = 8;
_bitsData = input.readByte();
}
useBits = numBits;
if (useBits > 8)
useBits = 8;
if (useBits > _bitsSize)
useBits = _bitsSize;
result |= (_bitsData & bitMasks[useBits]) << (totalBits - numBits);
numBits -= useBits;
_bitsSize -= useBits;
_bitsData >>= useBits;
}
return result;
}
Decompressor::Decompressor() {
}
Decompressor::~Decompressor() {
}
byte *Decompressor::decompress(Common::SeekableReadStream *input, int size, uint32 &uncompressedSize) {
byte compression = input->readByte();
uncompressedSize = input->readUint32LE();
byte *data = new byte[uncompressedSize];
uint32 result = 0;
uint32 expectedSize = size;
switch (compression) {
case 0x00:
result = input->read(data, size);
break;
case 0x01:
result = _rleDecompressor.decompress(data, uncompressedSize, *input);
expectedSize = uncompressedSize;
break;
case 0x02:
result = _lzwDecompressor.decompress(data, uncompressedSize, *input);
expectedSize = uncompressedSize;
break;
default:
input->skip(size);
result = size;
warning("Unknown chunk compression: 0x%x", compression);
break;
}
if (result != expectedSize) {
warning("Loading resource with compression type %d - expected %d bytes, got %d",
compression, expectedSize, result);
}
return data;
}
} // End of namespace Dgds