scummvm/engines/m4/resource.cpp
Ori Avtalion 9414d7a6e2 JANITORIAL: Reduce header dependencies in shared code
Some backends may break as I only compiled SDL
2011-04-28 15:08:58 +03:00

534 lines
14 KiB
C++

/* 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 "m4/m4.h"
#include "m4/resource.h"
#include "m4/events.h"
#include "common/substream.h"
#include "common/textconsole.h"
namespace M4 {
FileSystem::FileSystem(const char *hashFilename) {
for (int i = 0; i < 10; i++) {
_hagEntries[i].filename[0] = '\0';
_hagEntries[i].fileIndex = 0; // Was -1
_hagEntries[i].hagFile = 0;
}
Common::File hashFile;
uint32 hashSize;
hashFile.open(hashFilename);
if (!hashFile.isOpen()) {
debugCN(kDebugCore, "FileSystem::FileSystem: error opening hash %s\n", hashFilename);
}
hashSize = hashFile.readUint32LE();
//debugCN(kDebugCore, "FileSystem::FileSystem: hashSize = %d\n", hashSize);
/* load file records and add them to the hash list */
for (uint i = 0; i < hashSize; i++) {
HashFileEntry entry;
hashFile.read(entry.filename, kM4MaxFilenameSize);
str_lower(entry.filename);
entry.hagfile = hashFile.readByte();
hashFile.readByte();
entry.offset = hashFile.readUint32LE();
entry.size = hashFile.readUint32LE();
hashFile.readUint32LE();
if (entry.filename[0]) {
/*
debugCN(kDebugCore, " filename: %s\n", entry.filename);
debugCN(kDebugCore, " hagfile: %d\n", entry.hagfile);
debugCN(kDebugCore, " disks: %d\n", entry.disks);
debugCN(kDebugCore, " offset: %08X\n", entry.offset);
debugCN(kDebugCore, " size: %d\n", entry.size);
debugCN(kDebugCore, " next: %08X\n", entry.next);
*/
_fileEntries[entry.filename] = entry;
}
}
/* load hagfile records and update the list */
while (!hashFile.eos()) {
HashHagEntry entry;
hashFile.read(entry.filename, kM4MaxFilenameSize);
entry.fileIndex = hashFile.readByte();
if (hashFile.eos())
break;
changeExtension(_hagEntries[entry.fileIndex].filename, entry.filename, "HAG");
_hagEntries[entry.fileIndex].fileIndex = entry.fileIndex;
_hagEntries[entry.fileIndex].hagFile = new Common::File();
_hagEntries[entry.fileIndex].hagFile->open(_hagEntries[entry.fileIndex].filename);
if (!_hagEntries[entry.fileIndex].hagFile->isOpen()) {
debugCN(kDebugCore, "FileSystem::FileSystem: error opening hag %s\n", _hagEntries[entry.fileIndex].filename);
}
}
hashFile.close();
}
FileSystem::~FileSystem() {
for (int i = 0; i < 10; i++) {
if (_hagEntries[i].hagFile)
delete _hagEntries[i].hagFile;
}
}
Common::SeekableReadStream *FileSystem::loadFile(const char *resourceName, bool preloadFlag) {
const HashFileEntry *hfe = getHashFileEntry(resourceName);
Common::SeekableReadStream *result = NULL;
if (hfe) {
//debugCN(kDebugCore, "FileSystem::loadFile() success opening %s\n", filename);
HashHagEntry *hagEntry = &_hagEntries[hfe->hagfile];
if (preloadFlag) {
// Creates a MemoryReadStream object that contains all of the resource in memory
hagEntry->hagFile->seek(hfe->offset);
result = _hagEntries[hfe->hagfile].hagFile->readStream(hfe->size);
}
else
// Creates a SeekableSubReadStream, which will read the data in from disk as the
// caller reads in data
result = new Common::SeekableSubReadStream(hagEntry->hagFile, hfe->offset,
hfe->offset + hfe->size);
} else {
debugCN(kDebugCore, "FileSystem::loadFile() error opening %s\n", resourceName);
}
return result;
}
const FileSystem::HashFileEntry *FileSystem::getHashFileEntry(const char *filename) {
char resourceName[20];
strcpy(resourceName, filename);
str_lower(resourceName);
FileHashMap::const_iterator entry = _fileEntries.find(filename);
if (entry != _fileEntries.end())
return &(entry->_value);
else
return NULL;
}
void FileSystem::changeExtension(char *destName, const char *sourceName, const char *extension) {
if (sourceName != destName)
strcpy(destName, sourceName);
char *dot = strrchr(destName, '.');
if (dot != NULL)
*dot = 0;
strcat(destName, ".");
strcat(destName, extension);
str_upper(destName);
}
//--------------------------------------------------------------------------
ResourceManager::~ResourceManager() {
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
delete r->stream;
}
}
Common::SeekableReadStream *ResourceManager::get(const char *resourceName, bool preloadFlag) {
char lowerName[kM4MaxFilenameSize];
strcpy(lowerName, resourceName);
str_lower(lowerName);
// Check whether the resource is already loaded
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
if (!strcmp(r->name, resourceName)) {
// Just in case resource was marked to be purged, reactive it again
r->flags &= ~kResourcePurge;
// Return the existing copy of the resource
r->stream->seek(0, SEEK_SET);
return r->stream;
}
}
// the resource wasn't found in the list, load it from disk
Resource *newRes = new Resource();
strncpy(newRes->name, resourceName, 63);
newRes->name[63] = '\0';
newRes->flags = 0;
newRes->stream = loadResource(resourceName, preloadFlag);
_resources.push_back(ResourceList::value_type(newRes));
return newRes->stream;
}
void ResourceManager::toss(const char *resourceName) {
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
if (!strcmp(r->name, resourceName)) {
r->flags |= kResourcePurge;
//debugCN(kDebugCore, "M4ResourceManager::toss: mark resource %s to be purged\n", resourceName);
}
}
}
void ResourceManager::purge() {
ResourceIterator i = _resources.begin();
while (i != _resources.end()) {
Resource *r = (*i).get();
if (r->flags & kResourcePurge) {
delete r->stream;
i = _resources.erase(i);
} else {
++i;
}
}
}
void ResourceManager::dump() {
_vm->_events->getConsole()->DebugPrintf("Scene resources:\n");
int index = 0;
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
if (!(r->flags & kResourcePurge)) {
_vm->_events->getConsole()->DebugPrintf(
"Resource #%i, name: %s, handle pointer: %p, size: %d, flags: %02X\n",
index++, r->name, r->buffer, r->stream->size(), r->flags);
}
}
}
//--------------------------------------------------------------------------
const char *madsConcatString = "MADSCONCAT";
ResourceType MADSResourceManager::getResourceType(const char *resourceName) {
if (!strncmp(resourceName, "RM", 2)) {
// Room resource
return RESTYPE_ROOM;
} else if (!strncmp(resourceName, "SC", 2)) {
// SC resource
return RESTYPE_SC;
} else if (strstr(resourceName, ".TXT")) {
// Text resource
return RESTYPE_TEXT;
} else if (strstr(resourceName, ".QUO")) {
// QUO resource
return RESTYPE_QUO;
} else if (*resourceName == 'I') {
// I resource
return RESTYPE_I;
} else if (!strncmp(resourceName, "OB", 2)) {
// OB resource
return RESTYPE_OB;
} else if (!strncmp(resourceName, "FONT", 4)) {
// FONT resource
return RESTYPE_FONT;
} else if (!strncmp(resourceName, "SOUND", 5)) {
// SOUND resource
return RESTYPE_SOUND;
} else if (!strncmp(resourceName, "SPCHC", 5)) {
// SPEECH resource
return RESTYPE_SPEECH;
}
// Check for a known extension
const char *extPos = strchr(resourceName, '.');
if (extPos) {
++extPos;
if (!strcmp(extPos, "FL") || !strcmp(extPos, "LBM") || !strcmp(extPos, "ANM") ||
!strcmp(extPos, "AA") || !strcmp(extPos, "SS")) {
return RESTYPE_HAS_EXT;
}
}
return RESTYPE_NO_EXT;
}
const char *MADSResourceManager::getResourceFilename(const char *resourceName) {
static char outputFilename[64];
ResourceType resType = getResourceType(resourceName);
strcpy(outputFilename, "GLOBAL.HAG");
if ((resType == RESTYPE_ROOM) || (resType == RESTYPE_SC)) {
int value = atoi(resourceName + 2);
int hagFileNum = (resType == RESTYPE_ROOM) ? value / 100 : value;
if (hagFileNum > 0)
sprintf(outputFilename, "SECTION%d.HAG", hagFileNum);
}
if (resType == RESTYPE_SPEECH)
strcpy(outputFilename, "SPEECH.HAG");
return outputFilename;
}
/**
* Forms a resource name based on the passed specifiers
*/
const char *MADSResourceManager::getResourceName(char asciiCh, int prefix, ExtensionType extType,
const char *suffix, int index) {
static char resourceName[100];
if (prefix <= 0)
strcpy(resourceName, "*");
else {
if (prefix < 100)
strcpy(resourceName, "*SC");
else
strcpy(resourceName, "*RM");
sprintf(resourceName + 3, "%.3d", prefix);
}
// Append the specified ascii prefix character
char asciiStr[2];
asciiStr[0] = asciiCh;
asciiStr[1] = '\0';
strcat(resourceName, asciiStr);
// Add in the index specified
if (index >= 0)
sprintf(resourceName + strlen(resourceName), "%d", index);
// Add in any suffix
if (suffix)
strcat(resourceName, suffix);
// Handle extension types
switch (extType) {
case EXTTYPE_SS:
strcat(resourceName, ".SS");
break;
case EXTTYPE_AA:
strcat(resourceName, ".AA");
break;
case EXTTYPE_DAT:
strcat(resourceName, ".DAT");
break;
case EXTTYPE_HH:
strcat(resourceName, ".HH");
break;
case EXTTYPE_ART:
strcat(resourceName, ".ART");
break;
case EXTTYPE_INT:
strcat(resourceName, ".INT");
break;
default:
break;
}
return &resourceName[0];
}
/**
* Another variation for forming resource names
*/
const char *MADSResourceManager::getResourceName(ResourcePrefixType prefixType, int idx, const char *extension) {
static char resourceName[100];
strcpy(resourceName, "*");
if (extension) {
switch (prefixType) {
case RESPREFIX_GL:
strcat(resourceName, "GL000");
break;
case RESPREFIX_SC:
case RESPREFIX_RM:
strcat(resourceName, (prefixType == RESPREFIX_SC) ? "SC" : "RM");
sprintf(resourceName + 3, "%.3d", idx);
break;
default:
break;
}
strcat(resourceName, extension);
}
return &resourceName[0];
}
/**
* Forms an AA resource name based on the given passed index
*/
const char *MADSResourceManager::getAAName(int index) {
return getResourceName('I', 0, EXTTYPE_AA, NULL, index);
}
Common::SeekableReadStream *MADSResourceManager::loadResource(const char *resourceName, bool loadFlag) {
Common::File hagFile;
uint32 offset = 0, size = 0;
// If the first character is a '@' then look for an external file
if (*resourceName == '@') {
++resourceName;
hagFile.open(resourceName);
if (loadFlag)
return hagFile.readStream(hagFile.size());
else
return new Common::SeekableSubReadStream(&hagFile, 0, hagFile.size());
}
// If the first character is the wildcard (resource indicator), skip over it
if (*resourceName == '*')
++resourceName;
char resName[20];
strcpy(resName, resourceName);
str_upper(resName);
hagFile.open(getResourceFilename(resName));
// Validate hag file header
char headerBuffer[16];
if ((hagFile.read(headerBuffer, 16) != 16) || (strncmp(headerBuffer, madsConcatString, 10) != 0))
error("Invalid HAG file opened");
int numEntries = hagFile.readUint16LE();
int resIndex = -1;
while (++resIndex < numEntries) {
// Read in the details of the next resource
char resourceBuffer[14];
offset = hagFile.readUint32LE();
size = hagFile.readUint32LE();
hagFile.read(resourceBuffer, 14);
if (!strcmp(resName, resourceBuffer))
break;
}
if (resIndex == numEntries)
error("Invalid resource '%s' specified", resourceName);
// Get the resource, either loading it in it's entirely or getting a stream reference
if (loadFlag) {
hagFile.seek(offset);
return hagFile.readStream(size);
} else {
return new Common::SeekableSubReadStream(&hagFile, offset, offset + size);
}
}
bool MADSResourceManager::resourceExists(const char *resourceName) {
Common::File hagFile;
// If the first character is the wildcard (resource indicator), skip over it
if (*resourceName == '*')
++resourceName;
char resName[20];
strcpy(resName, resourceName);
str_upper(resName);
hagFile.open(getResourceFilename(resName));
// Validate hag file header
char headerBuffer[16];
if ((hagFile.read(headerBuffer, 16) != 16) || (strncmp(headerBuffer, madsConcatString, 10) != 0))
error("Invalid HAG file opened");
int numEntries = hagFile.readUint16LE();
int resIndex = -1;
while (++resIndex < numEntries) {
// Read in the details of the next resource
char resourceBuffer[14];
hagFile.readUint32LE(); // offset
hagFile.readUint32LE(); // size
hagFile.read(resourceBuffer, 14);
if (!strcmp(resName, resourceBuffer))
break;
}
if (resIndex == numEntries)
return false;
else
return true;
}
//--------------------------------------------------------------------------
M4ResourceManager::M4ResourceManager(MadsM4Engine *vm): ResourceManager(vm) {
_hfs = new FileSystem(_vm->getGameFile(kFileTypeHash));
}
M4ResourceManager::~M4ResourceManager() {
}
Common::SeekableReadStream *M4ResourceManager::loadResource(const char *resourceName, bool preloadFlag) {
//debugCN(kDebugCore, "M4ResourceManager::loadResource() loading resource %s\n", resourceName);
Common::SeekableReadStream* result = NULL;
if (_hfs) {
// actually load the resource
result = _hfs->loadFile(resourceName, preloadFlag);
if (!result) {
error("M4ResourceManager::loadResource() Resource %s not found", resourceName);
}
} else {
error("M4ResourceManager::loadResource() No FileSystem attached");
}
return result;
}
bool M4ResourceManager::resourceExists(const char *resourceName) {
return (_hfs->getHashFileEntry(resourceName) != NULL);
}
} // End of namespace M4