/* ScummVM - Scumm Interpreter * Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "common/stdafx.h" #include "common/endian.h" #include "common/config-manager.h" #include "queen/resource.h" namespace Queen { #ifdef PALMOS_68K static ResourceEntry *_resourceTablePEM10; #endif const char *Resource::_tableFilename = "queen.tbl"; const RetailGameVersion Resource::_gameVersions[] = { { "PEM10", 0x00000008, 22677657 }, { "CEM10", 0x0000584E, 190787021 }, { "PFM10", 0x0002CD93, 22157304 }, { "CFM10", 0x00032585, 186689095 }, { "PGM10", 0x00059ACA, 22240013 }, { "CGM10", 0x0005F2A7, 217648975 }, { "PIM10", 0x000866B1, 22461366 }, { "CIM10", 0x0008BEE2, 190795582 }, { "CSM10", 0x000B343C, 190730602 }, { "CHM10", 0x000DA981, 190705558 }, { "PE100", 0x00101EC6, 3724538 }, { "PE100", 0x00102B7F, 3732177 }, { "PEint", 0x00103838, 1915913 }, { "aEM10", 0x00103F1E, 351775 }, { "CE101", 0x00107D8D, 563335 }, { "PE100", 0x001086D4, 597032 } }; static int compareResourceEntry(const void *a, const void *b) { const char *filename = (const char *)a; const ResourceEntry *entry = (const ResourceEntry *)b; return strcmp(filename, entry->filename); } Resource::Resource() : _resourceEntries(0), _resourceTable(NULL) { memset(&_version, 0, sizeof(_version)); if (!_resourceFile.open("queen.1c")) { if (!_resourceFile.open("queen.1")) { error("Could not open resource file 'queen.1[c]'"); } } if (!detectVersion(&_version, &_resourceFile)) { error("Unable to detect game version"); } if (_version.features & GF_REBUILT) { readTableEntries(&_resourceFile); } else { readTableFile(_version.tableOffset); } checkJASVersion(); debug(5, "Detected game version: %s, which has %d resource entries", _version.str, _resourceEntries); } Resource::~Resource() { _resourceFile.close(); if (_resourceTable != _resourceTablePEM10) delete[] _resourceTable; } ResourceEntry *Resource::resourceEntry(const char *filename) const { assert(filename[0] && strlen(filename) < 14); Common::String entryName(filename); entryName.toUppercase(); ResourceEntry *re = NULL; #ifndef PALMOS_MODE re = (ResourceEntry *)bsearch(entryName.c_str(), _resourceTable, _resourceEntries, sizeof(ResourceEntry), compareResourceEntry); #else // PALMOS FIXME (?) : still doesn't work for me (????) use this instead uint32 cur = 0; do { if (!strcmp(entryName.c_str(), _resourceTable[cur].filename)) { re = &_resourceTable[cur]; break; } } while (cur++ < _resourceEntries); #endif return re; } static uint8 emptyBank[] = { 0, 0 }; uint8 *Resource::loadFile(const char *filename, uint32 skipBytes, uint32 *size) { debug(7, "Resource::loadFile('%s')", filename); ResourceEntry *re = resourceEntry(filename); if (_version.platform == Common::kPlatformAmiga && re == NULL) { return emptyBank; } assert(re != NULL); uint32 sz = re->size - skipBytes; if (size != NULL) { *size = sz; } byte *dstBuf = new byte[sz]; if (re->bundle == 1) { _resourceFile.seek(re->offset + skipBytes); _resourceFile.read(dstBuf, sz); } else { Common::File resourceFile; char name[20]; sprintf(name, "queen.%d", re->bundle); if (!resourceFile.open(name)) { error("Could not open resource file '%s'", name); } resourceFile.seek(re->offset + skipBytes); resourceFile.read(dstBuf, sz); } return dstBuf; } bool Resource::detectVersion(DetectedGameVersion *ver, Common::File *f) { memset(ver, 0, sizeof(DetectedGameVersion)); if (f->readUint32BE() == MKID_BE('QTBL')) { f->read(ver->str, 6); f->skip(2); ver->compression = f->readByte(); ver->features = GF_REBUILT; ver->tableOffset = 0; } else { const RetailGameVersion *gameVersion = detectGameVersionFromSize(f->size()); if (gameVersion == NULL) { warning("Unknown/unsupported FOTAQ version"); return false; } strcpy(ver->str, gameVersion->str); ver->compression = COMPRESSION_NONE; ver->features = 0; ver->tableOffset = gameVersion->tableOffset; strcpy(ver->str, gameVersion->str); // Handle game versions for which versionStr information is irrevelant if (gameVersion == &_gameVersions[VER_AMI_DEMO]) { // CE101 ver->features |= GF_FLOPPY | GF_DEMO; ver->platform = Common::kPlatformAmiga; return true; } if (gameVersion == &_gameVersions[VER_AMI_INTERVIEW]) { // PE100 ver->features |= GF_FLOPPY | GF_INTERVIEW; ver->platform = Common::kPlatformAmiga; return true; } } switch (ver->str[1]) { case 'E': if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) { ver->language = Common::RU_RUS; } else { ver->language = Common::EN_ANY; } break; case 'G': ver->language = Common::DE_DEU; break; case 'F': ver->language = Common::FR_FRA; break; case 'I': ver->language = Common::IT_ITA; break; case 'S': ver->language = Common::ES_ESP; break; case 'H': ver->language = Common::HB_ISR; break; default: error("Invalid language id '%c'", ver->str[1]); break; } switch (ver->str[0]) { case 'P': ver->features |= GF_FLOPPY; ver->platform = Common::kPlatformPC; break; case 'C': ver->features |= GF_TALKIE; ver->platform = Common::kPlatformPC; break; case 'a': ver->features |= GF_FLOPPY; ver->platform = Common::kPlatformAmiga; break; default: error("Invalid platform id '%c'", ver->str[0]); break; } if (strcmp(ver->str + 2, "100") == 0 || strcmp(ver->str + 2, "101") == 0) { ver->features |= GF_DEMO; } else if (strcmp(ver->str + 2, "int") == 0) { ver->features |= GF_INTERVIEW; } return true; } void Resource::checkJASVersion() { if (_version.platform == Common::kPlatformAmiga) { warning("Resource::checkJASVersion() disabled for Amiga versions"); return; } ResourceEntry *re = resourceEntry("QUEEN.JAS"); assert(re != NULL && re->bundle == 1); uint32 offset = re->offset; if (isDemo()) offset += JAS_VERSION_OFFSET_DEMO; else if (isInterview()) offset += JAS_VERSION_OFFSET_INTV; else offset += JAS_VERSION_OFFSET_PC; _resourceFile.seek(offset); char versionStr[6]; _resourceFile.read(versionStr, 6); if (strcmp(_version.str, versionStr)) error("Verifying game version failed! (expected: '%s', found: '%s')", _version.str, versionStr); } void Resource::readTableFile(uint32 offset) { Common::File tableFile; tableFile.open(_tableFilename); if (tableFile.isOpen() && tableFile.readUint32BE() == MKID_BE('QTBL')) { if (tableFile.readUint32BE() != CURRENT_TBL_VERSION) { warning("Incorrect version of queen.tbl, please update it"); } tableFile.seek(offset); readTableEntries(&tableFile); } else { // check if it is the english floppy version, for which we have a hardcoded version of the table if (strcmp(_version.str, _gameVersions[VER_ENG_FLOPPY].str) == 0) { _resourceEntries = 1076; _resourceTable = _resourceTablePEM10; } else { error("Could not find tablefile '%s'", _tableFilename); } } } void Resource::readTableEntries(Common::File *file) { _resourceEntries = file->readUint16BE(); _resourceTable = new ResourceEntry[_resourceEntries]; for (uint16 i = 0; i < _resourceEntries; ++i) { ResourceEntry *re = &_resourceTable[i]; file->read(re->filename, 12); re->filename[12] = '\0'; re->bundle = file->readByte(); re->offset = file->readUint32BE(); re->size = file->readUint32BE(); } } const RetailGameVersion *Resource::detectGameVersionFromSize(uint32 size) { for (int i = 0; i < VER_COUNT; ++i) { if (_gameVersions[i].dataFileSize == size) { return &_gameVersions[i]; } } return NULL; } Common::File *Resource::findSound(const char *filename, uint32 *size) { assert(strstr(filename, ".SB") != NULL || strstr(filename, ".AMR") != NULL); ResourceEntry *re = resourceEntry(filename); if (re && re->bundle == 1) { *size = re->size; _resourceFile.seek(re->offset); return &_resourceFile; } return NULL; } LineReader::LineReader(char *buffer, uint32 bufsize) : _buffer(buffer), _bufSize(bufsize), _current(0) { } LineReader::~LineReader() { delete[] _buffer; } char *LineReader::nextLine() { char *startOfLine = _buffer + _current; char *curPos = startOfLine; while (curPos < _buffer + _bufSize && *curPos++ != 0xd) ; *(curPos - 1) = '\0'; // '\r' if (curPos < _buffer + _bufSize) { *curPos = '\0'; // '\n' _current = (curPos - _buffer) + 1; } return startOfLine; } } // End of namespace Queen #ifdef PALMOS_68K #include "scumm_globals.h" _GINIT(Queen_Restables) _GSETPTR(Queen::_resourceTablePEM10, GBVARS_RESOURCETABLEPM10_INDEX, Queen::ResourceEntry, GBVARS_QUEEN) _GEND _GRELEASE(Queen_Restables) _GRELEASEPTR(GBVARS_RESOURCETABLEPM10_INDEX, GBVARS_QUEEN) _GEND #endif