/* 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 { 0x41137051, 667019, 23391, 41398, 41401 }, // "Options" menu header text { 0x0f960021, 402268, 1704, 4378, 1870 }, // "Save" menu { 0x1301a7ea, 1220008, 2373, 4146, 2877 }, // "Load" menu { 0x84181e81, 201409, 1622, 5058, 1833 }, // "Delete" menu { 0x08C0AC24, 1031009, 3030, 6498, 3646 }, // Overwrite dialog { 0xc6604282, 12813649, 19623, 35894, 35895 }, // One of the fonts when reading Willie's notes { 0x80283101, 13104841, 1961, 3712, 3511 }, // The first message from Willie { 0x00918480, 17676417, 581, 916, 706 }, // The first wall in the museum { 0x00800090C,16064875, 19555, 38518, 38526 }, // The first wall in the museum // Fixes for the Russian "Fargus" version { 0x41137051, 758264, 29037, 49590, 49591 }, // "Options" menu header text { 0xc10b2015, 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::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