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