2003-09-28 15:50:47 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
|
|
|
* Copyright (C) 2003 The ScummVM project
|
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* $Header$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "queen/queen.h"
|
|
|
|
#include "queen/resource.h"
|
|
|
|
|
2003-10-03 19:47:41 +00:00
|
|
|
namespace Queen {
|
|
|
|
|
2003-09-28 15:50:47 +00:00
|
|
|
#define DEMO_JAS_VERSION_OFFSET 0x119A8
|
|
|
|
#define JAS_VERSION_OFFSET 0x12484
|
|
|
|
|
2003-10-06 23:04:00 +00:00
|
|
|
static const char *tableFilename = "queen.tbl";
|
|
|
|
|
2003-10-30 23:20:47 +00:00
|
|
|
|
2003-10-06 23:04:00 +00:00
|
|
|
const GameVersion Resource::_gameVersions[] = {
|
2003-10-30 23:20:47 +00:00
|
|
|
{ "PEM10", true, false, 0x00000008, 22677657 },
|
|
|
|
{ "CEM10", false, false, 0x0000584E, 190787021 },
|
|
|
|
{ "PFM10", true, false, 0x0002CD93, 22157304 },
|
|
|
|
{ "CFM10", false, false, 0x00032585, 186689095 },
|
|
|
|
{ "PGM10", true, false, 0x00059ACA, 22240013 },
|
|
|
|
{ "CGM10", false, false, 0x0005F2A7, 217648975 },
|
|
|
|
{ "PIM10", true, false, 0x000866B1, 22461366 },
|
|
|
|
{ "CIM10", false, false, 0x0008BEE2, 190795582 },
|
|
|
|
{ "CSM10", false, false, 0x000B343C, 190730602 },
|
|
|
|
{ "PE100", true, true, 0x000DA981, 3724538 },
|
|
|
|
{ "PE100", true, true, 0x000DB63A, 3732177 }
|
2003-10-06 23:04:00 +00:00
|
|
|
};
|
|
|
|
|
2003-10-30 23:20:47 +00:00
|
|
|
|
2003-10-20 19:18:02 +00:00
|
|
|
Resource::Resource(const Common::String &datafilePath, const char *datafileName)
|
2003-10-14 08:31:56 +00:00
|
|
|
: _JAS2Pos(0), _datafilePath(datafilePath), _resourceEntries(0), _resourceTable(NULL) {
|
2003-09-28 15:50:47 +00:00
|
|
|
|
|
|
|
_resourceFile = new File();
|
2003-10-20 19:18:02 +00:00
|
|
|
_resourceFile->open(datafileName, _datafilePath);
|
2003-09-28 15:50:47 +00:00
|
|
|
if (_resourceFile->isOpen() == false)
|
2003-10-20 19:18:02 +00:00
|
|
|
error("Could not open resource file '%s%s'", _datafilePath.c_str(), datafileName);
|
2003-10-06 23:04:00 +00:00
|
|
|
|
2003-10-20 19:18:02 +00:00
|
|
|
if (_resourceFile->readUint32BE() == 'QTBL') {
|
|
|
|
readTableCompResource();
|
|
|
|
} else {
|
2003-10-21 12:38:01 +00:00
|
|
|
_compression = COMPRESSION_NONE;
|
2003-10-20 19:18:02 +00:00
|
|
|
_gameVersion = detectGameVersion(_resourceFile->size());
|
2003-10-06 23:04:00 +00:00
|
|
|
|
2003-10-20 19:18:02 +00:00
|
|
|
if (!readTableFile()) {
|
|
|
|
//check if it is the english floppy version, for which we have a hardcoded version of the tables
|
|
|
|
if (!strcmp(_gameVersion->versionString, _gameVersions[VER_ENG_FLOPPY].versionString)) {
|
|
|
|
_gameVersion = &_gameVersions[VER_ENG_FLOPPY];
|
|
|
|
_resourceEntries = 1076;
|
|
|
|
_resourceTable = _resourceTablePEM10;
|
|
|
|
} else {
|
|
|
|
error("Couldn't find tablefile '%s%s'", _datafilePath.c_str(), tableFilename);
|
|
|
|
}
|
2003-10-06 23:04:00 +00:00
|
|
|
}
|
2003-09-28 15:50:47 +00:00
|
|
|
}
|
|
|
|
if (strcmp(_gameVersion->versionString, JASVersion()))
|
2003-10-06 23:04:00 +00:00
|
|
|
error("Verifying game version failed! (expected: '%s', found: '%s')", _gameVersion->versionString, JASVersion());
|
2003-09-28 15:50:47 +00:00
|
|
|
|
2003-10-06 23:04:00 +00:00
|
|
|
debug(5, "Detected game version: %s, which has %d resource entries", _gameVersion->versionString, _resourceEntries);
|
2003-10-12 18:44:44 +00:00
|
|
|
|
|
|
|
_JAS2Ptr = (char *)loadFile("QUEEN2.JAS", 0);
|
2003-09-28 15:50:47 +00:00
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
Resource::~Resource() {
|
2003-09-28 15:50:47 +00:00
|
|
|
_resourceFile->close();
|
2003-10-06 23:04:00 +00:00
|
|
|
if(_resourceTable != _resourceTablePEM10)
|
|
|
|
delete[] _resourceTable;
|
2003-10-28 12:42:35 +00:00
|
|
|
delete[] _JAS2Ptr;
|
2003-09-28 15:50:47 +00:00
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
int32 Resource::resourceIndex(const char *filename) {
|
2003-09-28 15:50:47 +00:00
|
|
|
|
|
|
|
char entryName[14];
|
|
|
|
char *ptr = entryName;
|
|
|
|
|
|
|
|
assert(strlen(filename));
|
|
|
|
strcpy(entryName, filename);
|
|
|
|
do
|
|
|
|
*ptr = toupper(*ptr);
|
|
|
|
while (*ptr++);
|
|
|
|
|
|
|
|
uint32 low = 0;
|
2003-10-06 23:04:00 +00:00
|
|
|
uint32 high = _resourceEntries - 1;
|
2003-09-28 15:50:47 +00:00
|
|
|
|
2003-10-06 23:04:00 +00:00
|
|
|
if (!strcmp(entryName, _resourceTable[low].filename))
|
2003-09-28 15:50:47 +00:00
|
|
|
return low;
|
2003-10-06 23:04:00 +00:00
|
|
|
if (!strcmp(entryName, _resourceTable[high].filename))
|
2003-09-28 15:50:47 +00:00
|
|
|
return high;
|
|
|
|
|
|
|
|
|
|
|
|
//Use simple binary search to locate file
|
|
|
|
for(;;) {
|
|
|
|
uint32 cur = (low + high) / 2;
|
2003-10-06 23:04:00 +00:00
|
|
|
int32 diff = strcmp(entryName, _resourceTable[cur].filename);
|
2003-09-28 15:50:47 +00:00
|
|
|
|
|
|
|
if (!diff)
|
|
|
|
return cur;
|
|
|
|
|
|
|
|
if ((cur == low) || (cur == high))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (diff > 0)
|
|
|
|
low = cur;
|
|
|
|
else
|
|
|
|
high = cur;
|
|
|
|
}
|
|
|
|
|
2003-10-16 19:40:29 +00:00
|
|
|
warning("Couldn't find file '%s'", entryName);
|
2003-09-28 15:50:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2003-10-12 18:44:44 +00:00
|
|
|
char *Resource::getJAS2Line() {
|
2003-10-12 19:17:26 +00:00
|
|
|
char *startOfLine = _JAS2Ptr + _JAS2Pos;
|
|
|
|
char *curPos = startOfLine;
|
|
|
|
while (*curPos++ != 0xd) ;
|
|
|
|
*(curPos-1) = '\0'; // '\r'
|
|
|
|
*curPos = '\0'; // '\n'
|
|
|
|
_JAS2Pos = (curPos - _JAS2Ptr) + 1;
|
|
|
|
return startOfLine;
|
2003-10-12 18:44:44 +00:00
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
uint32 Resource::fileSize(const char *filename) {
|
2003-10-06 23:04:00 +00:00
|
|
|
return _resourceTable[resourceIndex(filename)].size;
|
2003-09-28 15:50:47 +00:00
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
uint32 Resource::fileOffset(const char *filename) {
|
2003-10-06 23:04:00 +00:00
|
|
|
return _resourceTable[resourceIndex(filename)].offset;
|
2003-09-28 15:50:47 +00:00
|
|
|
}
|
|
|
|
|
2003-10-13 16:49:53 +00:00
|
|
|
uint8 *Resource::loadFile(const char *filename, uint32 skipBytes, byte *dstBuf) {
|
2003-10-28 13:27:37 +00:00
|
|
|
uint32 size = fileSize(filename) - skipBytes;
|
2003-10-28 12:42:35 +00:00
|
|
|
if (dstBuf == NULL)
|
|
|
|
dstBuf = new byte[size];
|
|
|
|
// skip 'skipBytes' bytes (useful for headers)
|
|
|
|
_resourceFile->seek(fileOffset(filename) + skipBytes, SEEK_SET);
|
2003-10-28 13:27:37 +00:00
|
|
|
_resourceFile->read(dstBuf, size);
|
2003-10-28 12:42:35 +00:00
|
|
|
return dstBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8 *Resource::loadFileMalloc(const char *filename, uint32 skipBytes, byte *dstBuf) {
|
2003-10-28 13:27:37 +00:00
|
|
|
return loadFile(filename, skipBytes, (byte *)malloc(fileSize(filename) - skipBytes));
|
2003-09-28 15:50:47 +00:00
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
bool Resource::exists(const char *filename) {
|
2003-09-29 22:27:08 +00:00
|
|
|
return resourceIndex(filename) >= 0;
|
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
const char *Resource::JASVersion() {
|
2003-09-28 15:50:47 +00:00
|
|
|
static char versionStr[6];
|
|
|
|
if (_gameVersion->isDemo)
|
|
|
|
_resourceFile->seek(fileOffset("QUEEN.JAS") + DEMO_JAS_VERSION_OFFSET, SEEK_SET );
|
|
|
|
else
|
|
|
|
_resourceFile->seek(fileOffset("QUEEN.JAS") + JAS_VERSION_OFFSET, SEEK_SET);
|
|
|
|
_resourceFile->read(versionStr, 6);
|
|
|
|
return versionStr;
|
|
|
|
}
|
|
|
|
|
2003-10-05 16:07:07 +00:00
|
|
|
bool Resource::isDemo() {
|
2003-10-02 14:55:28 +00:00
|
|
|
return _gameVersion->isDemo;
|
|
|
|
}
|
|
|
|
|
2003-10-12 18:44:44 +00:00
|
|
|
bool Resource::isFloppy() {
|
|
|
|
return _gameVersion->isFloppy;
|
|
|
|
}
|
|
|
|
|
|
|
|
Language Resource::getLanguage() {
|
|
|
|
switch (_gameVersion->versionString[1]) {
|
|
|
|
case 'E':
|
|
|
|
return ENGLISH;
|
|
|
|
case 'G':
|
|
|
|
return GERMAN;
|
|
|
|
case 'F':
|
|
|
|
return FRENCH;
|
|
|
|
case 'I':
|
|
|
|
return ITALIAN;
|
2003-10-30 23:20:47 +00:00
|
|
|
case 'S':
|
|
|
|
return SPANISH;
|
2003-10-12 18:44:44 +00:00
|
|
|
default:
|
|
|
|
return ENGLISH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-10-06 23:04:00 +00:00
|
|
|
const GameVersion *Resource::detectGameVersion(uint32 dataFilesize) {
|
|
|
|
//detect game version based on resource file size.
|
|
|
|
//we try to verify that it is indeed the version we think it is later on
|
2003-10-30 23:20:47 +00:00
|
|
|
const GameVersion *pgv = _gameVersions;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < VER_NUMBER; ++i, ++pgv) {
|
|
|
|
if (pgv->dataFileSize == dataFilesize) {
|
|
|
|
return pgv;
|
|
|
|
}
|
2003-10-06 23:04:00 +00:00
|
|
|
}
|
2003-10-30 23:20:47 +00:00
|
|
|
error("Unknown/unsupported FOTAQ version");
|
|
|
|
return NULL;
|
2003-10-06 23:04:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Resource::readTableFile() {
|
|
|
|
File tableFile;
|
|
|
|
tableFile.open(tableFilename, _datafilePath);
|
2003-10-17 14:26:23 +00:00
|
|
|
if (!tableFile.isOpen())
|
|
|
|
tableFile.open(tableFilename, ""); //try current directory
|
2003-10-06 23:04:00 +00:00
|
|
|
if (tableFile.isOpen() && tableFile.readUint32BE() == 'QTBL') {
|
|
|
|
tableFile.seek(_gameVersion->tableOffset);
|
|
|
|
_resourceEntries = tableFile.readUint16BE();
|
|
|
|
_resourceTable = new ResourceEntry[_resourceEntries];
|
|
|
|
ResourceEntry *pre = _resourceTable;
|
|
|
|
for (uint32 i = 0; i < _resourceEntries; ++i, ++pre) {
|
|
|
|
tableFile.read(pre->filename, 12);
|
|
|
|
pre->filename[12] = '\0';
|
|
|
|
pre->inBundle = tableFile.readByte();
|
|
|
|
pre->offset = tableFile.readUint32BE();
|
|
|
|
pre->size = tableFile.readUint32BE();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-10-20 19:18:02 +00:00
|
|
|
void Resource::readTableCompResource() {
|
|
|
|
GameVersion *gv = new GameVersion;
|
|
|
|
_resourceFile->read(gv->versionString, 6);
|
|
|
|
gv->isFloppy = _resourceFile->readByte() != 0;
|
|
|
|
gv->isDemo = _resourceFile->readByte() != 0;
|
|
|
|
_compression = _resourceFile->readByte();
|
|
|
|
_resourceEntries = _resourceFile->readUint16BE();
|
|
|
|
_gameVersion = gv;
|
|
|
|
|
|
|
|
_resourceFile->seek(15, SEEK_SET);
|
|
|
|
_resourceTable = new ResourceEntry[_resourceEntries];
|
|
|
|
ResourceEntry *pre = _resourceTable;
|
|
|
|
for (uint32 i = 0; i < _resourceEntries; ++i, ++pre) {
|
|
|
|
_resourceFile->read(pre->filename, 12);
|
|
|
|
pre->filename[12] = '\0';
|
|
|
|
pre->inBundle = _resourceFile->readByte();
|
|
|
|
pre->offset = _resourceFile->readUint32BE();
|
|
|
|
pre->size = _resourceFile->readUint32BE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-10-28 15:26:05 +00:00
|
|
|
File *Resource::giveCompressedSound(const char *filename) {
|
2003-10-20 19:18:02 +00:00
|
|
|
assert(strstr(filename, ".SB"));
|
|
|
|
_resourceFile->seek(fileOffset(filename), SEEK_SET);
|
|
|
|
return _resourceFile;
|
|
|
|
}
|
|
|
|
|
2003-10-03 19:47:41 +00:00
|
|
|
} // End of namespace Queen
|
2003-10-05 16:07:07 +00:00
|
|
|
|