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/keyboard.h"
25 #include "common/memstream.h"
26 #include "common/punycode.h"
27 #include "common/tokenizer.h"
28 #include "common/zlib.h"
29 
30 #include "graphics/macgui/macwindowmanager.h"
31 #include "graphics/macgui/macfontmanager.h"
32 
33 #include "director/director.h"
34 #include "director/movie.h"
35 #include "director/util.h"
36 
37 namespace Director {
38 
39 static struct MacKeyCodeMapping {
40 	Common::KeyCode scummvm;
41 	int mac;
42 } MackeyCodeMappings[] = {
43 	{ Common::KEYCODE_ESCAPE,		53 },
44 	{ Common::KEYCODE_F1,			122 },
45 	{ Common::KEYCODE_F2,			120 },
46 	{ Common::KEYCODE_F3,			99 },
47 	{ Common::KEYCODE_F4,			118 },
48 	{ Common::KEYCODE_F5,			96 },
49 	{ Common::KEYCODE_F6,			97 },
50 	{ Common::KEYCODE_F7,			98 },
51 	{ Common::KEYCODE_F8,			100 },
52 	{ Common::KEYCODE_F9,			101 },
53 	{ Common::KEYCODE_F10,			109 },
54 	{ Common::KEYCODE_F11,			103 },
55 	{ Common::KEYCODE_F12,			111 },
56 	{ Common::KEYCODE_F13,			105 }, // mirrored by print
57 	{ Common::KEYCODE_F14,			107 }, // mirrored by scroll lock
58 	{ Common::KEYCODE_F15,			113 }, // mirrored by pause
59 
60 	{ Common::KEYCODE_BACKQUOTE,	10 },
61 	{ Common::KEYCODE_1,			18 },
62 	{ Common::KEYCODE_2,			19 },
63 	{ Common::KEYCODE_3,			20 },
64 	{ Common::KEYCODE_4,			21 },
65 	{ Common::KEYCODE_5,			23 },
66 	{ Common::KEYCODE_6,			22 },
67 	{ Common::KEYCODE_7,			26 },
68 	{ Common::KEYCODE_8,			28 },
69 	{ Common::KEYCODE_9,			25 },
70 	{ Common::KEYCODE_0,			29 },
71 	{ Common::KEYCODE_MINUS,		27 },
72 	{ Common::KEYCODE_EQUALS,		24 },
73 	{ Common::KEYCODE_BACKSPACE,	51 },
74 
75 	{ Common::KEYCODE_TAB,			48 },
76 	{ Common::KEYCODE_q,			12 },
77 	{ Common::KEYCODE_w,			13 },
78 	{ Common::KEYCODE_e,			14 },
79 	{ Common::KEYCODE_r,			15 },
80 	{ Common::KEYCODE_t,			17 },
81 	{ Common::KEYCODE_y,			16 },
82 	{ Common::KEYCODE_u,			32 },
83 	{ Common::KEYCODE_i,			34 },
84 	{ Common::KEYCODE_o,			31 },
85 	{ Common::KEYCODE_p,			35 },
86 	{ Common::KEYCODE_LEFTBRACKET,	33 },
87 	{ Common::KEYCODE_RIGHTBRACKET,	30 },
88 	{ Common::KEYCODE_BACKSLASH,	42 },
89 
90 	{ Common::KEYCODE_CAPSLOCK,		57 },
91 	{ Common::KEYCODE_a,			0 },
92 	{ Common::KEYCODE_s,			1 },
93 	{ Common::KEYCODE_d,			2 },
94 	{ Common::KEYCODE_f,			3 },
95 	{ Common::KEYCODE_g,			5 },
96 	{ Common::KEYCODE_h,			4 },
97 	{ Common::KEYCODE_j,			38 },
98 	{ Common::KEYCODE_k,			40 },
99 	{ Common::KEYCODE_l,			37 },
100 	{ Common::KEYCODE_SEMICOLON,	41 },
101 	{ Common::KEYCODE_QUOTE,		39 },
102 	{ Common::KEYCODE_RETURN,		36 },
103 
104 	{ Common::KEYCODE_LSHIFT,		56 },
105 	{ Common::KEYCODE_z,			6 },
106 	{ Common::KEYCODE_x,			7 },
107 	{ Common::KEYCODE_c,			8 },
108 	{ Common::KEYCODE_v,			9 },
109 	{ Common::KEYCODE_b,			11 },
110 	{ Common::KEYCODE_n,			45 },
111 	{ Common::KEYCODE_m,			46 },
112 	{ Common::KEYCODE_COMMA,		43 },
113 	{ Common::KEYCODE_PERIOD,		47 },
114 	{ Common::KEYCODE_SLASH,		44 },
115 	{ Common::KEYCODE_RSHIFT,		56 },
116 
117 	{ Common::KEYCODE_LCTRL,		54 }, // control
118 	{ Common::KEYCODE_LALT,			58 }, // option
119 	{ Common::KEYCODE_LSUPER,		55 }, // command
120 	{ Common::KEYCODE_SPACE,		49 },
121 	{ Common::KEYCODE_RSUPER,		55 }, // command
122 	{ Common::KEYCODE_RALT,			58 }, // option
123 	{ Common::KEYCODE_RCTRL,		54 }, // control
124 
125 	{ Common::KEYCODE_LEFT,			123 },
126 	{ Common::KEYCODE_RIGHT,		124 },
127 	{ Common::KEYCODE_DOWN,			125 },
128 	{ Common::KEYCODE_UP,			126 },
129 
130 	{ Common::KEYCODE_NUMLOCK,		71 },
131 	{ Common::KEYCODE_KP_EQUALS,	81 },
132 	{ Common::KEYCODE_KP_DIVIDE,	75 },
133 	{ Common::KEYCODE_KP_MULTIPLY,	67 },
134 
135 	{ Common::KEYCODE_KP7,			89 },
136 	{ Common::KEYCODE_KP8,			91 },
137 	{ Common::KEYCODE_KP9,			92 },
138 	{ Common::KEYCODE_KP_MINUS,		78 },
139 
140 	{ Common::KEYCODE_KP4,			86 },
141 	{ Common::KEYCODE_KP5,			87 },
142 	{ Common::KEYCODE_KP6,			88 },
143 	{ Common::KEYCODE_KP_PLUS,		69 },
144 
145 	{ Common::KEYCODE_KP1,			83 },
146 	{ Common::KEYCODE_KP2,			84 },
147 	{ Common::KEYCODE_KP3,			85 },
148 
149 	{ Common::KEYCODE_KP0,			82 },
150 	{ Common::KEYCODE_KP_PERIOD,	65 },
151 	{ Common::KEYCODE_KP_ENTER,		76 },
152 
153 	{ Common::KEYCODE_MENU,			50 }, // international
154 	{ Common::KEYCODE_PRINT,		105 }, // mirrored by F13
155 	{ Common::KEYCODE_SCROLLOCK,	107 }, // mirrored by F14
156 	{ Common::KEYCODE_PAUSE,		113 }, // mirrored by F15
157 	{ Common::KEYCODE_INSERT,		114 },
158 	{ Common::KEYCODE_HOME,			115 },
159 	{ Common::KEYCODE_PAGEUP,		116 },
160 	{ Common::KEYCODE_DELETE,		117 },
161 	{ Common::KEYCODE_END,			119 },
162 	{ Common::KEYCODE_PAGEDOWN,		121 },
163 
164 	{ Common::KEYCODE_INVALID,		0 }
165 };
166 
167 static struct WinKeyCodeMapping {
168 	Common::KeyCode scummvm;
169 	int win;
170 } WinkeyCodeMappings[] = {
171 	{ Common::KEYCODE_BACKSPACE,	0x08 },
172 	{ Common::KEYCODE_TAB,			0x09 },
173 	{ Common::KEYCODE_CAPSLOCK,		0x14 },
174 
175 	{ Common::KEYCODE_SPACE,		0x20 },
176 	{ Common::KEYCODE_MENU,			0x12 },
177 	{ Common::KEYCODE_CANCEL, 		0x03 },
178 	{ Common::KEYCODE_RETURN,		0x0D },
179 	{ Common::KEYCODE_PAUSE,		0x13 },
180 	{ Common::KEYCODE_CAPSLOCK,		0x14 },
181 	{ Common::KEYCODE_PRINT,		0x2A },
182 
183 	{ Common::KEYCODE_0,			0x30 },
184 	{ Common::KEYCODE_1,			0x31 },
185 	{ Common::KEYCODE_2,			0x32 },
186 	{ Common::KEYCODE_3,			0x33 },
187 	{ Common::KEYCODE_4,			0x34 },
188 	{ Common::KEYCODE_5,			0x35 },
189 	{ Common::KEYCODE_6,			0x36 },
190 	{ Common::KEYCODE_7,			0x37 },
191 	{ Common::KEYCODE_8,			0x38 },
192 	{ Common::KEYCODE_9,			0x39 },
193 
194 	{ Common::KEYCODE_a,			0x41 },
195 	{ Common::KEYCODE_b,			0x42 },
196 	{ Common::KEYCODE_c,			0x43 },
197 	{ Common::KEYCODE_d,			0x44 },
198 	{ Common::KEYCODE_e,			0x45 },
199 	{ Common::KEYCODE_f,			0x46 },
200 	{ Common::KEYCODE_g,			0x47 },
201 	{ Common::KEYCODE_h,			0x48 },
202 	{ Common::KEYCODE_i,			0x49 },
203 	{ Common::KEYCODE_j,			0x4A },
204 	{ Common::KEYCODE_k,			0x4B },
205 	{ Common::KEYCODE_l,			0x4C },
206 	{ Common::KEYCODE_m,			0x4D },
207 	{ Common::KEYCODE_n,			0x4E },
208 	{ Common::KEYCODE_o,			0x4F },
209 	{ Common::KEYCODE_p,			0x50 },
210 	{ Common::KEYCODE_q,			0x51 },
211 	{ Common::KEYCODE_r,			0x52 },
212 	{ Common::KEYCODE_s,			0x53 },
213 	{ Common::KEYCODE_t,			0x54 },
214 	{ Common::KEYCODE_u,			0x55 },
215 	{ Common::KEYCODE_v,			0x56 },
216 	{ Common::KEYCODE_w,			0x57 },
217 	{ Common::KEYCODE_x,			0x58 },
218 	{ Common::KEYCODE_y,			0x59 },
219 	{ Common::KEYCODE_z,			0x5A },
220 
221 	{ Common::KEYCODE_KP0,			0x60 },
222 	{ Common::KEYCODE_KP1,			0x61 },
223 	{ Common::KEYCODE_KP2,			0x62 },
224 	{ Common::KEYCODE_KP3,			0x63 },
225 	{ Common::KEYCODE_KP4,			0x64 },
226 	{ Common::KEYCODE_KP5,			0x65 },
227 	{ Common::KEYCODE_KP6,			0x66 },
228 	{ Common::KEYCODE_KP7,			0x67 },
229 	{ Common::KEYCODE_KP8,			0x68 },
230 	{ Common::KEYCODE_KP9,			0x69 },
231 
232 	{ Common::KEYCODE_KP_PLUS,		0x6B },
233 	{ Common::KEYCODE_KP_MULTIPLY,	0x6A },
234 	{ Common::KEYCODE_KP_DIVIDE,	0x6F },
235 	{ Common::KEYCODE_KP_MINUS,		0x6D },
236 
237 	{ Common::KEYCODE_F1,			0x70 },
238 	{ Common::KEYCODE_F2,			0x71 },
239 	{ Common::KEYCODE_F3,			0x72 },
240 	{ Common::KEYCODE_F4,			0x73 },
241 	{ Common::KEYCODE_F5,			0x74 },
242 	{ Common::KEYCODE_F6,			0x75 },
243 	{ Common::KEYCODE_F7,			0x76 },
244 	{ Common::KEYCODE_F8,			0x77 },
245 	{ Common::KEYCODE_F9,			0x78 },
246 	{ Common::KEYCODE_F10,			0x79 },
247 	{ Common::KEYCODE_F11,			0x7A },
248 	{ Common::KEYCODE_F12,			0x7B },
249 	{ Common::KEYCODE_F13,			0x7C },
250 	{ Common::KEYCODE_F14,			0x7D },
251 	{ Common::KEYCODE_F15,			0x7E },
252 	{ Common::KEYCODE_F16,			0x7F },
253 	{ Common::KEYCODE_F17,			0x80 },
254 	{ Common::KEYCODE_F18,			0x81 },
255 
256 	{ Common::KEYCODE_LEFT,			0x25 },
257 	{ Common::KEYCODE_RIGHT,		0x27 },
258 	{ Common::KEYCODE_DOWN,			0x28 },
259 	{ Common::KEYCODE_UP,			0x26 },
260 
261 	{ Common::KEYCODE_NUMLOCK,		0x90 },
262 	{ Common::KEYCODE_SCROLLOCK,	0x91 },
263 	{ Common::KEYCODE_SLEEP,		0x5F },
264 	{ Common::KEYCODE_INSERT,		0x2D },
265 	{ Common::KEYCODE_HELP,			0x2F },
266 	{ Common::KEYCODE_SELECT, 		0x29 },
267 	{ Common::KEYCODE_HOME,			0x24 },
268 	{ Common::KEYCODE_PRINT,		0x2A },
269 	{ Common::KEYCODE_ESCAPE, 		0x18 },
270 	{ Common::KEYCODE_PAGEUP,		0x21 },
271 	{ Common::KEYCODE_DELETE,		0x2E },
272 	{ Common::KEYCODE_END,			0x23 },
273 	{ Common::KEYCODE_PAGEDOWN,		0x22 },
274 
275 	{ Common::KEYCODE_INVALID,		0 }
276 };
277 
loadKeyCodes()278 void DirectorEngine::loadKeyCodes() {
279 	if (g_director->getPlatform() == Common::kPlatformWindows) {
280 		for (WinKeyCodeMapping *k = WinkeyCodeMappings; k->scummvm != Common::KEYCODE_INVALID; k++)
281 			_KeyCodes[k->scummvm] = k->win;
282 	} else {
283 		for (MacKeyCodeMapping *k = MackeyCodeMappings; k->scummvm != Common::KEYCODE_INVALID; k++)
284 			_KeyCodes[k->scummvm] = k->mac;
285 	}
286 }
287 
castNumToNum(const char * str)288 int castNumToNum(const char *str) {
289 	if (strlen(str) != 3)
290 		return -1;
291 
292 	if (tolower(str[0]) >= 'a' && tolower(str[0]) <= 'h' &&
293 		str[1] >= '1' && str[1] <= '8' &&
294 		str[2] >= '1' && str[2] <= '8') {
295 
296 		return (tolower(str[0]) - 'a') * 64 + (str[1] - '1') * 8 + (str[2] - '1') + 1;
297 	}
298 
299 	return -1;
300 }
301 
numToCastNum(int num)302 char *numToCastNum(int num) {
303 	static char res[4];
304 
305 	res[0] = res[1] = res[2] = '?';
306 	res[3] = '\0';
307 	num--;
308 
309 	if (num >= 0 && num < 512) {
310 		int c = num / 64;
311 		res[0] = 'A' + c;
312 		num -= 64 * c;
313 
314 		c =  num / 8;
315 		res[1] = '1' + c;
316 		num -= 8 * c;
317 
318 		res[2] = '1' + num;
319 	}
320 
321 	return res;
322 }
323 
asString() const324 Common::String CastMemberID::asString() const {
325 	Common::String res = Common::String::format("member %d", member);
326 
327 	if (g_director->getVersion() < 400 || g_director->getCurrentMovie()->_allowOutdatedLingo)
328 		res += "(" + Common::String(numToCastNum(member)) + ")";
329 	else if (g_director->getVersion() >= 500)
330 		res += Common::String::format(" of castLib %d", castLib);
331 
332 	return res;
333 }
334 
convertPath(Common::String & path)335 Common::String convertPath(Common::String &path) {
336 	if (path.empty())
337 		return path;
338 
339 	if (!path.contains(':') && !path.contains('\\') && !path.contains('@')) {
340 		return path;
341 	}
342 
343 	Common::String res;
344 	uint32 idx = 0;
345 
346 	if (path.hasPrefix("::")) { // Parent directory
347 		idx = 2;
348 	} else if (path.hasPrefix("@:")) { // Root of the game
349 		idx = 2;
350 	} else if (path.size() >= 3
351 					&& Common::isAlpha(path[0])
352 					&& path[1] == ':'
353 					&& path[2] == '\\') { // Windows drive letter
354 		idx = 3;
355 	} else if (path[0] == ':') {
356 		idx = 1;
357 	}
358 
359 	while (idx < path.size()) {
360 		if (path[idx] == ':' || path[idx] == '\\')
361 			res += g_director->_dirSeparator;
362 		else
363 			res += path[idx];
364 
365 		idx++;
366 	}
367 
368 	return res;
369 }
370 
unixToMacPath(const Common::String & path)371 Common::String unixToMacPath(const Common::String &path) {
372 	Common::String res;
373 	for (uint32 idx = 0; idx < path.size(); idx++) {
374 		if (path[idx] == ':')
375 			res += '/';
376 		else if (path[idx] == '/')
377 			res += ':';
378 		else
379 			res += path[idx];
380 	}
381 	return res;
382 }
383 
getPath(Common::String path,Common::String cwd)384 Common::String getPath(Common::String path, Common::String cwd) {
385 	const char *s;
386 	if ((s = strrchr(path.c_str(), g_director->_dirSeparator))) {
387 		return Common::String(path.c_str(), s + 1);
388 	}
389 
390 	return cwd; // The path is not altered
391 }
392 
testPath(Common::String & path,bool directory)393 bool testPath(Common::String &path, bool directory) {
394 	if (directory) {
395 		Common::FSNode d = Common::FSNode(*g_director->getGameDataDir());
396 
397 		// check for the game data dir
398 		if (!path.contains(g_director->_dirSeparator) && path.equalsIgnoreCase(d.getName())) {
399 			path = "";
400 			return true;
401 		}
402 
403 		Common::StringTokenizer directory_list(path, Common::String(g_director->_dirSeparator));
404 
405 		if (d.getChild(directory_list.nextToken()).exists()) {
406 			// then this part is for the "relative to current directory"
407 			// we find the child directory recursively
408 			directory_list.reset();
409 			while (!directory_list.empty() && d.exists())
410 				d = d.getChild(directory_list.nextToken());
411 		} else {
412 			return false;
413 		}
414 
415 		return d.exists();
416 	}
417 
418 	Common::File f;
419 	if (f.open(Common::Path(path, g_director->_dirSeparator))) {
420 		if (f.size())
421 			return true;
422 		f.close();
423 	}
424 	return false;
425 }
426 
427 // if we are finding the file path, then this func will return exactly the executable file path
428 // if we are finding the directory path, then we will get the path relative to the game data dir.
429 // e.g. if we have game data dir as SSwarlock, then "A:SSwarlock" -> "", "A:SSwarlock:Nav" -> "Nav"
pathMakeRelative(Common::String path,bool recursive,bool addexts,bool directory)430 Common::String pathMakeRelative(Common::String path, bool recursive, bool addexts, bool directory) {
431 	Common::String initialPath(path);
432 
433 	if (recursive) // first level
434 		initialPath = convertPath(initialPath);
435 
436 	debug(9, "pathMakeRelative(): s1 %s -> %s", path.c_str(), initialPath.c_str());
437 
438 	initialPath = Common::normalizePath(g_director->getCurrentPath() + initialPath, g_director->_dirSeparator);
439 	Common::String convPath = initialPath;
440 
441 	debug(9, "pathMakeRelative(): s2 %s", convPath.c_str());
442 
443 	// Strip the leading whitespace from the path
444 	initialPath.trim();
445 
446 	if (testPath(initialPath, directory))
447 		return initialPath;
448 
449 	// Now try to search the file
450 	bool opened = false;
451 
452 	while (convPath.contains(g_director->_dirSeparator)) {
453 		int pos = convPath.find(g_director->_dirSeparator);
454 		convPath = Common::String(&convPath.c_str()[pos + 1]);
455 
456 		debug(9, "pathMakeRelative(): s3 try %s", convPath.c_str());
457 
458 		if (!testPath(convPath, directory))
459 			continue;
460 
461 		debug(9, "pathMakeRelative(): s3 converted %s -> %s", path.c_str(), convPath.c_str());
462 
463 		opened = true;
464 
465 		break;
466 	}
467 
468 	if (!opened) {
469 		// Try stripping all of the characters not allowed in FAT
470 		convPath = stripMacPath(initialPath.c_str());
471 
472 		debug(9, "pathMakeRelative(): s4 %s", convPath.c_str());
473 
474 		if (testPath(initialPath, directory))
475 			return initialPath;
476 
477 		// Now try to search the file
478 		while (convPath.contains(g_director->_dirSeparator)) {
479 			int pos = convPath.find(g_director->_dirSeparator);
480 			convPath = Common::String(&convPath.c_str()[pos + 1]);
481 
482 			debug(9, "pathMakeRelative(): s5 try %s", convPath.c_str());
483 
484 			if (!testPath(convPath, directory))
485 				continue;
486 
487 			debug(9, "pathMakeRelative(): s5 converted %s -> %s", path.c_str(), convPath.c_str());
488 
489 			opened = true;
490 
491 			break;
492 		}
493 	}
494 
495 	if (!opened && recursive && !directory) {
496 		// Hmmm. We couldn't find the path as is.
497 		// Let's try to translate file path into 8.3 format
498 		Common::String addedexts;
499 
500 		if (g_director->getPlatform() == Common::kPlatformWindows && g_director->getVersion() < 500) {
501 			convPath.clear();
502 			const char *ptr = initialPath.c_str();
503 			Common::String component;
504 
505 			while (*ptr) {
506 				if (*ptr == g_director->_dirSeparator) {
507 					if (component.equals(".")) {
508 						convPath += component;
509 					} else {
510 						convPath += convertMacFilename(component.c_str());
511 					}
512 
513 					component.clear();
514 					convPath += g_director->_dirSeparator;
515 				} else {
516 					component += *ptr;
517 				}
518 
519 				ptr++;
520 			}
521 
522 			if (hasExtension(component)) {
523 				Common::String nameWithoutExt = component.substr(0, component.size() - 4);
524 				Common::String ext = component.substr(component.size() - 4);
525 				Common::String newpath = convPath + convertMacFilename(nameWithoutExt.c_str()) + ext;
526 
527 				debug(9, "pathMakeRelative(): s6 %s -> try %s", initialPath.c_str(), newpath.c_str());
528 				Common::String res = pathMakeRelative(newpath, false, false);
529 
530 				if (testPath(res))
531 					return res;
532 			}
533 
534 			if (addexts)
535 				addedexts = testExtensions(component, initialPath, convPath);
536 		} else {
537 			if (addexts)
538 				addedexts = testExtensions(initialPath, initialPath, convPath);
539 		}
540 
541 		if (!addedexts.empty()) {
542 			return addedexts;
543 		}
544 
545 		return initialPath;	// Anyway nothing good is happening
546 	}
547 
548 	if (opened)
549 		return convPath;
550 	else
551 		return initialPath;
552 }
553 
hasExtension(Common::String filename)554 bool hasExtension(Common::String filename) {
555 	uint len = filename.size();
556 	return len >= 4 && filename[len - 4] == '.'
557 					&& Common::isAlpha(filename[len - 3])
558 					&& Common::isAlpha(filename[len - 2])
559 					&& Common::isAlpha(filename[len - 1]);
560 }
561 
testExtensions(Common::String component,Common::String initialPath,Common::String convPath)562 Common::String testExtensions(Common::String component, Common::String initialPath, Common::String convPath) {
563 	const char *extsD3[] = { ".MMM", 0 };
564 	const char *extsD4[] = { ".DIR", ".DXR", 0 };
565 
566 	const char **exts = (g_director->getVersion() >= 400) ? extsD4 : extsD3;
567 	for (int i = 0; exts[i]; ++i) {
568 		Common::String newpath = convPath + convertMacFilename(component.c_str()) + exts[i];
569 
570 		debug(9, "pathMakeRelative(): s6 %s -> try %s", initialPath.c_str(), newpath.c_str());
571 		Common::String res = pathMakeRelative(newpath, false, false);
572 
573 		if (testPath(res))
574 			return res;
575 	}
576 
577 	return Common::String();
578 }
579 
getFileName(Common::String path)580 Common::String getFileName(Common::String path) {
581 	while (path.contains(g_director->_dirSeparator)) {
582 		int pos = path.find(g_director->_dirSeparator);
583 		path = Common::String(&path.c_str()[pos + 1]);
584 	}
585 	return path;
586 }
587 
588 //////////////////
589 ////// Mac --> Windows filename conversion
590 //////////////////
myIsVowel(byte c)591 static bool myIsVowel(byte c) {
592 	return c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U';
593 }
594 
myIsAlpha(byte c)595 static bool myIsAlpha(byte c) {
596 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
597 }
598 
myIsDigit(byte c)599 static bool myIsDigit(byte c) {
600 	return c >= '0' && c <= '9';
601 }
602 
myIsAlnum(byte c)603 static bool myIsAlnum(byte c) {
604 	return myIsAlpha(c) || myIsDigit(c);
605 }
606 
myIsSpace(byte c)607 static bool myIsSpace(byte c) {
608 	return c == ' ';
609 }
610 
myIsFATChar(byte c)611 static bool myIsFATChar(byte c) {
612 	return (c >= ' ' && c <= '!') || (c >= '#' && c == ')') || (c >= '-' && c <= '.') ||
613 			(c >= '?' && c <= '@') || (c >= '^' && c <= '`') || c == '{' || (c >= '}' && c <= '~');
614 }
615 
stripMacPath(const char * name)616 Common::String stripMacPath(const char *name) {
617 	Common::String res;
618 
619 	int origlen = strlen(name);
620 
621 	// Remove trailing spaces
622 	const char *end = &name[origlen - 1];
623 	while (myIsSpace(*end))
624 		end--;
625 	const char *ptr = name;
626 
627 	while (ptr <= end) {
628 		if (myIsAlnum(*ptr) || myIsFATChar(*ptr) || *ptr == g_director->_dirSeparator) {
629 			res += *ptr;
630 		}
631 		ptr++;
632 	}
633 
634 	return res;
635 }
636 
convertMacFilename(const char * name)637 Common::String convertMacFilename(const char *name) {
638 	Common::String res;
639 
640 	int origlen = strlen(name);
641 
642 	if (g_director->getVersion() < 400) {
643 		// Remove trailing spaces
644 		const char *ptr = &name[origlen - 1];
645 		while (myIsSpace(*ptr))
646 			ptr--;
647 
648 		int numDigits = 0;
649 		char digits[10];
650 
651 		// Count trailing digits, but leave front letter
652 		while (myIsDigit(*ptr) && (numDigits < (8 - 1)))
653 			digits[++numDigits] = *ptr--;
654 
655 		// Count file name without vowels, spaces and digits in-between
656 		ptr = name;
657 		int cnt = 0;
658 		while (cnt < (8 - numDigits) && ptr < &name[origlen]) {
659 			char c = toupper(*ptr++);
660 
661 			if ((myIsVowel(c) && (cnt != 0)) || myIsSpace(c) || myIsDigit(c))
662 				continue;
663 
664 			if ((c != '_') && !myIsAlnum(c))
665 				continue;
666 
667 			cnt++;
668 		}
669 
670 		// Make sure all trailing digits fit
671 		int numVowels = 8 - (numDigits + cnt);
672 		ptr = name;
673 
674 		// Put enough characters from beginning
675 		for (cnt = 0; cnt < (8 - numDigits) && ptr < &name[origlen];) {
676 			char c = toupper(*ptr++);
677 
678 			if (myIsVowel(c) && (cnt != 0)) {
679 				if (numVowels > 0)
680 					numVowels--;
681 				else
682 					continue;
683 			}
684 
685 			if (myIsSpace(c) || myIsDigit(c) || ((c != '_') && !myIsAlnum(c)))
686 				continue;
687 
688 			res += c;
689 
690 			cnt++;
691 		}
692 
693 		// Now attach all digits
694 		while (numDigits)
695 			res += digits[numDigits--];
696 	} else {
697 		const char *ptr = name;
698 		for (int cnt = 0; cnt < 8 && ptr < &name[origlen];) {
699 			char c = toupper(*ptr++);
700 
701 			if (myIsSpace(c) || (!myIsAlnum(c) && !myIsFATChar(c)))
702 				continue;
703 
704 			res += c;
705 
706 			cnt++;
707 		}
708 	}
709 
710 	return res;
711 }
712 
dumpScriptName(const char * prefix,int type,int id,const char * ext)713 Common::String dumpScriptName(const char *prefix, int type, int id, const char *ext) {
714 	Common::String typeName;
715 
716 	switch (type) {
717 	case kNoneScript:
718 	default:
719 		error("dumpScriptName(): Incorrect call (type %d)", type);
720 	case kMovieScript:
721 		typeName = "movie";
722 		break;
723 	case kCastScript:
724 		typeName = "cast";
725 		break;
726 	case kEventScript:
727 		typeName = "event";
728 		break;
729 	case kScoreScript:
730 		typeName = "score";
731 		break;
732 	}
733 
734 	return Common::String::format("./dumps/%s-%s-%d.%s", prefix, typeName.c_str(), id, ext);
735 }
736 
setSeed(int seed)737 void RandomState::setSeed(int seed) {
738 	init(32);
739 
740 	_seed = seed ? seed : 1;
741 }
742 
getRandom(int32 range)743 int32 RandomState::getRandom(int32 range) {
744 	int32 res;
745 
746 	if (_seed == 0)
747 		init(32);
748 
749 	res = perlin(genNextRandom() * 71);
750 
751 	if (range > 0)
752 		res = ((res & 0x7fffffff) % range);
753 
754 	return res;
755 }
756 
757 static const uint32 masks[31] = {
758 	0x00000003, 0x00000006, 0x0000000c, 0x00000014, 0x00000030, 0x00000060, 0x000000b8, 0x00000110,
759 	0x00000240, 0x00000500, 0x00000ca0, 0x00001b00, 0x00003500, 0x00006000, 0x0000b400, 0x00012000,
760 	0x00020400, 0x00072000, 0x00090000, 0x00140000, 0x00300000, 0x00400000, 0x00d80000, 0x01200000,
761 	0x03880000, 0x07200000, 0x09000000, 0x14000000, 0x32800000, 0x48000000, 0xa3000000
762 };
763 
init(int len)764 void RandomState::init(int len) {
765 	if (len < 2 || len > 32) {
766 		len = 32;
767 		_len = (uint32)-1; // Since we cannot shift 32 bits
768 	} else {
769 		_len = (1 << len) - 1;
770 	}
771 
772 	_seed = 1;
773 	_mask = masks[len - 2];
774 }
775 
genNextRandom()776 int32 RandomState::genNextRandom() {
777 	if (_seed & 1)
778 		_seed = (_seed >> 1) ^ _mask;
779 	else
780 		_seed >>= 1;
781 
782 	return _seed;
783 }
784 
perlin(int32 val)785 int32 RandomState::perlin(int32 val) {
786 	int32 res;
787 
788 	val = ((val << 13) ^ val) - (val >> 21);
789 
790 	res = (val * (val * val * 15731 + 789221) + 1376312589) & 0x7fffffff;
791 	res += val;
792 	res = ((res << 13) ^ res) - (res >> 21);
793 
794 	return res;
795 }
796 
readVarInt(Common::SeekableReadStream & stream)797 uint32 readVarInt(Common::SeekableReadStream &stream) {
798 	// Shockwave variable-length integer
799 	uint32 val = 0;
800 	byte b;
801 	do {
802 		b = stream.readByte();
803 		val = (val << 7) | (b & 0x7f); // The 7 least significant bits are appended to the result
804 	} while (b >> 7); // If the most significant bit is 1, there's another byte after
805 	return val;
806 }
807 
readZlibData(Common::SeekableReadStream & stream,unsigned long len,unsigned long * outLen,bool bigEndian)808 Common::SeekableReadStreamEndian *readZlibData(Common::SeekableReadStream &stream, unsigned long len, unsigned long *outLen, bool bigEndian) {
809 #ifdef USE_ZLIB
810 	byte *in = (byte *)malloc(len);
811 	byte *out = (byte *)malloc(*outLen);
812 	stream.read(in, len);
813 
814 	if (!Common::uncompress(out, outLen, in, len)) {
815 		free(in);
816 		free(out);
817 		return nullptr;
818 	}
819 
820 	free(in);
821 	return new Common::MemoryReadStreamEndian(out, *outLen, bigEndian, DisposeAfterUse::YES);
822 # else
823 	return nullptr;
824 # endif
825 }
826 
humanVersion(uint16 ver)827 uint16 humanVersion(uint16 ver) {
828 	if (ver >= kFileVer1201)
829 		return 1201;
830 	if (ver >= kFileVer1200)
831 		return 1200;
832 	if (ver >= kFileVer1150)
833 		return 1150;
834 	if (ver >= kFileVer1100)
835 		return 1100;
836 	if (ver >= kFileVer1000)
837 		return 1000;
838 	if (ver >= kFileVer850)
839 		return 850;
840 	if (ver >= kFileVer800)
841 		return 800;
842 	if (ver >= kFileVer700)
843 		return 700;
844 	if (ver >= kFileVer600)
845 		return 600;
846 	if (ver >= kFileVer500)
847 		return 500;
848 	if (ver >= kFileVer404)
849 		return 404;
850 	if (ver >= kFileVer400)
851 		return 400;
852 	if (ver >= kFileVer310)
853 		return 310;
854 	if (ver >= kFileVer300)
855 		return 300;
856 	return 200;
857 }
858 
platformFromID(uint16 id)859 Common::Platform platformFromID(uint16 id) {
860 	switch (id) {
861 	case 1:
862 		return Common::kPlatformMacintosh;
863 	case 2:
864 		return Common::kPlatformWindows;
865 	default:
866 		warning("platformFromID: Unknown platform ID %d", id);
867 		break;
868 	}
869 	return Common::kPlatformUnknown;
870 }
871 
isButtonSprite(SpriteType spriteType)872 bool isButtonSprite(SpriteType spriteType) {
873 	return spriteType == kButtonSprite || spriteType == kCheckboxSprite || spriteType == kRadioButtonSprite;
874 }
875 
getEncoding(Common::Platform platform,Common::Language language)876 Common::CodePage getEncoding(Common::Platform platform, Common::Language language) {
877 	switch (language) {
878 	case Common::JA_JPN:
879 		return Common::kWindows932; // Shift JIS
880 	default:
881 		break;
882 	}
883 	return (platform == Common::kPlatformWindows)
884 				? Common::kWindows1252
885 				: Common::kMacRoman;
886 }
887 
detectFontEncoding(Common::Platform platform,uint16 fontId)888 Common::CodePage detectFontEncoding(Common::Platform platform, uint16 fontId) {
889 	return getEncoding(platform, g_director->_wm->_fontMan->getFontLanguage(fontId));
890 }
891 
charToNum(Common::u32char_type_t ch)892 int charToNum(Common::u32char_type_t ch) {
893 	Common::String encodedCh = Common::U32String(ch).encode(g_director->getPlatformEncoding());
894 	int res = 0;
895 	while (encodedCh.size()) {
896 		res = (res << 8) | (byte)encodedCh.firstChar();
897 		encodedCh.deleteChar(0);
898 	}
899 	return res;
900 }
901 
numToChar(int num)902 Common::u32char_type_t numToChar(int num) {
903 	Common::String encodedCh;
904 	while (num) {
905 		encodedCh.insertChar((char)(num & 0xFF), 0);
906 		num >>= 8;
907 	}
908 	Common::U32String str = encodedCh.decode(g_director->getPlatformEncoding());
909 	return str.lastChar();
910 }
911 
compareStrings(const Common::String & s1,const Common::String & s2)912 int compareStrings(const Common::String &s1, const Common::String &s2) {
913 	Common::U32String u32S1 = s1.decode(Common::kUtf8);
914 	Common::U32String u32S2 = s2.decode(Common::kUtf8);
915 	const Common::u32char_type_t *p1 = u32S1.c_str();
916 	const Common::u32char_type_t *p2 = u32S2.c_str();
917 	uint32 c1, c2;
918 	while ((c1 = charToNum(*p1)) && (c2 = charToNum(*p2)) && c1 == c2) {
919 		p1++;
920 		p2++;
921 	}
922 	return c1 - c2;
923 }
924 
encodePathForDump(const Common::String & path)925 Common::String encodePathForDump(const Common::String &path) {
926 	return punycode_encodepath(Common::Path(path, g_director->_dirSeparator)).toString();
927 }
928 
utf8ToPrintable(const Common::String & str)929 Common::String utf8ToPrintable(const Common::String &str) {
930 	return Common::toPrintable(Common::U32String(str));
931 }
932 
castTypeToString(const CastType & type)933 Common::String castTypeToString(const CastType &type) {
934 	Common::String res;
935 	switch(type) {
936 	case kCastBitmap:
937 		res = "bitmap";
938 		break;
939 	case kCastPalette:
940 		res = "palette";
941 		break;
942 	case kCastButton:
943 		res = "button";
944 		break;
945 	case kCastPicture:
946 		res = "picture";
947 		break;
948 	case kCastDigitalVideo:
949 		res = "digitalVideo";
950 		break;
951 	case kCastLingoScript:
952 		res = "script";
953 		break;
954 	case kCastShape:
955 		res = "shape";
956 		break;
957 	case kCastFilmLoop:
958 		res = "filmLoop";
959 		break;
960 	case kCastSound:
961 		res = "sound";
962 		break;
963 	case kCastMovie:
964 		res = "movie";
965 		break;
966 	case kCastText:
967 		res = "text";
968 		break;
969 	default:
970 		res = "empty";
971 		break;
972 	}
973 	return res;
974 }
975 
976 } // End of namespace Director
977