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 "sword1/sword1.h"
24 
25 #include "sword1/resman.h"
26 #include "sword1/objectman.h"
27 #include "sword1/mouse.h"
28 #include "sword1/logic.h"
29 #include "sword1/sound.h"
30 #include "sword1/screen.h"
31 #include "sword1/swordres.h"
32 #include "sword1/menu.h"
33 #include "sword1/music.h"
34 #include "sword1/control.h"
35 
36 #include "common/config-manager.h"
37 #include "common/textconsole.h"
38 
39 #include "engines/util.h"
40 
41 #include "gui/message.h"
42 
43 namespace Sword1 {
44 
45 SystemVars SwordEngine::_systemVars;
46 
SwordEngine(OSystem * syst)47 SwordEngine::SwordEngine(OSystem *syst)
48 	: Engine(syst) {
49 
50 	if (!scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1demo") ||
51 	        !scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1psxdemo") ||
52 	        !scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1macdemo"))
53 		_features = GF_DEMO;
54 	else
55 		_features = 0;
56 
57 	// Add default file directories
58 	const Common::FSNode gameDataDir(ConfMan.get("path"));
59 	SearchMan.addSubDirectoryMatching(gameDataDir, "clusters");
60 	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
61 	SearchMan.addSubDirectoryMatching(gameDataDir, "speech");
62 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
63 	SearchMan.addSubDirectoryMatching(gameDataDir, "smackshi");
64 	SearchMan.addSubDirectoryMatching(gameDataDir, "streams"); // PSX videos
65 	SearchMan.addSubDirectoryMatching(gameDataDir, "english"); // PSX Demo
66 	SearchMan.addSubDirectoryMatching(gameDataDir, "italian"); // PSX Demo
67 
68 	setDebugger(new SwordConsole(this));
69 
70 	_mouseState = 0;
71 	_resMan = 0;
72 	_objectMan = 0;
73 	_screen = 0;
74 	_mouse = 0;
75 	_logic = 0;
76 	_sound = 0;
77 	_menu = 0;
78 	_music = 0;
79 	_control = 0;
80 }
81 
~SwordEngine()82 SwordEngine::~SwordEngine() {
83 	delete _control;
84 	delete _logic;
85 	delete _menu;
86 	delete _sound;
87 	delete _music;
88 	delete _screen;
89 	delete _mouse;
90 	delete _objectMan;
91 	delete _resMan;
92 }
93 
init()94 Common::Error SwordEngine::init() {
95 
96 	initGraphics(640, 480);
97 
98 	if (0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1mac") ||
99 	        0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1macdemo"))
100 		_systemVars.platform = Common::kPlatformMacintosh;
101 	else if (0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1psx") ||
102 	         0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1psxdemo"))
103 		_systemVars.platform = Common::kPlatformPSX;
104 	else
105 		_systemVars.platform = Common::kPlatformWindows;
106 
107 	checkCdFiles();
108 
109 	debug(5, "Starting resource manager");
110 	_resMan = new ResMan("swordres.rif", _systemVars.platform == Common::kPlatformMacintosh);
111 	debug(5, "Starting object manager");
112 	_objectMan = new ObjectMan(_resMan);
113 	_mouse = new Mouse(_system, _resMan, _objectMan);
114 	_screen = new Screen(_system, _resMan, _objectMan);
115 	_music = new Music(_mixer);
116 	_sound = new Sound(_mixer, _resMan);
117 	_menu = new Menu(_screen, _mouse);
118 	_logic = new Logic(this, _objectMan, _resMan, _screen, _mouse, _sound, _music, _menu, _system, _mixer);
119 	_mouse->useLogicAndMenu(_logic, _menu);
120 
121 	syncSoundSettings();
122 
123 	_systemVars.justRestoredGame = 0;
124 	_systemVars.currentCD = 0;
125 	_systemVars.controlPanelMode = CP_NEWGAME;
126 	_systemVars.forceRestart = false;
127 	_systemVars.wantFade = true;
128 	_systemVars.realLanguage = Common::parseLanguage(ConfMan.get("language"));
129 	_systemVars.isLangRtl = false;
130 
131 	switch (_systemVars.realLanguage) {
132 	case Common::DE_DEU:
133 		_systemVars.language = BS1_GERMAN;
134 		break;
135 	case Common::FR_FRA:
136 		_systemVars.language = BS1_FRENCH;
137 		break;
138 	case Common::IT_ITA:
139 		_systemVars.language = BS1_ITALIAN;
140 		break;
141 	case Common::ES_ESP:
142 		_systemVars.language = BS1_SPANISH;
143 		break;
144 	case Common::PT_BRA:
145 		_systemVars.language = BS1_PORT;
146 		break;
147 	case Common::CZ_CZE:
148 		_systemVars.language = BS1_CZECH;
149 		break;
150 	case Common::HE_ISR:
151 		// Hebrew is using "faked" English
152 		_systemVars.language = BS1_ENGLISH;
153 		_systemVars.isLangRtl = true;
154 		break;
155 	default:
156 		_systemVars.language = BS1_ENGLISH;
157 		break;
158 	}
159 
160 	_systemVars.showText = ConfMan.getBool("subtitles");
161 
162 	_systemVars.playSpeech = true;
163 	_mouseState = 0;
164 
165 	// Some Mac versions use big endian for the speech files but not all of them.
166 	if (_systemVars.platform == Common::kPlatformMacintosh)
167 		_sound->checkSpeechFileEndianness();
168 
169 	_logic->initialize();
170 	_objectMan->initialize();
171 	_mouse->initialize();
172 	_control = new Control(_saveFileMan, _resMan, _objectMan, _system, _mouse, _sound, _music);
173 
174 	return Common::kNoError;
175 }
176 
reinitialize()177 void SwordEngine::reinitialize() {
178 	_sound->quitScreen();
179 	_resMan->flush(); // free everything that's currently alloced and opened. (*evil*)
180 
181 	_logic->initialize();     // now reinitialize these objects as they (may) have locked
182 	_objectMan->initialize(); // resources which have just been wiped.
183 	_mouse->initialize();
184 	_system->warpMouse(320, 240);
185 	_systemVars.wantFade = true;
186 }
187 
syncSoundSettings()188 void SwordEngine::syncSoundSettings() {
189 	Engine::syncSoundSettings();
190 
191 	uint musicVol = ConfMan.getInt("music_volume");
192 	uint sfxVol = ConfMan.getInt("sfx_volume");
193 	uint speechVol = ConfMan.getInt("speech_volume");
194 
195 	uint musicBal = 50;
196 	if (ConfMan.hasKey("music_balance")) {
197 		musicBal = CLIP(ConfMan.getInt("music_balance"), 0, 100);
198 	}
199 
200 	uint speechBal = 50;
201 	if (ConfMan.hasKey("speech_balance")) {
202 		speechBal = CLIP(ConfMan.getInt("speech_balance"), 0, 100);
203 	}
204 	uint sfxBal = 50;
205 	if (ConfMan.hasKey("sfx_balance")) {
206 		sfxBal = CLIP(ConfMan.getInt("sfx_balance"), 0, 100);
207 	}
208 
209 	uint musicVolL = 2 * musicVol * musicBal / 100;
210 	uint musicVolR = 2 * musicVol - musicVolL;
211 
212 	uint speechVolL = 2 * speechVol * speechBal / 100;
213 	uint speechVolR = 2 * speechVol - speechVolL;
214 
215 	uint sfxVolL = 2 * sfxVol * sfxBal / 100;
216 	uint sfxVolR = 2 * sfxVol - sfxVolL;
217 
218 	if (musicVolR > 255) {
219 		musicVolR = 255;
220 	}
221 	if (musicVolL > 255) {
222 		musicVolL = 255;
223 	}
224 
225 	if (speechVolR > 255) {
226 		speechVolR = 255;
227 	}
228 	if (speechVolL > 255) {
229 		speechVolL = 255;
230 	}
231 	if (sfxVolR > 255) {
232 		sfxVolR = 255;
233 	}
234 	if (sfxVolL > 255) {
235 		sfxVolL = 255;
236 	}
237 
238 	bool mute = ConfMan.getBool("mute");
239 
240 	if (mute) {
241 		_music->setVolume(0, 0);
242 		_sound->setSpeechVol(0, 0);
243 		_sound->setSfxVol(0, 0);
244 	} else {
245 		_music->setVolume(musicVolL, musicVolR);
246 		_sound->setSpeechVol(speechVolL, speechVolR);
247 		_sound->setSfxVol(sfxVolL, sfxVolR);
248 	}
249 }
250 
flagsToBool(bool * dest,uint8 flags)251 void SwordEngine::flagsToBool(bool *dest, uint8 flags) {
252 	uint8 bitPos = 0;
253 	while (flags) {
254 		if (flags & 1)
255 			dest[bitPos] = true;
256 		flags >>= 1;
257 		bitPos++;
258 	}
259 }
260 
261 static const char *const errorMsgs[] = {
262 	"The file \"%s\" is missing and the game doesn't work without it.\n"
263 	"Please copy it from CD %d and try starting the game again.\n"
264 	"The Readme file also contains further information.",
265 
266 	"%d important files are missing, the game can't start without them.\n"
267 	"Please copy these files from their corresponding CDs:\n",
268 
269 	"The file \"%s\" is missing.\n"
270 	"Even though the game may initially seem to\n"
271 	"work fine, it will crash when it needs the\n"
272 	"data from this file and you will be thrown back to your last savegame.\n"
273 	"Please copy the file from CD %d and start the game again.",
274 
275 	"%d files are missing.\n"
276 	"Even though the game may initially seem to\n"
277 	"work fine, it will crash when it needs the\n"
278 	"data from these files and you will be thrown back to your last savegame.\n"
279 	"Please copy these files from their corresponding CDs:\n"
280 };
281 
282 const CdFile SwordEngine::_pcCdFileList[] = {
283 	{ "paris2.clu", FLAG_CD1 },
284 	{ "ireland.clu", FLAG_CD2 },
285 	{ "paris3.clu", FLAG_CD1 },
286 	{ "paris4.clu", FLAG_CD1 },
287 	{ "scotland.clu", FLAG_CD2 },
288 	{ "spain.clu", FLAG_CD2 },
289 	{ "syria.clu", FLAG_CD2 },
290 	{ "train.clu", FLAG_CD2 },
291 	{ "compacts.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
292 	{ "general.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
293 	{ "maps.clu", FLAG_CD1 | FLAG_DEMO },
294 	{ "paris1.clu", FLAG_CD1 | FLAG_DEMO },
295 	{ "scripts.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
296 	{ "swordres.rif", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
297 	{ "text.clu", FLAG_CD1 | FLAG_DEMO },
298 	{ "1m14a.wav", FLAG_DEMO },
299 	{ "speech1.clu", FLAG_SPEECH1 },
300 	{ "speech2.clu", FLAG_SPEECH2 },
301 	{ "speech.clu", FLAG_SPEECH | FLAG_DEMO } // Spanish Demo
302 #ifdef USE_FLAC
303 	, { "speech1.clf", FLAG_SPEECH1 },
304 	{ "speech2.clf", FLAG_SPEECH2 }
305 #endif
306 #ifdef USE_VORBIS
307 	, { "speech1.clv", FLAG_SPEECH1 },
308 	{ "speech2.clv", FLAG_SPEECH2 }
309 #endif
310 #ifdef USE_MAD
311 	, { "speech1.cl3", FLAG_SPEECH1 },
312 	{ "speech2.cl3", FLAG_SPEECH2 }
313 #endif
314 };
315 
316 const CdFile SwordEngine::_macCdFileList[] = {
317 	{ "paris2.clm", FLAG_CD1 },
318 	{ "ireland.clm", FLAG_CD2 },
319 	{ "paris3.clm", FLAG_CD1 },
320 	{ "paris4.clm", FLAG_CD1 },
321 	{ "scotland.clm", FLAG_CD2 },
322 	{ "spain.clm", FLAG_CD2 },
323 	{ "syria.clm", FLAG_CD2 },
324 	{ "train.clm", FLAG_CD2 },
325 	{ "compacts.clm", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
326 	{ "general.clm", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
327 	{ "maps.clm", FLAG_CD1 | FLAG_DEMO },
328 	{ "paris1.clm", FLAG_CD1 | FLAG_DEMO },
329 	{ "scripts.clm", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
330 	{ "swordres.rif", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
331 	{ "text.clm", FLAG_CD1 | FLAG_DEMO },
332 	{ "speech1.clu", FLAG_SPEECH1 },
333 	{ "speech2.clu", FLAG_SPEECH2 }
334 #ifdef USE_FLAC
335 	,{ "speech1.clf", FLAG_SPEECH1 },
336 	{ "speech2.clf", FLAG_SPEECH2 }
337 #endif
338 #ifdef USE_VORBIS
339 	,{ "speech1.clv", FLAG_SPEECH1 },
340 	{ "speech2.clv", FLAG_SPEECH2 }
341 #endif
342 #ifdef USE_MAD
343 	,{ "speech1.cl3", FLAG_SPEECH1 },
344 	{ "speech2.cl3", FLAG_SPEECH2 }
345 #endif
346 };
347 
348 const CdFile SwordEngine::_psxCdFileList[] = { // PSX edition has only one cd
349 	{ "paris2.clu", FLAG_CD1 },
350 	{ "ireland.clu", FLAG_CD1 },
351 	{ "paris3.clu", FLAG_CD1 },
352 	{ "paris4.clu", FLAG_CD1 },
353 	{ "scotland.clu", FLAG_CD1 },
354 	{ "spain.clu", FLAG_CD1 },
355 	{ "syria.clu", FLAG_CD1 },
356 	{ "train.clu", FLAG_CD1 },
357 	{ "train.plx", FLAG_CD1 },
358 	{ "compacts.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
359 	{ "general.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
360 	{ "maps.clu", FLAG_CD1 | FLAG_DEMO },
361 	{ "paris1.clu", FLAG_CD1 | FLAG_DEMO},
362 	{ "scripts.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
363 	{ "swordres.rif", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
364 	{ "text.clu", FLAG_CD1 | FLAG_DEMO },
365 	{ "speech.dat", FLAG_SPEECH1 | FLAG_DEMO },
366 	{ "speech.tab", FLAG_SPEECH1 | FLAG_DEMO },
367 	{ "speech.inf", FLAG_SPEECH1 | FLAG_DEMO },
368 	{ "speech.lis", FLAG_SPEECH1 | FLAG_DEMO }
369 };
370 
showFileErrorMsg(uint8 type,bool * fileExists)371 void SwordEngine::showFileErrorMsg(uint8 type, bool *fileExists) {
372 	char msg[1024];
373 	int missCnt = 0, missNum = 0;
374 
375 	if (SwordEngine::isMac()) {
376 		for (int i = 0; i < ARRAYSIZE(_macCdFileList); i++)
377 			if (!fileExists[i]) {
378 				missCnt++;
379 				missNum = i;
380 			}
381 		assert(missCnt > 0); // this function shouldn't get called if there's nothing missing.
382 		warning("%d files missing", missCnt);
383 		int msgId = (type == TYPE_IMMED) ? 0 : 2;
384 		if (missCnt == 1) {
385 			sprintf(msg, errorMsgs[msgId],
386 			        _macCdFileList[missNum].name, (_macCdFileList[missNum].flags & FLAG_CD2) ? 2 : 1);
387 			warning("%s", msg);
388 		} else {
389 			char *pos = msg + sprintf(msg, errorMsgs[msgId + 1], missCnt);
390 			warning("%s", msg);
391 			for (int i = 0; i < ARRAYSIZE(_macCdFileList); i++)
392 				if (!fileExists[i]) {
393 					warning("\"%s\" (CD %d)", _macCdFileList[i].name, (_macCdFileList[i].flags & FLAG_CD2) ? 2 : 1);
394 					pos += sprintf(pos, "\"%s\" (CD %d)\n", _macCdFileList[i].name, (_macCdFileList[i].flags & FLAG_CD2) ? 2 : 1);
395 				}
396 		}
397 	} else if (SwordEngine::isPsx()) {
398 		for (int i = 0; i < ARRAYSIZE(_psxCdFileList); i++)
399 			if (!fileExists[i]) {
400 				missCnt++;
401 				missNum = i;
402 			}
403 		assert(missCnt > 0); // this function shouldn't get called if there's nothing missing.
404 		warning("%d files missing", missCnt);
405 		int msgId = (type == TYPE_IMMED) ? 0 : 2;
406 		if (missCnt == 1) {
407 			sprintf(msg, errorMsgs[msgId], _psxCdFileList[missNum].name, 1);
408 			warning("%s", msg);
409 		} else {
410 			char *pos = msg + sprintf(msg, errorMsgs[msgId + 1], missCnt);
411 			warning("%s", msg);
412 			for (int i = 0; i < ARRAYSIZE(_psxCdFileList); i++)
413 				if (!fileExists[i]) {
414 					warning("\"%s\"", _macCdFileList[i].name);
415 					pos += sprintf(pos, "\"%s\"\n", _macCdFileList[i].name);
416 				}
417 		}
418 	} else {
419 		for (int i = 0; i < ARRAYSIZE(_pcCdFileList); i++)
420 			if (!fileExists[i]) {
421 				missCnt++;
422 				missNum = i;
423 			}
424 		assert(missCnt > 0); // this function shouldn't get called if there's nothing missing.
425 		warning("%d files missing", missCnt);
426 		int msgId = (type == TYPE_IMMED) ? 0 : 2;
427 		if (missCnt == 1) {
428 			sprintf(msg, errorMsgs[msgId],
429 			        _pcCdFileList[missNum].name, (_pcCdFileList[missNum].flags & FLAG_CD2) ? 2 : 1);
430 			warning("%s", msg);
431 		} else {
432 			char *pos = msg + sprintf(msg, errorMsgs[msgId + 1], missCnt);
433 			warning("%s", msg);
434 			for (int i = 0; i < ARRAYSIZE(_pcCdFileList); i++)
435 				if (!fileExists[i]) {
436 					warning("\"%s\" (CD %d)", _pcCdFileList[i].name, (_pcCdFileList[i].flags & FLAG_CD2) ? 2 : 1);
437 					pos += sprintf(pos, "\"%s\" (CD %d)\n", _pcCdFileList[i].name, (_pcCdFileList[i].flags & FLAG_CD2) ? 2 : 1);
438 				}
439 		}
440 	}
441 	GUI::MessageDialog dialog(msg);
442 	dialog.runModal();
443 	if (type == TYPE_IMMED) // we can't start without this file, so error() out.
444 		error("%s", msg);
445 }
446 
checkCdFiles()447 void SwordEngine::checkCdFiles() { // check if we're running from cd, hdd or what...
448 	bool fileExists[30];
449 	bool isFullVersion = false; // default to demo version
450 	bool missingTypes[9] = { false, false, false, false, false, false, false, false, false };
451 	bool foundTypes[9] = { false, false, false, false, false, false, false, false, false };
452 	bool cd2FilesFound = false;
453 	_systemVars.runningFromCd = false;
454 	_systemVars.playSpeech = true;
455 
456 	// check all files and look out if we can find a file that wouldn't exist if this was the demo version
457 	if (SwordEngine::isMac()) {
458 		for (int fcnt = 0; fcnt < ARRAYSIZE(_macCdFileList); fcnt++) {
459 			if (Common::File::exists(_macCdFileList[fcnt].name)) {
460 				fileExists[fcnt] = true;
461 				flagsToBool(foundTypes, _macCdFileList[fcnt].flags);
462 				if (!(_macCdFileList[fcnt].flags & FLAG_DEMO))
463 					isFullVersion = true;
464 				if (_macCdFileList[fcnt].flags & FLAG_CD2)
465 					cd2FilesFound = true;
466 			} else {
467 				flagsToBool(missingTypes, _macCdFileList[fcnt].flags);
468 				fileExists[fcnt] = false;
469 			}
470 		}
471 	} else if (SwordEngine::isPsx()) {
472 		for (int fcnt = 0; fcnt < ARRAYSIZE(_psxCdFileList); fcnt++) {
473 			if (Common::File::exists(_psxCdFileList[fcnt].name)) {
474 				fileExists[fcnt] = true;
475 				flagsToBool(foundTypes, _psxCdFileList[fcnt].flags);
476 				if (!(_psxCdFileList[fcnt].flags & FLAG_DEMO))
477 					isFullVersion = true;
478 				cd2FilesFound = true;
479 			} else {
480 				flagsToBool(missingTypes, _psxCdFileList[fcnt].flags);
481 				fileExists[fcnt] = false;
482 			}
483 		}
484 	} else {
485 		for (int fcnt = 0; fcnt < ARRAYSIZE(_pcCdFileList); fcnt++) {
486 			if (Common::File::exists(_pcCdFileList[fcnt].name)) {
487 				fileExists[fcnt] = true;
488 				flagsToBool(foundTypes, _pcCdFileList[fcnt].flags);
489 				if (!(_pcCdFileList[fcnt].flags & FLAG_DEMO))
490 					isFullVersion = true;
491 				if (_pcCdFileList[fcnt].flags & FLAG_CD2)
492 					cd2FilesFound = true;
493 			} else {
494 				flagsToBool(missingTypes, _pcCdFileList[fcnt].flags);
495 				fileExists[fcnt] = false;
496 			}
497 		}
498 	}
499 
500 	if (((_features & GF_DEMO) == 0) != isFullVersion) // shouldn't happen...
501 		warning("Your Broken Sword 1 version looks like a %s version but you are starting it as a %s version", isFullVersion ? "full" : "demo", (_features & GF_DEMO) ? "demo" : "full");
502 
503 	if (foundTypes[TYPE_SPEECH1]) // we found some kind of speech1 file (.clu, .cl3, .clv)
504 		missingTypes[TYPE_SPEECH1] = false; // so we don't care if there's a different kind missing
505 	if (foundTypes[TYPE_SPEECH2]) // same for speech2
506 		missingTypes[TYPE_SPEECH2] = false;
507 
508 	if (isFullVersion)                   // if this is the full version...
509 		missingTypes[TYPE_DEMO] = false; // then we don't need demo files...
510 	else                                 // and vice versa
511 		missingTypes[TYPE_SPEECH1] = missingTypes[TYPE_SPEECH2] = missingTypes[TYPE_CD1] = missingTypes[TYPE_CD2] = false;
512 
513 	bool somethingMissing = false;
514 	for (int i = 0; i < 8; i++)
515 		somethingMissing |= missingTypes[i];
516 	if (somethingMissing) { // okay, there *are* files missing
517 		// first, update the fileExists[] array depending on our changed missingTypes
518 		if (SwordEngine::isMac()) {
519 			for (int fileCnt = 0; fileCnt < ARRAYSIZE(_macCdFileList); fileCnt++)
520 				if (!fileExists[fileCnt]) {
521 					fileExists[fileCnt] = true;
522 					for (int flagCnt = 0; flagCnt < 8; flagCnt++)
523 						if (missingTypes[flagCnt] && ((_macCdFileList[fileCnt].flags & (1 << flagCnt)) != 0))
524 							fileExists[fileCnt] = false; // this is one of the files we were looking for
525 				}
526 		} else if (SwordEngine::isPsx()) {
527 			for (int fileCnt = 0; fileCnt < ARRAYSIZE(_psxCdFileList); fileCnt++)
528 				if (!fileExists[fileCnt]) {
529 					fileExists[fileCnt] = true;
530 					for (int flagCnt = 0; flagCnt < 8; flagCnt++)
531 						if (missingTypes[flagCnt] && ((_psxCdFileList[fileCnt].flags & (1 << flagCnt)) != 0))
532 							fileExists[fileCnt] = false; // this is one of the files we were looking for
533 				}
534 		} else {
535 			for (int fileCnt = 0; fileCnt < ARRAYSIZE(_pcCdFileList); fileCnt++)
536 				if (!fileExists[fileCnt]) {
537 					fileExists[fileCnt] = true;
538 					for (int flagCnt = 0; flagCnt < 8; flagCnt++)
539 						if (missingTypes[flagCnt] && ((_pcCdFileList[fileCnt].flags & (1 << flagCnt)) != 0))
540 							fileExists[fileCnt] = false; // this is one of the files we were looking for
541 				}
542 		}
543 		if (missingTypes[TYPE_IMMED]) {
544 			// important files missing, can't start the game without them
545 			showFileErrorMsg(TYPE_IMMED, fileExists);
546 		} else if ((!missingTypes[TYPE_CD1]) && !cd2FilesFound) {
547 			/* we have all the data from cd one, but not a single one from CD2.
548 			    I'm not sure how we should handle this, for now I'll just assume that the
549 			    user has set up the extrapath correctly and copied the necessary files to HDD.
550 			    A quite optimistic assumption, I'd say. Maybe we should change this for the release
551 			    to warn the user? */
552 			warning("CD2 data files not found. I hope you know what you're doing and that\n"
553 			        "you have set up the extrapath and additional data correctly.\n"
554 			        "If you didn't, you should better read the ScummVM readme file");
555 			_systemVars.runningFromCd = true;
556 			_systemVars.playSpeech = true;
557 		} else if (missingTypes[TYPE_CD1] || missingTypes[TYPE_CD2]) {
558 			// several files from CD1 both CDs are missing. we can probably start, but it'll crash sooner or later
559 			showFileErrorMsg(TYPE_CD1, fileExists);
560 		} else if (missingTypes[TYPE_SPEECH1] || missingTypes[TYPE_SPEECH2]) {
561 			// not so important, but there won't be any voices
562 			if (missingTypes[TYPE_SPEECH1] && missingTypes[TYPE_SPEECH2])
563 				warning("Unable to find the speech files. The game will work, but you won't hear any voice output.\n"
564 				        "Please copy the SPEECH.CLU files from both CDs and rename them to SPEECH1.CLU and SPEECH2.CLU,\n"
565 				        "corresponding to the CD number.\n"
566 				        "Please read the ScummVM Readme file for more information");
567 			else
568 				warning("Unable to find the speech file from CD %d.\n"
569 				        "You won't hear any voice output in that part of the game.\n"
570 				        "Please read the ScummVM Readme file for more information", missingTypes[TYPE_SPEECH1] ? 1 : 2);
571 		} else if (missingTypes[TYPE_DEMO]) {
572 			// for the demo version, we simply expect to have all files immediately
573 			showFileErrorMsg(TYPE_IMMED, fileExists);
574 		}
575 	} // everything's fine, let's play.
576 	/*if (!isFullVersion)
577 		_systemVars.isDemo = true;
578 	*/
579 	// make the demo flag depend on the Gamesettings for now, and not on what the datafiles look like
580 	_systemVars.isDemo = (_features & GF_DEMO) != 0;
581 
582 	// Spanish demo has proper speech.clu and uses normal sound and var mapping
583 	_systemVars.isSpanishDemo = (_systemVars.isDemo && foundTypes[TYPE_SPEECH]) != 0;
584 }
585 
go()586 Common::Error SwordEngine::go() {
587 	_control->checkForOldSaveGames();
588 	setTotalPlayTime(0);
589 
590 	uint16 startPos = ConfMan.getInt("boot_param");
591 	_control->readSavegameDescriptions();
592 	if (startPos) {
593 		_logic->startPositions(startPos);
594 	} else {
595 		int saveSlot = ConfMan.getInt("save_slot");
596 		// Savegames are numbered starting from 1 in the dialog window,
597 		// but their filenames are numbered starting from 0.
598 		if (saveSlot >= 0 && _control->savegamesExist() && _control->restoreGameFromFile(saveSlot)) {
599 			_control->doRestore();
600 		} else if (_control->savegamesExist()) {
601 			_systemVars.controlPanelMode = CP_NEWGAME;
602 			if (_control->runPanel() == CONTROL_GAME_RESTORED)
603 				_control->doRestore();
604 			else if (!shouldQuit())
605 				_logic->startPositions(0);
606 		} else {
607 			// no savegames, start new game.
608 			_logic->startPositions(0);
609 		}
610 	}
611 	_systemVars.controlPanelMode = CP_NORMAL;
612 
613 	while (!shouldQuit()) {
614 		uint8 action = mainLoop();
615 
616 		if (!shouldQuit()) {
617 			// the mainloop was left, we have to reinitialize.
618 			reinitialize();
619 			if (action == CONTROL_GAME_RESTORED)
620 				_control->doRestore();
621 			else if (action == CONTROL_RESTART_GAME)
622 				_logic->startPositions(1);
623 			_systemVars.forceRestart = false;
624 			_systemVars.controlPanelMode = CP_NORMAL;
625 		}
626 	}
627 
628 	return Common::kNoError;
629 }
630 
checkCd()631 void SwordEngine::checkCd() {
632 	uint8 needCd = _cdList[Logic::_scriptVars[NEW_SCREEN]];
633 	if (_systemVars.runningFromCd) { // are we running from cd?
634 		if (needCd == 0) { // needCd == 0 means we can use either CD1 or CD2.
635 			if (_systemVars.currentCD == 0) {
636 				_systemVars.currentCD = 1; // if there is no CD currently inserted, ask for CD1.
637 				_control->askForCd();
638 			} // else: there is already a cd inserted and we don't care if it's cd1 or cd2.
639 		} else if (needCd != _systemVars.currentCD) { // we need a different CD than the one in drive.
640 			_music->startMusic(0, 0); //
641 			_sound->closeCowSystem(); // close music and sound files before changing CDs
642 			_systemVars.currentCD = needCd; // askForCd will ask the player to insert _systemVars.currentCd,
643 			_control->askForCd();           // so it has to be updated before calling it.
644 		}
645 	} else {        // we're running from HDD, we don't have to care about music files and Sound will take care of
646 		if (needCd) // switching sound.clu files on Sound::newScreen by itself, so there's nothing to be done.
647 			_systemVars.currentCD = needCd;
648 		else if (_systemVars.currentCD == 0)
649 			_systemVars.currentCD = 1;
650 	}
651 }
652 
mainLoop()653 uint8 SwordEngine::mainLoop() {
654 	uint8 retCode = 0;
655 	_keyPressed.reset();
656 
657 	while ((retCode == 0) && (!shouldQuit())) {
658 		// do we need the section45-hack from sword.c here?
659 		checkCd();
660 
661 		_screen->newScreen(Logic::_scriptVars[NEW_SCREEN]);
662 		_logic->newScreen(Logic::_scriptVars[NEW_SCREEN]);
663 		_sound->newScreen(Logic::_scriptVars[NEW_SCREEN]);
664 		Logic::_scriptVars[SCREEN] = Logic::_scriptVars[NEW_SCREEN];
665 
666 		do {
667 			uint32 newTime;
668 			bool scrollFrameShown = false;
669 
670 			uint32 frameTime = _system->getMillis();
671 			_logic->engine();
672 			_logic->updateScreenParams(); // sets scrolling
673 
674 			_screen->draw();
675 			_mouse->animate();
676 			_sound->engine();
677 			_menu->refresh(MENU_TOP);
678 			_menu->refresh(MENU_BOT);
679 
680 			newTime = _system->getMillis();
681 			if (newTime - frameTime < 1000 / FRAME_RATE) {
682 				scrollFrameShown = _screen->showScrollFrame();
683 				delay((1000 / (FRAME_RATE * 2)) - (_system->getMillis() - frameTime));
684 			}
685 
686 			newTime = _system->getMillis();
687 			if ((newTime - frameTime < 1000 / FRAME_RATE) || (!scrollFrameShown))
688 				_screen->updateScreen();
689 			delay((1000 / FRAME_RATE) - (_system->getMillis() - frameTime));
690 
691 			_mouse->engine(_mouseCoord.x, _mouseCoord.y, _mouseState);
692 
693 			if (_systemVars.forceRestart)
694 				retCode = CONTROL_RESTART_GAME;
695 
696 			// The control panel is triggered by F5 or ESC.
697 			else if (((_keyPressed.keycode == Common::KEYCODE_F5 || _keyPressed.keycode == Common::KEYCODE_ESCAPE)
698 			          && (Logic::_scriptVars[MOUSE_STATUS] & 1)) || (_systemVars.controlPanelMode)) {
699 				retCode = _control->runPanel();
700 				if (retCode == CONTROL_NOTHING_DONE)
701 					_screen->fullRefresh();
702 			}
703 
704 			_mouseState = 0;
705 			_keyPressed.reset();
706 		} while ((Logic::_scriptVars[SCREEN] == Logic::_scriptVars[NEW_SCREEN]) && (retCode == 0) && (!shouldQuit()));
707 
708 		if ((retCode == 0) && (Logic::_scriptVars[SCREEN] != 53) && _systemVars.wantFade && (!shouldQuit())) {
709 			_screen->fadeDownPalette();
710 			int32 relDelay = (int32)_system->getMillis();
711 			while (_screen->stillFading()) {
712 				relDelay += (1000 / FRAME_RATE);
713 				_screen->updateScreen();
714 				delay(relDelay - (int32)_system->getMillis());
715 			}
716 		}
717 
718 		_sound->quitScreen();
719 		_screen->quitScreen(); // close graphic resources
720 		_objectMan->closeSection(Logic::_scriptVars[SCREEN]); // close the section that PLAYER has just left, if it's empty now
721 	}
722 	return retCode;
723 }
724 
delay(int32 amount)725 void SwordEngine::delay(int32 amount) { //copied and mutilated from sky.cpp
726 
727 	Common::Event event;
728 	uint32 start = _system->getMillis();
729 
730 	do {
731 		while (_eventMan->pollEvent(event)) {
732 			switch (event.type) {
733 			case Common::EVENT_KEYDOWN:
734 				_keyPressed = event.kbd;
735 				break;
736 			case Common::EVENT_MOUSEMOVE:
737 				_mouseCoord = event.mouse;
738 				break;
739 			case Common::EVENT_LBUTTONDOWN:
740 				_mouseState |= BS1L_BUTTON_DOWN;
741 				_mouseCoord = event.mouse;
742 				break;
743 			case Common::EVENT_RBUTTONDOWN:
744 				_mouseState |= BS1R_BUTTON_DOWN;
745 				_mouseCoord = event.mouse;
746 				break;
747 			case Common::EVENT_LBUTTONUP:
748 				_mouseState |= BS1L_BUTTON_UP;
749 				_mouseCoord = event.mouse;
750 				break;
751 			case Common::EVENT_RBUTTONUP:
752 				_mouseState |= BS1R_BUTTON_UP;
753 				_mouseCoord = event.mouse;
754 				break;
755 			default:
756 				break;
757 			}
758 		}
759 
760 		_system->updateScreen();
761 
762 		if (amount > 0)
763 			_system->delayMillis(10);
764 
765 	} while (_system->getMillis() < start + amount);
766 }
767 
mouseIsActive()768 bool SwordEngine::mouseIsActive() {
769 	return Logic::_scriptVars[MOUSE_STATUS] & 1;
770 }
771 
772 // The following function is needed to restore proper status after GMM load game
reinitRes()773 void SwordEngine::reinitRes() {
774 	checkCd(); // Reset currentCD var to correct value
775 	_screen->newScreen(Logic::_scriptVars[NEW_SCREEN]);
776 	_logic->newScreen(Logic::_scriptVars[NEW_SCREEN]);
777 	_sound->newScreen(Logic::_scriptVars[NEW_SCREEN]);
778 	Logic::_scriptVars[SCREEN] = Logic::_scriptVars[NEW_SCREEN];
779 	_logic->engine();
780 	_logic->updateScreenParams();
781 	_screen->fullRefresh();
782 	_screen->draw();
783 }
784 
785 } // End of namespace Sword1
786