mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 14:14:43 +00:00
88913c0139
This tries to make our code a bit more compliant with our code formatting conventions. For future use, this is the command I used: git ls-files "*.cpp" "*.h" | xargs sed -i -e 's/[ \t]*$//'
531 lines
14 KiB
C++
531 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.
|
|
*
|
|
*/
|
|
|
|
#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
|