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 "common/debug.h"
24 
25 #include "sludge/fileset.h"
26 #include "sludge/moreio.h"
27 #include "sludge/newfatal.h"
28 #include "sludge/sludge.h"
29 #include "sludge/version.h"
30 
31 namespace Sludge {
32 
ResourceManager()33 ResourceManager::ResourceManager() {
34 	init();
35 }
36 
~ResourceManager()37 ResourceManager::~ResourceManager() {
38 	kill();
39 }
40 
init()41 void ResourceManager::init() {
42 	_sliceBusy = true;
43 	_bigDataFile = nullptr;
44 	_startOfDataIndex = 0;
45 	_startOfTextIndex = 0;
46 	_startOfSubIndex = 0;
47 	_startOfObjectIndex = 0;
48 	_startIndex = 0;
49 	_allResourceNames.clear();
50 }
kill()51 void ResourceManager::kill() {
52 	if (_bigDataFile) {
53 		delete _bigDataFile;
54 		_bigDataFile = nullptr;
55 	}
56 	_allResourceNames.clear();
57 }
58 
openSubSlice(int num)59 bool ResourceManager::openSubSlice(int num) {
60 	if (_sliceBusy) {
61 		fatal("Can't read from data file", "I'm already reading something");
62 		return false;
63 	}
64 	_bigDataFile->seek(_startOfSubIndex + (num << 2), 0);
65 	_bigDataFile->seek(_bigDataFile->readUint32LE(), 0);
66 
67 	return _sliceBusy = true;
68 }
69 
openObjectSlice(int num)70 bool ResourceManager::openObjectSlice(int num) {
71 	if (_sliceBusy) {
72 		fatal("Can't read from data file", "I'm already reading something");
73 		return false;
74 	}
75 
76 	_bigDataFile->seek(_startOfObjectIndex + (num << 2), 0);
77 	_bigDataFile->seek(_bigDataFile->readUint32LE(), 0);
78 	return _sliceBusy = true;
79 }
80 
openFileFromNum(int num)81 uint ResourceManager::openFileFromNum(int num) {
82 	if (_sliceBusy) {
83 		fatal("Can't read from data file", "I'm already reading something");
84 		return 0;
85 	}
86 
87 	_bigDataFile->seek(_startOfDataIndex + (num << 2), 0);
88 	_bigDataFile->seek(_bigDataFile->readUint32LE(), 1);
89 	_sliceBusy = true;
90 
91 	return _bigDataFile->readUint32LE();
92 }
93 
94 uint32 ResourceManager::_cp1250ToUTF32[128] = {
95 	/* 0x80 */
96 	0x20ac, 0xfffd, 0x201a, 0xfffd, 0x201e, 0x2026, 0x2020, 0x2021,
97 	0xfffd, 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
98 	/* 0x90 */
99 	0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
100 	0xfffd, 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
101 	/* 0xa0 */
102 	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
103 	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
104 	/* 0xb0 */
105 	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
106 	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
107 	/* 0xc0 */
108 	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
109 	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
110 	/* 0xd0 */
111 	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
112 	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
113 	/* 0xe0 */
114 	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
115 	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
116 	/* 0xf0 */
117 	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
118 	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
119 };
120 
121 // Converts a string from ISO8859-2 or CP1250 to UTF8.
122 // This is needed for old games.
convertString(const Common::String & s)123 Common::String ResourceManager::convertString(const Common::String &s) {
124 	Common::String res;
125 	Common::U32String tmp;
126 
127 	// Convert CP1250 to UTF32
128 	for (uint i = 0; i < s.size(); ++i) {
129 		const byte c = s[i];
130 		if (c < 0x80) {
131 			tmp += c;
132 		} else {
133 			uint32 utf32 = _cp1250ToUTF32[c - 0x80];
134 			if (utf32) {
135 				tmp += utf32;
136 			} else {
137 				// It's an invalid CP1250 character...
138 				return s;
139 			}
140 		}
141 	}
142 
143 	// Convert UTF32 to UTF8
144 	for (uint i = 0; i < tmp.size(); ++i) {
145 		uint32 wc = tmp[i];
146 		int count;
147 		if (wc < 0x80)
148 			count = 1;
149 		else if (wc < 0x800)
150 			count = 2;
151 		else if (wc < 0x10000) {
152 			if (wc < 0xd800 || wc >= 0xe000) {
153 				count = 3;
154 			} else {
155 				// It's an invalid UTF32 character...
156 				return s;
157 			}
158 		} else if (wc < 0x110000) {
159 			count = 4;
160 		} else {
161 			// It's an invalid UTF32 character...
162 			return s;
163 		}
164 		Common::String r = "";
165 		switch (count) {
166 			case 4:
167 				r = (char)(0x80 | (wc & 0x3f)) + r;
168 				wc = wc >> 6;
169 				wc |= 0x10000;
170 				// falls through
171 			case 3:
172 				r = (char)(0x80 | (wc & 0x3f)) + r;
173 				wc = wc >> 6;
174 				wc |= 0x800;
175 				// falls through
176 			case 2:
177 				r = (char)(0x80 | (wc & 0x3f)) + r;
178 				wc = wc >> 6;
179 				wc |= 0xc0;
180 				// falls through
181 			case 1:
182 				r = wc + r;
183 				break;
184 			default:
185 				break;
186 		}
187 		res += r;
188 	}
189 
190 	return res;
191 }
192 
getNumberedString(int value)193 Common::String ResourceManager::getNumberedString(int value) {
194 	uint32 pos = _bigDataFile->pos();
195 
196 	_bigDataFile->seek((value << 2) + _startOfTextIndex, 0);
197 	value = _bigDataFile->readUint32LE();
198 	_bigDataFile->seek(value, 0);
199 
200 	Common::String s = readString(_bigDataFile);
201 
202 	if (gameVersion < VERSION(2, 2)) {
203 		// This is an older game - We need to convert the string to UTF-8
204 		s = convertString(s);
205 	}
206 
207 	if (_sliceBusy)
208 		_bigDataFile->seek(pos);
209 
210 	return s;
211 }
212 
startAccess()213 bool ResourceManager::startAccess() {
214 	int wasBusy = _sliceBusy;
215 	_sliceBusy = true;
216 	return wasBusy;
217 }
finishAccess()218 void ResourceManager::finishAccess() {
219 	_sliceBusy = false;
220 }
221 
dumpFile(int num,const char * pattern)222 void ResourceManager::dumpFile(int num, const char *pattern) {
223 	if (!g_sludge->_dumpScripts)
224 		return;
225 
226 	Common::DumpFile dumpFile;
227 	dumpFile.open(Common::String("dumps/") + Common::String::format(pattern, num));
228 	uint32 pos = _bigDataFile->pos();
229 
230 	_bigDataFile->seek(_startOfDataIndex + (num << 2), 0);
231 	_bigDataFile->seek(_bigDataFile->readUint32LE(), 1);
232 
233 	uint fsize = _bigDataFile->readUint32LE();
234 
235 	byte *data = (byte *)malloc(fsize);
236 
237 	_bigDataFile->read(data, fsize);
238 	dumpFile.write(data, fsize);
239 	dumpFile.close();
240 
241 	free(data);
242 
243 	_bigDataFile->seek(pos);
244 }
245 
readResourceNames(Common::SeekableReadStream * readStream)246 void ResourceManager::readResourceNames(Common::SeekableReadStream *readStream) {
247 	int numResourceNames = readStream->readUint16BE();
248 	debugC(2, kSludgeDebugDataLoad, "numResourceNames %i", numResourceNames);
249 	_allResourceNames.reserve(numResourceNames);
250 
251 	for (int fn = 0; fn < numResourceNames; fn++) {
252 		_allResourceNames.push_back(readString(readStream));
253 		debugC(2, kSludgeDebugDataLoad, "Resource %i: %s", fn, _allResourceNames[fn].c_str());
254 	}
255 }
256 
resourceNameFromNum(int i)257 const Common::String ResourceManager::resourceNameFromNum(int i) {
258 	if (i == -1)
259 		return "";
260 
261 	if (_allResourceNames.empty())
262 		return "RESOURCE";
263 
264 	if (i < (int)_allResourceNames.size())
265 		return _allResourceNames[i];
266 
267 	return "Unknown resource";
268 }
269 
setData(Common::File * fp)270 void ResourceManager::setData(Common::File *fp) {
271 	_bigDataFile = fp;
272 	_startIndex = fp->pos();
273 }
274 
setFileIndices(uint numLanguages,uint skipBefore)275 void ResourceManager::setFileIndices(uint numLanguages, uint skipBefore) {
276 	_bigDataFile->seek(_startIndex, SEEK_SET);
277 	_sliceBusy = false;
278 
279 	if (skipBefore > numLanguages) {
280 		warning("Not a valid language ID! Using default instead.");
281 		skipBefore = 0;
282 	}
283 
284 	// STRINGS
285 	int skipAfter = numLanguages - skipBefore;
286 	while (skipBefore) {
287 		_bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_SET);
288 		skipBefore--;
289 	}
290 	_startOfTextIndex = _bigDataFile->pos() + 4;
291 	debugC(2, kSludgeDebugDataLoad, "startOfTextIndex: %i", _startOfTextIndex);
292 
293 	_bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_SET);
294 
295 	while (skipAfter) {
296 		_bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_SET);
297 		skipAfter--;
298 	}
299 
300 	_startOfSubIndex = _bigDataFile->pos() + 4;
301 	_bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_CUR);
302 	debugC(2, kSludgeDebugDataLoad, "startOfSubIndex: %i", _startOfSubIndex);
303 
304 	_startOfObjectIndex = _bigDataFile->pos() + 4;
305 	_bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_CUR);
306 	debugC(2, kSludgeDebugDataLoad, "startOfObjectIndex: %i", _startOfObjectIndex);
307 
308 	// Remember that the data section starts here
309 	_startOfDataIndex = _bigDataFile->pos();
310 	debugC(2, kSludgeDebugDataLoad, "startOfDataIndex: %i", _startOfDataIndex);
311 }
312 
313 } // End of namespace Sludge
314