scummvm/saga/rscfile.cpp
2005-07-19 21:34:46 +00:00

221 lines
6.0 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004-2005 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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$
*
*/
// RSC Resource file management module
#include "saga/saga.h"
#include "saga/rscfile.h"
#include "saga/stream.h"
namespace Saga {
Resource::Resource(SagaEngine *vm): _vm(vm) {
_contexts = NULL;
_contextsCount = 0;
}
Resource::~Resource() {
clearContexts();
}
bool Resource::loadContext(ResourceContext *context) {
size_t i;
int j;
bool result;
byte tableInfo[RSC_TABLEINFO_SIZE];
uint32 resourceTableOffset;
GamePatchDescription *patchDescription;
ResourceData *resourceData;
byte *tableBuffer;
size_t tableSize;
if (!context->file->open(context->fileName)) {
return false;
}
context->isBigEndian = _vm->isBigEndian();
if (!context->isBigEndian) {
context->isBigEndian = ((_vm->getFeatures() & GF_BIG_ENDIAN_VOICES) != 0) && ((context->fileType & GAME_VOICEFILE) != 0);
}
if (context->file->size() < RSC_MIN_FILESIZE) {
return false;
}
// Read resource table info from the rear end of file
context->file->seek((long)(-RSC_TABLEINFO_SIZE), SEEK_END);
if (context->file->read(tableInfo, RSC_TABLEINFO_SIZE) != RSC_TABLEINFO_SIZE) {
return false;
}
MemoryReadStreamEndian readS(tableInfo, RSC_TABLEINFO_SIZE, context->isBigEndian);
resourceTableOffset = readS.readUint32();
context->count = readS.readUint32();
// Check for sane table offset
if (resourceTableOffset != context->file->size() - RSC_TABLEINFO_SIZE - RSC_TABLEENTRY_SIZE * context->count) {
return false;
}
// Load resource table
tableSize = RSC_TABLEENTRY_SIZE * context->count;
tableBuffer = (byte *)malloc(tableSize);
context->file->seek((long)resourceTableOffset, SEEK_SET);
result = (context->file->read(tableBuffer, tableSize) == tableSize);
if (result) {
context->table = (ResourceData *)calloc(context->count, sizeof(*context->table));
MemoryReadStreamEndian readS1(tableBuffer, tableSize, context->isBigEndian);
for (i = 0; i < context->count; i++) {
resourceData = &context->table[i];
resourceData->offset = readS1.readUint32();
resourceData->size = readS1.readUint32();
//sanity check
if ((resourceData->offset > context->file->size()) || (resourceData->size > context->file->size())) {
result = false;
break;
}
}
}
free(tableBuffer);
//process patch files
if (result) {
for (j = 0; j < _vm->getGameDescription()->patchsCount; j++) {
patchDescription = &_vm->getGameDescription()->patchDescriptions[j];
if ((patchDescription->fileType & context->fileType) != 0) {
if (patchDescription->resourceId < context->count) {
//TODO|fix: should we convert this ID? or make separate patch list for MAC version?
resourceData = &context->table[patchDescription->resourceId];
resourceData->patchFile = new Common::File();
if (resourceData->patchFile->open(patchDescription->fileName)) {
resourceData->offset = 0;
resourceData->size = resourceData->patchFile->size();
} else {
warning("loadContext: patch file not found %s", patchDescription->fileName);
delete resourceData->patchFile;
resourceData->patchFile = NULL;
}
}
}
}
}
return result;
}
bool Resource::createContexts() {
int i, j;
ResourceContext *context;
_contextsCount = _vm->getGameDescription()->filesCount;
_contexts = (ResourceContext*)calloc(_contextsCount, sizeof(*_contexts));
for (i = 0; i < _contextsCount; i++) {
context = &_contexts[i];
context->file = new Common::File();
context->fileName = _vm->getGameDescription()->filesDescriptions[i].fileName;
context->fileType = _vm->getGameDescription()->filesDescriptions[i].fileType;
//self check
for (j = 0; j < i; j++) {
if ((_contexts[j].fileType & context->fileType) != 0) {
error("Resource::createContexts() duplicate fileType");
}
}
if (!loadContext(context)) {
return false;
}
}
return true;
}
void Resource::clearContexts() {
int i;
size_t j;
ResourceContext *context;
if (_contexts == NULL) {
return;
}
for(i = 0; i < _contextsCount; i++) {
context = &_contexts[i];
delete context->file;
if (context->table != NULL) {
for(j = 0; j < context->count; j++) {
delete context->table[j].patchFile;
}
}
free(context->table);
}
free(_contexts);
_contexts = NULL;
}
uint32 Resource::convertResourceId(uint32 resourceId) {
if ((_vm->getGameType() == GType_ITE) && (_vm->getFeatures() & GF_MAC_RESOURCES)) {
if (resourceId > 1537) {
return resourceId - 2;
} else {
if (resourceId == 1535 || resourceId == 1536) {
error ("Wrong resource number %d for Mac ITE", resourceId);
}
}
}
return resourceId;
}
void Resource::loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize) {
Common::File *file;
uint32 resourceOffset;
ResourceData *resourceData;
debug(8, "loadResource %d", resourceId);
resourceData = getResourceData(context, resourceId);
file = context->getFile(resourceData);
resourceOffset = resourceData->offset;
resourceSize = resourceData->size;
resourceBuffer = (byte*)malloc(resourceSize);
file->seek((long)resourceOffset, SEEK_SET);
if (file->read(resourceBuffer, resourceSize) != resourceSize) {
error("Resource::loadResource() failed to read");
}
}
} // End of namespace Saga