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