scummvm/engines/neverhood/resourceman.cpp
2014-02-18 02:39:36 +01:00

177 lines
6.6 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 "neverhood/resourceman.h"
namespace Neverhood {
ResourceHandle::ResourceHandle()
: _resourceFileEntry(NULL), _data(NULL) {
}
ResourceHandle::~ResourceHandle() {
}
ResourceMan::ResourceMan() {
}
ResourceMan::~ResourceMan() {
}
void ResourceMan::addArchive(const Common::String &filename) {
BlbArchive *archive = new BlbArchive();
archive->open(filename);
_archives.push_back(archive);
debug(3, "ResourceMan::addArchive(%s) %d files", filename.c_str(), archive->getCount());
for (uint archiveEntryIndex = 0; archiveEntryIndex < archive->getCount(); archiveEntryIndex++) {
BlbArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex);
ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash);
if (entry) {
if (archiveEntry->timeStamp > entry->archiveEntry->timeStamp) {
entry->archive = archive;
entry->archiveEntry = archiveEntry;
}
} else {
ResourceFileEntry newEntry;
newEntry.resourceHandle = -1;
newEntry.archive = archive;
newEntry.archiveEntry = archiveEntry;
_entries[archiveEntry->fileHash] = newEntry;
}
}
}
ResourceFileEntry *ResourceMan::findEntrySimple(uint32 fileHash) {
EntriesMap::iterator p = _entries.find(fileHash);
return p != _entries.end() ? &(*p)._value : NULL;
}
ResourceFileEntry *ResourceMan::findEntry(uint32 fileHash, ResourceFileEntry **firstEntry) {
ResourceFileEntry *entry = findEntrySimple(fileHash);
if (firstEntry)
*firstEntry = entry;
for (; entry && entry->archiveEntry->comprType == 0x65; fileHash = entry->archiveEntry->diskSize)
entry = findEntrySimple(fileHash);
return entry;
}
Common::SeekableReadStream *ResourceMan::createStream(uint32 fileHash) {
ResourceFileEntry *entry = findEntry(fileHash);
return entry ? entry->archive->createStream(entry->archiveEntry) : NULL;
}
void ResourceMan::queryResource(uint32 fileHash, ResourceHandle &resourceHandle) {
ResourceFileEntry *firstEntry;
resourceHandle._resourceFileEntry = findEntry(fileHash, &firstEntry);
resourceHandle._extData = firstEntry ? firstEntry->archiveEntry->extData : NULL;
}
struct EntrySizeFix {
uint32 fileHash;
uint32 offset;
uint32 diskSize;
uint32 size;
uint32 fixedSize;
};
static const EntrySizeFix entrySizeFixes[] = {
// fileHash offset diskSize size fixedSize
// Fixes for the Russian "Dyadyushka Risech" version
{ 0x041137051, 667019, 23391, 41398, 41401 }, // "Options" menu header text
{ 0x00f960021, 402268, 1704, 4378, 1870 }, // "Save" menu
{ 0x01301a7ea, 1220008, 2373, 4146, 2877 }, // "Load" menu
{ 0x084181e81, 201409, 1622, 5058, 1833 }, // "Delete" menu
{ 0x008C0AC24, 1031009, 3030, 6498, 3646 }, // Overwrite dialog
{ 0x0c6604282, 12813649, 19623, 35894, 35895 }, // One of the fonts when reading Willie's notes
{ 0x080283101, 13104841, 1961, 3712, 3511 }, // First message from Willie
{ 0x000918480, 17676417, 581, 916, 706 }, // First wall in the museum
{ 0x00800090C, 16064875, 19555, 38518, 38526 }, // First wall in the museum
{ 0x058208810, 46010519, 24852, 131874, 131776 }, // Entry to hut with musical lock
{ 0x00008E486, 39600019, 240, 454, 271 }, // Second wall in the museum
{ 0x003086004, 39621755, 482, 614, 600 }, // Second wall in the museum
{ 0x02008048E, 39611075, 3798, 21089, 21087 }, // Next couple of walls in the museum
{ 0x008586283, 39587864, 12155, 29731, 29730 }, // Next couple of walls in the museum
{ 0x030A84C80, 39606142, 4933, 16305, 16275 }, // Next couple of walls in the museum
{ 0x000C9A480, 39614873, 6882, 23915, 23913 }, // Next couple of walls in the museum
{ 0x000098880, 39603114, 3028, 10860, 10859 }, // Next couple of walls in the museum
{ 0x040080183, 39600259, 2855, 13400, 13395 }, // Last buggy wall in the museum
// Fixes for the Russian "Fargus" version
{ 0x041137051, 758264, 29037, 49590, 49591 }, // "Options" menu header text
{ 0x0c10b2015, 787304, 4414, 15848, 15853 }, // Text on option buttons
//
{ 0, 0, 0, 0, 0 }
};
void ResourceMan::loadResource(ResourceHandle &resourceHandle, bool applyResourceFixes) {
resourceHandle._data = NULL;
if (resourceHandle.isValid()) {
const uint32 fileHash = resourceHandle.fileHash();
ResourceData *resourceData = _data[fileHash];
if (!resourceData) {
resourceData = new ResourceData();
_data[fileHash] = resourceData;
}
if (resourceData->data != NULL) {
resourceData->dataRefCount++;
} else {
BlbArchiveEntry *entry = resourceHandle._resourceFileEntry->archiveEntry;
// Apply fixes for broken resources in Russian versions
if (applyResourceFixes) {
for (const EntrySizeFix *cur = entrySizeFixes; cur->fileHash > 0; ++cur) {
if (entry->fileHash == cur->fileHash && entry->offset == cur->offset &&
entry->diskSize == cur->diskSize && entry->size == cur->size)
entry->size = cur->fixedSize;
}
}
resourceData->data = new byte[entry->size];
resourceHandle._resourceFileEntry->archive->load(entry, resourceData->data, 0);
resourceData->dataRefCount = 1;
}
resourceHandle._data = resourceData->data;
}
}
void ResourceMan::unloadResource(ResourceHandle &resourceHandle) {
if (resourceHandle.isValid()) {
ResourceData *resourceData = _data[resourceHandle.fileHash()];
if (resourceData && resourceData->dataRefCount > 0)
--resourceData->dataRefCount;
resourceHandle._resourceFileEntry = NULL;
resourceHandle._data = NULL;
}
}
void ResourceMan::purgeResources() {
for (Common::HashMap<uint32, ResourceData*>::iterator it = _data.begin(); it != _data.end(); ++it) {
ResourceData *resourceData = (*it)._value;
if (resourceData->dataRefCount == 0) {
delete[] resourceData->data;
resourceData->data = NULL;
}
}
}
} // End of namespace Neverhood