Initial commit
This commit is contained in:
447
engines/made/redreader.cpp
Normal file
447
engines/made/redreader.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/* 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 "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 */
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user