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