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