1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "neverhood/resourceman.h"
24
25 namespace Neverhood {
26
ResourceHandle()27 ResourceHandle::ResourceHandle()
28 : _resourceFileEntry(NULL), _data(NULL) {
29 }
30
~ResourceHandle()31 ResourceHandle::~ResourceHandle() {
32 }
33
ResourceMan()34 ResourceMan::ResourceMan() {
35 }
36
~ResourceMan()37 ResourceMan::~ResourceMan() {
38 }
39
addArchive(const Common::String & filename)40 void ResourceMan::addArchive(const Common::String &filename) {
41 BlbArchive *archive = new BlbArchive();
42 archive->open(filename);
43 _archives.push_back(archive);
44 debug(3, "ResourceMan::addArchive(%s) %d files", filename.c_str(), archive->getCount());
45 for (uint archiveEntryIndex = 0; archiveEntryIndex < archive->getCount(); archiveEntryIndex++) {
46 BlbArchiveEntry *archiveEntry = archive->getEntry(archiveEntryIndex);
47 ResourceFileEntry *entry = findEntrySimple(archiveEntry->fileHash);
48 if (entry) {
49 if (archiveEntry->timeStamp > entry->archiveEntry->timeStamp) {
50 entry->archive = archive;
51 entry->archiveEntry = archiveEntry;
52 }
53 } else {
54 ResourceFileEntry newEntry;
55 newEntry.resourceHandle = -1;
56 newEntry.archive = archive;
57 newEntry.archiveEntry = archiveEntry;
58 _entries[archiveEntry->fileHash] = newEntry;
59 }
60 }
61 }
62
findEntrySimple(uint32 fileHash)63 ResourceFileEntry *ResourceMan::findEntrySimple(uint32 fileHash) {
64 EntriesMap::iterator p = _entries.find(fileHash);
65 return p != _entries.end() ? &(*p)._value : NULL;
66 }
67
findEntry(uint32 fileHash,ResourceFileEntry ** firstEntry)68 ResourceFileEntry *ResourceMan::findEntry(uint32 fileHash, ResourceFileEntry **firstEntry) {
69 ResourceFileEntry *entry = findEntrySimple(fileHash);
70 if (firstEntry)
71 *firstEntry = entry;
72 for (; entry && entry->archiveEntry->comprType == 0x65; fileHash = entry->archiveEntry->diskSize)
73 entry = findEntrySimple(fileHash);
74 return entry;
75 }
76
createStream(uint32 fileHash)77 Common::SeekableReadStream *ResourceMan::createStream(uint32 fileHash) {
78 ResourceFileEntry *entry = findEntry(fileHash);
79 return entry ? entry->archive->createStream(entry->archiveEntry) : NULL;
80 }
81
queryResource(uint32 fileHash,ResourceHandle & resourceHandle)82 void ResourceMan::queryResource(uint32 fileHash, ResourceHandle &resourceHandle) {
83 ResourceFileEntry *firstEntry;
84 resourceHandle._resourceFileEntry = findEntry(fileHash, &firstEntry);
85 resourceHandle._extData = firstEntry ? firstEntry->archiveEntry->extData : NULL;
86 }
87
88 struct EntrySizeFix {
89 uint32 fileHash;
90 uint32 offset;
91 uint32 diskSize;
92 uint32 size;
93 uint32 fixedSize;
94 };
95
96 static const EntrySizeFix entrySizeFixes[] = {
97 // fileHash offset diskSize size fixedSize
98 // Fixes for the Russian "Dyadyushka Risech" version
99 { 0x041137051, 667019, 23391, 41398, 29191 }, // "Options" menu header text
100 { 0x00f960021, 402268, 1704, 4378, 1870 }, // "Save" menu
101 { 0x01301a7ea, 1220008, 2373, 4146, 2877 }, // "Load" menu
102 { 0x084181e81, 201409, 1622, 5058, 1833 }, // "Delete" menu
103 { 0x0C10B2015, 690410, 5850, 11162, 7870 }, // Menu text
104 { 0x008C0AC24, 1031009, 3030, 6498, 3646 }, // Overwrite dialog
105 { 0x0c6604282, 12813649, 19623, 35894, 30370 }, // One of the fonts when reading Willie's notes
106 { 0x080283101, 13104841, 1961, 3712, 3511 }, // First message from Willie
107 { 0x058208810, 46010519, 24852, 131874, 114762 }, // Entry to hut with musical lock
108 { 0x000918480, 17676417, 581, 916, 706 }, // First wall in the museum
109 { 0x00800090C, 16064875, 19555, 38518, 30263 }, // First wall in the museum
110 { 0x00008E486, 39600019, 240, 454, 271 }, // Second wall in the museum
111 { 0x003086004, 39621755, 482, 614, 600 }, // Second wall in the museum
112 { 0x02008048E, 39611075, 3798, 21089, 6374 }, // Next walls in the museum
113 { 0x008586283, 39587864, 12155, 29731, 20582 }, // Next walls in the museum
114 { 0x030A84C80, 39606142, 4933, 16305, 8770 }, // Next walls in the museum
115 { 0x000C9A480, 39614873, 6882, 23915, 11571 }, // Next walls in the museum
116 { 0x000098880, 39603114, 3028, 10860, 4762 }, // Next walls in the museum
117 { 0x040080183, 39600259, 2855, 13400, 4305 }, // Next walls in the museum
118 { 0x004290188, 39580567, 7297, 27131, 12322 }, // Next walls in the museum
119 { 0x0283CE401, 12795150, 18499, 36658, 29166 }, // Late-game notes
120
121 // Fixes for the Russian "Fargus" version
122 { 0x041137051, 758264, 29037, 49590, 49591 }, // "Options" menu header text
123 { 0x0c10b2015, 787304, 4414, 15848, 15853 }, // Text on option buttons
124 { 0x006802920, 1076824, 1010, 5642, 1546 }, // Crash in front of the Aqua House
125 //
126 { 0, 0, 0, 0, 0 }
127 };
128
loadResource(ResourceHandle & resourceHandle,bool applyResourceFixes)129 void ResourceMan::loadResource(ResourceHandle &resourceHandle, bool applyResourceFixes) {
130 resourceHandle._data = NULL;
131 if (resourceHandle.isValid()) {
132 const uint32 fileHash = resourceHandle.fileHash();
133 ResourceData *resourceData = _data[fileHash];
134 if (!resourceData) {
135 resourceData = new ResourceData();
136 _data[fileHash] = resourceData;
137 }
138 if (resourceData->data != NULL) {
139 resourceData->dataRefCount++;
140 } else {
141 BlbArchiveEntry *entry = resourceHandle._resourceFileEntry->archiveEntry;
142
143 // Apply fixes for broken resources in Russian versions
144 if (applyResourceFixes) {
145 for (const EntrySizeFix *cur = entrySizeFixes; cur->fileHash > 0; ++cur) {
146 if (entry->fileHash == cur->fileHash && entry->offset == cur->offset &&
147 entry->diskSize == cur->diskSize && entry->size == cur->size)
148 entry->size = cur->fixedSize;
149 }
150 }
151
152 resourceData->data = new byte[entry->size];
153 resourceHandle._resourceFileEntry->archive->load(entry, resourceData->data, 0);
154 resourceData->dataRefCount = 1;
155 }
156 resourceHandle._data = resourceData->data;
157 }
158 }
159
unloadResource(ResourceHandle & resourceHandle)160 void ResourceMan::unloadResource(ResourceHandle &resourceHandle) {
161 if (resourceHandle.isValid()) {
162 ResourceData *resourceData = _data[resourceHandle.fileHash()];
163 if (resourceData && resourceData->dataRefCount > 0)
164 --resourceData->dataRefCount;
165 resourceHandle._resourceFileEntry = NULL;
166 resourceHandle._data = NULL;
167 }
168 }
169
purgeResources()170 void ResourceMan::purgeResources() {
171 for (Common::HashMap<uint32, ResourceData*>::iterator it = _data.begin(); it != _data.end(); ++it) {
172 ResourceData *resourceData = (*it)._value;
173 if (resourceData->dataRefCount == 0) {
174 delete[] resourceData->data;
175 resourceData->data = NULL;
176 }
177 }
178 }
179
180 } // End of namespace Neverhood
181