mirror of
https://github.com/libretro/scummvm.git
synced 2024-11-27 19:30:41 +00:00
449 lines
10 KiB
C++
449 lines
10 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.
|
|
*
|
|
*/
|
|
|
|
#include "made/redreader.h"
|
|
|
|
#include "common/file.h"
|
|
#include "common/memstream.h"
|
|
|
|
namespace Made {
|
|
|
|
Common::SeekableReadStream *RedReader::load(const char *redFilename, const char *filename) {
|
|
|
|
Common::File fd;
|
|
FileEntry fileEntry;
|
|
|
|
if (!fd.open(redFilename))
|
|
error("RedReader::RedReader() Could not open %s", redFilename);
|
|
|
|
if (!seekFile(fd, fileEntry, filename))
|
|
error("RedReader::RedReader() Could not find %s in archive %s", filename, redFilename);
|
|
|
|
byte *fileBuf = (byte *)malloc(fileEntry.origSize);
|
|
|
|
LzhDecompressor* lzhDec = new LzhDecompressor();
|
|
lzhDec->decompress(fd, fileBuf, fileEntry.compSize, fileEntry.origSize);
|
|
delete lzhDec;
|
|
|
|
return new Common::MemoryReadStream(fileBuf, fileEntry.origSize, DisposeAfterUse::YES);
|
|
|
|
}
|
|
|
|
Common::SeekableReadStream *RedReader::loadFromRed(const char *redFilename, const char *filename) {
|
|
RedReader* red = new RedReader();
|
|
Common::SeekableReadStream *stream = red->load(redFilename, filename);
|
|
delete red;
|
|
return stream;
|
|
}
|
|
|
|
bool RedReader::seekFile(Common::File &fd, FileEntry &fileEntry, const char *filename) {
|
|
char arcFilename[13];
|
|
while (true) {
|
|
fd.skip(8); // skip unknown
|
|
fileEntry.compSize = fd.readUint32LE();
|
|
if (fd.eos()) break;
|
|
|
|
fileEntry.origSize = fd.readUint32LE();
|
|
fd.skip(10); // skip unknown
|
|
fd.read(arcFilename, 13);
|
|
fd.skip(2); // skip unknown
|
|
// Check if we have found the file
|
|
if (!scumm_stricmp(arcFilename, filename))
|
|
return true;
|
|
// Skip compressed data
|
|
fd.skip(fileEntry.compSize);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LzhDecompressor::LzhDecompressor() {
|
|
freq = nullptr;
|
|
len_table = nullptr;
|
|
sortptr = nullptr;
|
|
_source = nullptr;
|
|
|
|
_compSize = 0;
|
|
_blockPos = 0;
|
|
_bitbuf = 0;
|
|
_subbitbuf = 0;
|
|
_bitcount = 0;
|
|
_blocksize = 0;
|
|
tree_n = 0;
|
|
heapsize = 0;
|
|
decode_i = 0;
|
|
decode_j = 0;
|
|
count_len_depth = 0;
|
|
}
|
|
|
|
LzhDecompressor::~LzhDecompressor() {
|
|
}
|
|
|
|
int LzhDecompressor::decompress(Common::SeekableReadStream &source, byte *dest, uint32 sourceLen, uint32 destLen) {
|
|
|
|
int bufsize;
|
|
byte* buffer;
|
|
|
|
buffer = (byte *)calloc(DICSIZ, 1);
|
|
|
|
_source = &source;
|
|
_compSize = sourceLen;
|
|
|
|
count_len_depth = 0;
|
|
|
|
_blockPos = 0;
|
|
|
|
decode_start();
|
|
while (destLen > 0) {
|
|
bufsize = ((destLen > DICSIZ) ? DICSIZ : destLen);
|
|
decode(bufsize, buffer);
|
|
memcpy(dest, buffer, bufsize);
|
|
dest += bufsize;
|
|
destLen -= bufsize;
|
|
}
|
|
|
|
free(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
byte LzhDecompressor::readByte() {
|
|
if (_blockPos == 0xFFE) {
|
|
_blockPos = 0;
|
|
_source->skip(2); // skip unknown value
|
|
}
|
|
byte temp = _source->readByte();
|
|
_blockPos++;
|
|
return temp;
|
|
}
|
|
|
|
void LzhDecompressor::fillbuf(int count) {
|
|
_bitbuf <<= count;
|
|
while (count > _bitcount) {
|
|
_bitbuf |= _subbitbuf << (count -= _bitcount);
|
|
if (_compSize != 0) {
|
|
_compSize--;
|
|
_subbitbuf = readByte();
|
|
} else _subbitbuf = 0;
|
|
_bitcount = 8;
|
|
}
|
|
_bitbuf |= _subbitbuf >> (_bitcount -= count);
|
|
}
|
|
|
|
uint LzhDecompressor::getbits(int count) {
|
|
uint x;
|
|
x = _bitbuf >> (BITBUFSIZ - count);
|
|
fillbuf(count);
|
|
return x;
|
|
}
|
|
|
|
void LzhDecompressor::init_getbits() {
|
|
_bitbuf = 0;
|
|
_subbitbuf = 0;
|
|
_bitcount = 0;
|
|
fillbuf(BITBUFSIZ);
|
|
}
|
|
|
|
void LzhDecompressor::decode_start() {
|
|
huf_decode_start();
|
|
decode_j = 0;
|
|
}
|
|
|
|
void LzhDecompressor::decode(uint count, byte buffer[]) {
|
|
uint r, c;
|
|
r = 0;
|
|
while (--decode_j >= 0) {
|
|
buffer[r] = buffer[decode_i];
|
|
decode_i = (decode_i + 1) & (DICSIZ - 1);
|
|
if (++r == count) return;
|
|
}
|
|
for ( ; ; ) {
|
|
c = decode_c();
|
|
if (c <= 255) {
|
|
buffer[r] = c;
|
|
if (++r == count) return;
|
|
} else {
|
|
decode_j = c - (255 + 1 - THRESHOLD);
|
|
decode_i = (r - decode_p() - 1) & (DICSIZ - 1);
|
|
while (--decode_j >= 0) {
|
|
buffer[r] = buffer[decode_i];
|
|
decode_i = (decode_i + 1) & (DICSIZ - 1);
|
|
if (++r == count) return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LzhDecompressor::read_pt_len(int nn, int nbit, int i_special) {
|
|
int i, c, v;
|
|
unsigned int mask;
|
|
v = getbits(nbit);
|
|
if (v == 0) {
|
|
c = getbits(nbit);
|
|
for (i = 0; i < nn; i++) _pt_len[i] = 0;
|
|
for (i = 0; i < 256; i++) _pt_table[i] = c;
|
|
} else {
|
|
i = 0;
|
|
while (i < v) {
|
|
c = _bitbuf >> (BITBUFSIZ - 3);
|
|
if (c == 7) {
|
|
mask = 1U << (BITBUFSIZ - 1 - 3);
|
|
while (mask & _bitbuf) { mask >>= 1; c++; }
|
|
}
|
|
fillbuf((c < 7) ? 3 : c - 3);
|
|
_pt_len[i++] = c;
|
|
if (i == i_special) {
|
|
c = getbits(2);
|
|
while (--c >= 0) _pt_len[i++] = 0;
|
|
}
|
|
}
|
|
while (i < nn) _pt_len[i++] = 0;
|
|
make_table(nn, _pt_len, 8, _pt_table);
|
|
}
|
|
}
|
|
|
|
void LzhDecompressor::read_c_len() {
|
|
uint i, v;
|
|
int c;
|
|
unsigned int mask;
|
|
v = getbits(CBIT);
|
|
if (v == 0) {
|
|
c = getbits(CBIT);
|
|
for (i = 0; i < NC; i++) _c_len[i] = 0;
|
|
for (i = 0; i < 4096; i++) _c_table[i] = c;
|
|
} else {
|
|
i = 0;
|
|
while (i < v) {
|
|
c = _pt_table[_bitbuf >> (BITBUFSIZ - 8)];
|
|
if (c >= NT) {
|
|
mask = 1U << (BITBUFSIZ - 1 - 8);
|
|
do {
|
|
if (_bitbuf & mask) c = _right[c];
|
|
else c = _left [c];
|
|
mask >>= 1;
|
|
} while (c >= NT);
|
|
}
|
|
fillbuf(_pt_len[c]);
|
|
if (c <= 2) {
|
|
if (c == 0) c = 1;
|
|
else if (c == 1) c = getbits(4) + 3;
|
|
else c = getbits(CBIT) + 20;
|
|
while (--c >= 0) _c_len[i++] = 0;
|
|
} else _c_len[i++] = c - 2;
|
|
}
|
|
while (i < NC) _c_len[i++] = 0;
|
|
make_table(NC, _c_len, 12, _c_table);
|
|
}
|
|
}
|
|
|
|
unsigned int LzhDecompressor::decode_c() {
|
|
uint j, mask;
|
|
if (_blocksize == 0) {
|
|
_blocksize = getbits(16);
|
|
read_pt_len(NT, TBIT, 3);
|
|
read_c_len();
|
|
read_pt_len(NP, PBIT, -1);
|
|
}
|
|
_blocksize--;
|
|
j = _c_table[_bitbuf >> (BITBUFSIZ - 12)];
|
|
if (j >= NC) {
|
|
mask = 1U << (BITBUFSIZ - 1 - 12);
|
|
do {
|
|
if (_bitbuf & mask) j = _right[j];
|
|
else j = _left [j];
|
|
mask >>= 1;
|
|
} while (j >= NC);
|
|
}
|
|
fillbuf(_c_len[j]);
|
|
return j;
|
|
}
|
|
|
|
unsigned int LzhDecompressor::decode_p() {
|
|
unsigned int j, mask;
|
|
j = _pt_table[_bitbuf >> (BITBUFSIZ - 8)];
|
|
if (j >= NP) {
|
|
mask = 1U << (BITBUFSIZ - 1 - 8);
|
|
do {
|
|
if (_bitbuf & mask) j = _right[j];
|
|
else j = _left [j];
|
|
mask >>= 1;
|
|
} while (j >= NP);
|
|
}
|
|
fillbuf(_pt_len[j]);
|
|
if (j != 0) j = (1U << (j - 1)) + getbits(j - 1);
|
|
return j;
|
|
}
|
|
|
|
void LzhDecompressor::huf_decode_start() {
|
|
init_getbits();
|
|
_blocksize = 0;
|
|
}
|
|
|
|
void LzhDecompressor::make_table(uint nchar, byte bitlen[], uint tablebits, uint16 table[]) {
|
|
uint16 count[17], weight[17], start[18], *p;
|
|
uint i, k, len, ch, jutbits, avail, nextcode, mask;
|
|
for (i = 1; i <= 16; i++) count[i] = 0;
|
|
for (i = 0; i < nchar; i++) count[bitlen[i]]++;
|
|
start[1] = 0;
|
|
for (i = 1; i <= 16; i++)
|
|
start[i + 1] = start[i] + (count[i] << (16 - i));
|
|
if (start[17] != (uint16)(1U << 16))
|
|
error("LzhDecompressor::make_table() Bad table");
|
|
jutbits = 16 - tablebits;
|
|
for (i = 1; i <= tablebits; i++) {
|
|
start[i] >>= jutbits;
|
|
weight[i] = 1U << (tablebits - i);
|
|
}
|
|
for (; i <= 16; i++) {
|
|
weight[i] = 1U << (16 - i);
|
|
}
|
|
i = start[tablebits + 1] >> jutbits;
|
|
if (i != (uint16)(1U << 16)) {
|
|
k = 1U << tablebits;
|
|
while (i != k) table[i++] = 0;
|
|
}
|
|
avail = nchar;
|
|
mask = 1U << (15 - tablebits);
|
|
for (ch = 0; ch < nchar; ch++) {
|
|
if ((len = bitlen[ch]) == 0) continue;
|
|
nextcode = start[len] + weight[len];
|
|
if (len <= tablebits) {
|
|
for (i = start[len]; i < nextcode; i++) table[i] = ch;
|
|
} else {
|
|
k = start[len];
|
|
p = &table[k >> jutbits];
|
|
i = len - tablebits;
|
|
while (i != 0) {
|
|
if (*p == 0) {
|
|
_right[avail] = _left[avail] = 0;
|
|
*p = avail++;
|
|
}
|
|
if (k & mask) p = &_right[*p];
|
|
else p = &_left[*p];
|
|
k <<= 1; i--;
|
|
}
|
|
*p = ch;
|
|
}
|
|
start[len] = nextcode;
|
|
}
|
|
}
|
|
|
|
/* call with i = root */
|
|
void LzhDecompressor::count_len(int i) {
|
|
if (i < tree_n)
|
|
len_cnt[(count_len_depth < 16) ? count_len_depth : 16]++;
|
|
else {
|
|
count_len_depth++;
|
|
count_len(_left [i]);
|
|
count_len(_right[i]);
|
|
count_len_depth--;
|
|
}
|
|
}
|
|
|
|
void LzhDecompressor::make_len(int root) {
|
|
int i, k;
|
|
uint cum;
|
|
for (i = 0; i <= 16; i++) len_cnt[i] = 0;
|
|
count_len(root);
|
|
cum = 0;
|
|
for (i = 16; i > 0; i--)
|
|
cum += len_cnt[i] << (16 - i);
|
|
while (cum != (1U << 16)) {
|
|
len_cnt[16]--;
|
|
for (i = 15; i > 0; i--) {
|
|
if (len_cnt[i] != 0) {
|
|
len_cnt[i]--;
|
|
len_cnt[i+1] += 2;
|
|
break;
|
|
}
|
|
}
|
|
cum--;
|
|
}
|
|
for (i = 16; i > 0; i--) {
|
|
k = len_cnt[i];
|
|
while (--k >= 0) len_table[*sortptr++] = i;
|
|
}
|
|
}
|
|
|
|
void LzhDecompressor::downheap(int i) {
|
|
int j, k;
|
|
k = heap[i];
|
|
while ((j = 2 * i) <= heapsize) {
|
|
if (j < heapsize && freq[heap[j]] > freq[heap[j + 1]])
|
|
j++;
|
|
if (freq[k] <= freq[heap[j]]) break;
|
|
heap[i] = heap[j]; i = j;
|
|
}
|
|
heap[i] = k;
|
|
}
|
|
|
|
void LzhDecompressor::make_code(int n, byte len[], uint16 code[]) {
|
|
int i;
|
|
uint16 start[18];
|
|
start[1] = 0;
|
|
for (i = 1; i <= 16; i++)
|
|
start[i + 1] = (start[i] + len_cnt[i]) << 1;
|
|
for (i = 0; i < n; i++) code[i] = start[len[i]]++;
|
|
}
|
|
|
|
/* make tree, calculate len[], return root */
|
|
int LzhDecompressor::make_tree(int nparm, uint16 freqparm[], byte lenparm[], uint16 codeparm[]) {
|
|
int i, j, k, avail;
|
|
|
|
tree_n = nparm;
|
|
freq = freqparm;
|
|
len_table = lenparm;
|
|
avail = tree_n;
|
|
heapsize = 0;
|
|
heap[1] = 0;
|
|
for (i = 0; i < tree_n; i++) {
|
|
len_table[i] = 0;
|
|
if (freq[i]) heap[++heapsize] = i;
|
|
}
|
|
if (heapsize < 2) {
|
|
codeparm[heap[1]] = 0;
|
|
return heap[1];
|
|
}
|
|
for (i = heapsize / 2; i >= 1; i--)
|
|
downheap(i); /* make priority queue */
|
|
sortptr = codeparm;
|
|
do { /* while queue has at least two entries */
|
|
i = heap[1]; /* take out least-freq entry */
|
|
if (i < tree_n) *sortptr++ = i;
|
|
heap[1] = heap[heapsize--];
|
|
downheap(1);
|
|
j = heap[1]; /* next least-freq entry */
|
|
if (j < tree_n) *sortptr++ = j;
|
|
k = avail++; /* generate new node */
|
|
freq[k] = freq[i] + freq[j];
|
|
heap[1] = k;
|
|
downheap(1); /* put into queue */
|
|
_left[k] = i;
|
|
_right[k] = j;
|
|
} while (heapsize > 1);
|
|
sortptr = codeparm;
|
|
make_len(k);
|
|
make_code(nparm, lenparm, codeparm);
|
|
return k; /* return root */
|
|
}
|
|
|
|
}
|