Initial commit
This commit is contained in:
253
engines/neverhood/resourceman.cpp
Normal file
253
engines/neverhood/resourceman.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "neverhood/resourceman.h"
|
||||
|
||||
namespace Neverhood {
|
||||
|
||||
ResourceHandle::ResourceHandle()
|
||||
: _resourceFileEntry(nullptr), _data(nullptr) {
|
||||
}
|
||||
|
||||
ResourceHandle::~ResourceHandle() {
|
||||
}
|
||||
|
||||
ResourceMan::ResourceMan() {
|
||||
}
|
||||
|
||||
ResourceMan::~ResourceMan() {
|
||||
}
|
||||
|
||||
void ResourceMan::addArchive(const Common::Path &filename, bool isOptional) {
|
||||
BlbArchive *archive = new BlbArchive();
|
||||
if (!archive->open(filename, isOptional)) {
|
||||
delete archive;
|
||||
return;
|
||||
}
|
||||
_archives.push_back(archive);
|
||||
debug(3, "ResourceMan::addArchive(%s) %d files", filename.toString(Common::Path::kNativeSeparator).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 (entry->archiveEntry == nullptr || archiveEntry->timeStamp > entry->archiveEntry->timeStamp) {
|
||||
entry->archive = archive;
|
||||
entry->archiveEntry = archiveEntry;
|
||||
}
|
||||
} else {
|
||||
ResourceFileEntry newEntry;
|
||||
newEntry.resourceHandle = -1;
|
||||
newEntry.archive = archive;
|
||||
newEntry.archiveEntry = archiveEntry;
|
||||
newEntry.nhcArchive = nullptr;
|
||||
newEntry.nhcArchiveEntry = nullptr;
|
||||
_entries[archiveEntry->fileHash] = newEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceMan::addNhcArchive(const Common::Path &filename) {
|
||||
NhcArchive *archive = new NhcArchive();
|
||||
if (!archive->open(filename, true)) {
|
||||
delete archive;
|
||||
return false;
|
||||
}
|
||||
_nhcArchives.push_back(archive);
|
||||
debug(3, "ResourceMan::addArchive(%s) %d files", filename.toString(Common::Path::kNativeSeparator).c_str(), archive->getCount());
|
||||
for (uint archiveEntryIndex = 0; archiveEntryIndex < archive->getCount(); archiveEntryIndex++) {
|
||||
NhcArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex);
|
||||
ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash);
|
||||
if (entry) {
|
||||
entry->nhcArchive = archive;
|
||||
entry->nhcArchiveEntry = archiveEntry;
|
||||
} else {
|
||||
ResourceFileEntry newEntry;
|
||||
newEntry.resourceHandle = -1;
|
||||
newEntry.archive = nullptr;
|
||||
newEntry.archiveEntry = nullptr;
|
||||
newEntry.nhcArchive = archive;
|
||||
newEntry.nhcArchiveEntry = archiveEntry;
|
||||
_entries[archiveEntry->fileHash] = newEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceFileEntry *ResourceMan::findEntrySimple(uint32 fileHash) {
|
||||
EntriesMap::iterator p = _entries.find(fileHash);
|
||||
return p != _entries.end() ? &(*p)._value : nullptr;
|
||||
}
|
||||
|
||||
ResourceFileEntry *ResourceMan::findEntry(uint32 fileHash, ResourceFileEntry **firstEntry) {
|
||||
ResourceFileEntry *entry = findEntrySimple(fileHash);
|
||||
if (firstEntry)
|
||||
*firstEntry = entry;
|
||||
for (; entry && entry->archiveEntry != nullptr && entry->archiveEntry->comprType == 0x65; fileHash = entry->archiveEntry->diskSize)
|
||||
entry = findEntrySimple(fileHash);
|
||||
return entry;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ResourceMan::createStream(uint32 fileHash) {
|
||||
ResourceFileEntry *entry = findEntry(fileHash);
|
||||
if (!entry)
|
||||
return nullptr;
|
||||
if (entry->nhcArchiveEntry && entry->nhcArchive && entry->nhcArchiveEntry->isNormal())
|
||||
return entry->nhcArchive->createStream(entry->nhcArchiveEntry);
|
||||
if (entry->archiveEntry && entry->archive)
|
||||
return entry->archive->createStream(entry->archiveEntry);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ResourceMan::createNhcStream(uint32 fileHash, uint32 type) {
|
||||
ResourceFileEntry *entry = findEntry(fileHash);
|
||||
if (!entry)
|
||||
return nullptr;
|
||||
if (entry->nhcArchiveEntry && entry->nhcArchive && entry->nhcArchiveEntry->type == type)
|
||||
return entry->nhcArchive->createStream(entry->nhcArchiveEntry);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ResourceMan::nhcExists(uint32 fileHash, uint32 type) {
|
||||
ResourceFileEntry *entry = findEntry(fileHash);
|
||||
if (!entry)
|
||||
return false;
|
||||
if (entry->nhcArchiveEntry && entry->nhcArchive && entry->nhcArchiveEntry->type == type)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ResourceMan::exists(uint32 fileHash) {
|
||||
ResourceFileEntry *entry = findEntry(fileHash);
|
||||
if (!entry)
|
||||
return false;
|
||||
if (entry->nhcArchiveEntry && entry->nhcArchive && entry->nhcArchiveEntry->isNormal())
|
||||
return true;
|
||||
if (entry->archiveEntry && entry->archive)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceMan::queryResource(uint32 fileHash, ResourceHandle &resourceHandle) {
|
||||
ResourceFileEntry *firstEntry;
|
||||
resourceHandle._resourceFileEntry = findEntry(fileHash, &firstEntry);
|
||||
resourceHandle._extData = firstEntry && firstEntry->archiveEntry ? firstEntry->archiveEntry->extData : nullptr;
|
||||
}
|
||||
|
||||
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, 29191 }, // "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
|
||||
{ 0x0C10B2015, 690410, 5850, 11162, 7870 }, // Menu text
|
||||
{ 0x008C0AC24, 1031009, 3030, 6498, 3646 }, // Overwrite dialog
|
||||
{ 0x0c6604282, 12813649, 19623, 35894, 30370 }, // One of the fonts when reading Willie's notes
|
||||
{ 0x080283101, 13104841, 1961, 3712, 3511 }, // First message from Willie
|
||||
{ 0x058208810, 46010519, 24852, 131874, 114762 }, // Entry to hut with musical lock
|
||||
{ 0x000918480, 17676417, 581, 916, 706 }, // First wall in the museum
|
||||
{ 0x00800090C, 16064875, 19555, 38518, 30263 }, // First wall in the museum
|
||||
{ 0x00008E486, 39600019, 240, 454, 271 }, // Second wall in the museum
|
||||
{ 0x003086004, 39621755, 482, 614, 600 }, // Second wall in the museum
|
||||
{ 0x02008048E, 39611075, 3798, 21089, 6374 }, // Next walls in the museum
|
||||
{ 0x008586283, 39587864, 12155, 29731, 20582 }, // Next walls in the museum
|
||||
{ 0x030A84C80, 39606142, 4933, 16305, 8770 }, // Next walls in the museum
|
||||
{ 0x000C9A480, 39614873, 6882, 23915, 11571 }, // Next walls in the museum
|
||||
{ 0x000098880, 39603114, 3028, 10860, 4762 }, // Next walls in the museum
|
||||
{ 0x040080183, 39600259, 2855, 13400, 4305 }, // Next walls in the museum
|
||||
{ 0x004290188, 39580567, 7297, 27131, 12322 }, // Next walls in the museum
|
||||
{ 0x0283CE401, 12795150, 18499, 36658, 29166 }, // Late-game notes
|
||||
|
||||
// Fixes for the Russian "Fargus" version
|
||||
{ 0x041137051, 758264, 29037, 49590, 49591 }, // "Options" menu header text
|
||||
{ 0x0c10b2015, 787304, 4414, 15848, 15853 }, // Text on option buttons
|
||||
{ 0x006802920, 1076824, 1010, 5642, 1546 }, // Crash in front of the Aqua House
|
||||
//
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
void ResourceMan::loadResource(ResourceHandle &resourceHandle, bool applyResourceFixes) {
|
||||
resourceHandle._data = nullptr;
|
||||
if (resourceHandle.isValid()) {
|
||||
const uint32 fileHash = resourceHandle.fileHash();
|
||||
ResourceData *resourceData = _data[fileHash];
|
||||
if (!resourceData) {
|
||||
resourceData = new ResourceData();
|
||||
_data[fileHash] = resourceData;
|
||||
}
|
||||
if (resourceData->data != nullptr) {
|
||||
resourceData->dataRefCount++;
|
||||
} else {
|
||||
NhcArchiveEntry *nhcEntry = resourceHandle._resourceFileEntry->nhcArchiveEntry;
|
||||
if (nhcEntry && nhcEntry->isNormal()) {
|
||||
resourceData->data = new byte[nhcEntry->size];
|
||||
resourceHandle._resourceFileEntry->nhcArchive->load(nhcEntry, resourceData->data, 0);
|
||||
} 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 = nullptr;
|
||||
resourceHandle._data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Neverhood
|
||||
Reference in New Issue
Block a user