scummvm/engines/glk/zcode/pics_decoder.cpp
2021-12-26 18:48:43 +01:00

168 lines
4.2 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 "glk/zcode/pics_decoder.h"
#include "glk/zcode/pics.h"
#include "common/memstream.h"
namespace Glk {
namespace ZCode {
#define MAX_BIT 512 /* Must be less than or equal to CODE_TABLE_SIZE */
#define CODE_SIZE 8
#define CODE_TABLE_SIZE 4096
#define PREFIX 0
#define PIXEL 1
/**
* Support class used for picture decompression
*/
class Compress {
private:
byte _codeBuffer[CODE_TABLE_SIZE];
public:
short _nextCode;
short _sLen;
short _sPtr;
short _tLen;
short _tPtr;
Compress() : _nextCode(0), _sLen(0), _sPtr(0), _tLen(0), _tPtr(0) {}
/**
* Read a code
*/
short readCode(Common::ReadStream &src);
};
static short MASK[16] = {
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
};
short Compress::readCode(Common::ReadStream &src) {
short code, bsize, tlen, tptr;
code = 0;
tlen = _tLen;
tptr = 0;
while (tlen) {
if (_sLen == 0) {
if ((_sLen = src.read(_codeBuffer, MAX_BIT)) == 0) {
error("fread");
}
_sLen *= 8;
_sPtr = 0;
}
bsize = ((_sPtr + 8) & ~7) - _sPtr;
bsize = (tlen > bsize) ? bsize : tlen;
code |= (((uint)_codeBuffer[_sPtr >> 3] >> (_sPtr & 7)) & MASK[bsize]) << tptr;
tlen -= bsize;
tptr += bsize;
_sLen -= bsize;
_sPtr += bsize;
}
if ((_nextCode == MASK[_tLen]) && (_tLen < 12))
_tLen++;
return code;
}
/*--------------------------------------------------------------------------*/
PictureDecoder::PictureDecoder() {
_tableVal = new byte[3 * 3840];
_tableRef = (uint16 *)(_tableVal + 3840);
}
PictureDecoder::~PictureDecoder() {
delete[] _tableVal;
}
Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint flags,
const Common::Array<byte> &palette, uint display, size_t width, size_t height) {
Common::MemoryWriteStreamDynamic out(DisposeAfterUse::NO);
short code_table[CODE_TABLE_SIZE][2];
byte buffer[CODE_TABLE_SIZE];
// Write out dimensions
out.writeUint16LE(width);
out.writeUint16LE(height);
// Write out palette
out.writeUint16LE(palette.size() / 3 + 2);
for (int idx = 0; idx < 6; ++idx)
out.writeByte((idx < 3) ? 0x77 : 0);
if (!palette.empty())
out.write(&palette[0], palette.size());
byte transparent = 0xff;
if (flags & 1)
transparent = flags >> 12;
out.writeByte(transparent);
int i;
short code, old = 0, first, clear_code;
Compress comp;
clear_code = 1 << CODE_SIZE;
comp._nextCode = clear_code + 2;
comp._tLen = CODE_SIZE + 1;
comp._tPtr = 0;
for (i = 0; i < CODE_TABLE_SIZE; i++) {
code_table[i][PREFIX] = CODE_TABLE_SIZE;
code_table[i][PIXEL] = i;
}
for (;;) {
if ((code = comp.readCode(src)) == (clear_code + 1))
break;
if (code == clear_code) {
comp._tLen = CODE_SIZE + 1;
comp._nextCode = clear_code + 2;
code = comp.readCode(src);
} else {
first = (code == comp._nextCode) ? old : code;
while (code_table[first][PREFIX] != CODE_TABLE_SIZE)
first = code_table[first][PREFIX];
code_table[comp._nextCode][PREFIX] = old;
code_table[comp._nextCode++][PIXEL] = code_table[first][PIXEL];
}
old = code;
i = 0;
do
buffer[i++] = (unsigned char)code_table[code][PIXEL];
while ((code = code_table[code][PREFIX]) != CODE_TABLE_SIZE);
do
out.writeByte(buffer[--i]);
while (i > 0);
}
return new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES);
}
} // End of namespace ZCode
} // End of namespace Glk