Implemented direct loading of rtzcd.dat from the archive rtzcd.red, i.e. the game doesn't have to be installed first to get rtzcd.dat. Also added the respective detection entry.

svn-id: r31661
This commit is contained in:
Benjamin Haisch 2008-04-23 08:08:37 +00:00
parent 18ed600abe
commit 6069dba988
6 changed files with 605 additions and 25 deletions

View File

@ -117,6 +117,16 @@ const char *Object::getString() {
return NULL;
}
void Object::setString(const char *str) {
if (getClass() == 0x7FFF) {
char *objStr = (char*)getData();
if (str)
strncpy(objStr, str, getSize());
else
objStr[0] = '\0';
}
}
bool Object::isObject() {
return getClass() < 0x7FFE;
}
@ -181,31 +191,48 @@ GameDatabase::~GameDatabase() {
}
void GameDatabase::open(const char *filename) {
_fd.open(filename);
debug(1, "GameDatabase::open() Loading from %s", filename);
Common::File fd;
if (!fd.open(filename))
error("GameDatabase::open() Could not open %s", filename);
load(fd);
fd.close();
}
void GameDatabase::openFromRed(const char *redFilename, const char *filename) {
debug(1, "GameDatabase::openFromRed() Loading from %s->%s", redFilename, filename);
Common::MemoryReadStream *fileS = RedReader::loadFromRed(redFilename, filename);
if (!fileS)
error("GameDatabase::openFromRed() Could not load %s from %s", filename, redFilename);
load(*fileS);
delete fileS;
}
void GameDatabase::load(Common::SeekableReadStream &sourceS) {
// TODO: Read/verifiy header
_fd.seek(0x1E);
sourceS.seek(0x1E);
uint32 objectIndexOffs = _fd.readUint32LE();
uint16 objectCount = _fd.readUint16LE();
uint32 gameStateOffs = _fd.readUint32LE();
_gameStateSize = _fd.readUint32LE();
uint32 objectsOffs = _fd.readUint32LE();
uint32 objectsSize = _fd.readUint32LE();
_mainCodeObjectIndex = _fd.readUint16LE();
uint32 objectIndexOffs = sourceS.readUint32LE();
uint16 objectCount = sourceS.readUint16LE();
uint32 gameStateOffs = sourceS.readUint32LE();
_gameStateSize = sourceS.readUint32LE();
uint32 objectsOffs = sourceS.readUint32LE();
uint32 objectsSize = sourceS.readUint32LE();
_mainCodeObjectIndex = sourceS.readUint16LE();
debug(2, "objectIndexOffs = %08X; objectCount = %d; gameStateOffs = %08X; gameStateSize = %d; objectsOffs = %08X; objectsSize = %d\n",
objectIndexOffs, objectCount, gameStateOffs, _gameStateSize, objectsOffs, objectsSize);
_gameState = new byte[_gameStateSize];
_fd.seek(gameStateOffs);
_fd.read(_gameState, _gameStateSize);
sourceS.seek(gameStateOffs);
sourceS.read(_gameState, _gameStateSize);
Common::Array<uint32> objectOffsets;
_fd.seek(objectIndexOffs);
sourceS.seek(objectIndexOffs);
for (uint32 i = 0; i < objectCount; i++)
objectOffsets.push_back(_fd.readUint32LE());
objectOffsets.push_back(sourceS.readUint32LE());
for (uint32 i = 0; i < objectCount; i++) {
Object *obj = new Object();
@ -218,8 +245,8 @@ void GameDatabase::open(const char *filename) {
if (objectOffsets[i] & 1) {
debug(2, "-> const %08X\n", objectsOffs + objectOffsets[i] - 1);
_fd.seek(objectsOffs + objectOffsets[i] - 1);
obj->load(_fd);
sourceS.seek(objectsOffs + objectOffsets[i] - 1);
obj->load(sourceS);
} else {
debug(2, "-> var\n");
obj->load(_gameState + objectOffsets[i]);
@ -253,11 +280,11 @@ int16 *GameDatabase::getObjectPropertyPtr(int16 objectIndex, int16 propertyId, i
while (count2-- > 0) {
if ((READ_LE_UINT16(prop) & 0x3FFF) == propertyId) {
if (READ_LE_UINT16(prop) & 0x4000) {
debug(2, "! L1.1\n");
//debug(2, "! L1.1\n");
propertyFlag = 1;
return (int16*)_gameState + READ_LE_UINT16(propPtr1);
} else {
debug(2, "! L1.2\n");
//debug(2, "! L1.2\n");
propertyFlag = obj->getFlags() & 1;
return propPtr1;
}
@ -269,13 +296,13 @@ int16 *GameDatabase::getObjectPropertyPtr(int16 objectIndex, int16 propertyId, i
// Now check in the object hierarchy of the given object
int16 parentObjectIndex = obj->getClass();
if (parentObjectIndex == 0) {
debug(2, "! NULL(np)\n");
//debug(2, "! NULL(np)\n");
return NULL;
}
while (parentObjectIndex != 0) {
debug(2, "parentObjectIndex = %04X\n", parentObjectIndex);
//debug(2, "parentObjectIndex = %04X\n", parentObjectIndex);
obj = getObject(parentObjectIndex);
@ -290,11 +317,11 @@ int16 *GameDatabase::getObjectPropertyPtr(int16 objectIndex, int16 propertyId, i
if (!(READ_LE_UINT16(prop) & 0x8000)) {
if ((READ_LE_UINT16(prop) & 0x3FFF) == propertyId) {
if (*prop & 0x4000) {
debug(2, "! L2.1\n");
//debug(2, "! L2.1\n");
propertyFlag = 1;
return (int16*)_gameState + READ_LE_UINT16(propPtr1);
} else {
debug(2, "! L2.2\n");
//debug(2, "! L2.2\n");
propertyFlag = obj->getFlags() & 1;
return propPtr1;
}
@ -304,11 +331,11 @@ int16 *GameDatabase::getObjectPropertyPtr(int16 objectIndex, int16 propertyId, i
} else {
if ((READ_LE_UINT16(prop) & 0x3FFF) == propertyId) {
if (*prop & 0x4000) {
debug(2, "! L3.1\n");
//debug(2, "! L3.1\n");
propertyFlag = 1;
return (int16*)_gameState + READ_LE_UINT16(propertyPtr);
} else {
debug(2, "! L3.2\n");
//debug(2, "! L3.2\n");
propertyFlag = obj->getFlags() & 1;
return propertyPtr;
}
@ -322,7 +349,7 @@ int16 *GameDatabase::getObjectPropertyPtr(int16 objectIndex, int16 propertyId, i
}
debug(2, "! NULL(nf)\n");
//debug(2, "! NULL(nf)\n");
return NULL;
}

View File

@ -31,6 +31,8 @@
#include "common/file.h"
#include "common/stream.h"
#include "made/redreader.h"
namespace Made {
class Object {
@ -49,6 +51,7 @@ public:
byte *getData();
const char *getString();
void setString(const char *str);
bool isObject();
bool isVector();
@ -73,6 +76,7 @@ public:
~GameDatabase();
void open(const char *filename);
void openFromRed(const char *redFilename, const char *filename);
Object *getObject(int16 index) const {
if (index >= 1)
@ -93,11 +97,11 @@ public:
void dumpObject(int16 index);
protected:
Common::File _fd;
Common::Array<Object*> _objects;
byte *_gameState;
uint32 _gameStateSize;
int16 _mainCodeObjectIndex;
void load(Common::SeekableReadStream &sourceS);
};
} // End of namespace Made

View File

@ -73,6 +73,8 @@ static const MadeGameDescription gameDescriptions[] = {
{
// Return to Zork - English CD version
// NOTE: This detects the game via the rtzcd.dat which is inside rtzcd.red.
// The entry below detects via rtzcd.red directly, which is the "official" way.
{
"rtz",
"CD",
@ -87,6 +89,22 @@ static const MadeGameDescription gameDescriptions[] = {
0,
},
{
// Return to Zork - English CD version
{
"rtz",
"CD",
AD_ENTRY1("rtzcd.red", "c4e2430e6b6c6ff1562a80fb4a9df24c"),
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
0,
0,
0,
0,
},
{
// Return to Zork - Demo
{

View File

@ -6,6 +6,7 @@ MODULE_OBJS = \
graphics.o \
made.o \
pmvplayer.o \
redreader.o \
resource.o \
screen.o \
script.o \

422
engines/made/redreader.cpp Normal file
View File

@ -0,0 +1,422 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "made/redreader.h"
namespace Made {
Common::MemoryReadStream *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 = new byte[fileEntry.origSize];
LzhDecompressor lzhDec;
lzhDec.decompress(fd, fileBuf, fileEntry.compSize, fileEntry.origSize);
return new Common::MemoryReadStream(fileBuf, fileEntry.origSize, true);
}
Common::MemoryReadStream *RedReader::loadFromRed(const char *redFilename, const char *filename) {
RedReader redReader;
return redReader.load(redFilename, filename);
}
bool RedReader::seekFile(Common::File &fd, FileEntry &fileEntry, const char *filename) {
char arcFilename[13];
while (!fd.eof()) {
fd.skip(8); // skip unknown
fileEntry.compSize = fd.readUint32LE();
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() {
}
LzhDecompressor::~LzhDecompressor() {
}
int LzhDecompressor::decompress(Common::SeekableReadStream &source, byte *dest, uint32 sourceLen, uint32 destLen) {
int bufsize;
byte buffer[DICSIZ];
_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;
}
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(void) {
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\n");
jutbits = 16 - tablebits;
for (i = 1; i <= tablebits; i++) {
start[i] >>= jutbits;
weight[i] = 1U << (tablebits - i);
}
while (i <= 16) 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 */
}
}

108
engines/made/redreader.h Normal file
View File

@ -0,0 +1,108 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef MADE_REDREADER_H
#define MADE_REDREADER_H
#include "common/util.h"
#include "common/file.h"
#include "common/stream.h"
namespace Made {
class RedReader {
public:
Common::MemoryReadStream *load(const char *redFilename, const char *filename);
static Common::MemoryReadStream *loadFromRed(const char *redFilename, const char *filename);
private:
struct FileEntry {
uint32 compSize, origSize;
};
bool seekFile(Common::File &fd, FileEntry &fileEntry, const char *filename);
};
const uint BITBUFSIZ = 16;
const uint DICBIT = 13;
const uint DICSIZ = 1 << DICBIT;
const uint MATCHBIT = 8;
const uint MAXMATCH = 256;
const uint THRESHOLD = 3;
const uint NC = 255 + MAXMATCH + 2 - THRESHOLD;
const uint CBIT = 9;
const uint CODE_BIT = 16;
const uint NP = DICBIT + 1;
const int NT = CODE_BIT + 3;
const uint PBIT = 4;
const uint TBIT = 5;
const uint NPT = NT;
class LzhDecompressor {
public:
LzhDecompressor();
~LzhDecompressor();
int decompress(Common::SeekableReadStream &source, byte *dest, uint32 compSize, uint32 origSize);
private:
Common::SeekableReadStream *_source;
uint32 _compSize, _blockPos;
uint16 _bitbuf;
uint _subbitbuf;
int _bitcount;
uint16 _left[2 * NC - 1], _right[2 * NC - 1];
byte _c_len[NC], _pt_len[NPT];
uint _blocksize;
uint16 _c_table[4096], _pt_table[256];
int tree_n, heapsize;
short heap[NC + 1];
uint16 *freq, *sortptr, len_cnt[17];
byte *len_table;
int decode_i, decode_j;
int count_len_depth;
byte readByte();
void fillbuf(int count);
uint getbits(int count);
void init_getbits(void);
void decode_start(void);
void decode(uint count, byte text[]);
void huf_decode_start(void);
unsigned int decode_c(void);
unsigned int decode_p();
void read_pt_len(int nn, int nbit, int i_special);
void read_c_len();
void count_len(int i);
void make_len(int root);
void downheap(int i);
void make_code(int n, byte len[], uint16 code[]);
void make_table(uint nchar, byte bitlen[], uint tablebits, uint16 table[]);
int make_tree(int nparm, uint16 freqparm[], byte lenparm[], uint16 codeparm[]);
};
} // End of namespace Made
#endif /* MADE_H */