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 "kyra/kyra_v1.h"
24 #include "kyra/sound/sound_intern.h"
25 #include "kyra/resource/resource.h"
26 #include "kyra/engine/timer.h"
27 #include "kyra/gui/debugger.h"
28 
29 #include "common/error.h"
30 #include "common/config-manager.h"
31 #include "common/debug-channels.h"
32 
33 namespace Kyra {
34 
KyraEngine_v1(OSystem * system,const GameFlags & flags)35 KyraEngine_v1::KyraEngine_v1(OSystem *system, const GameFlags &flags)
36 	: Engine(system), _flags(flags), _rnd("kyra") {
37 	_res = 0;
38 	_sound = 0;
39 	_text = 0;
40 	_staticres = 0;
41 	_timer = 0;
42 	_emc = 0;
43 	_debugger = 0;
44 
45 	_configRenderMode = Common::kRenderDefault;
46 
47 	if (_flags.platform == Common::kPlatformAmiga)
48 		_gameSpeed = 50;
49 	else
50 		_gameSpeed = 60;
51 	_tickLength = (uint8)(1000.0 / _gameSpeed);
52 
53 	_trackMap = 0;
54 	_trackMapSize = 0;
55 	_lastMusicCommand = -1;
56 	_curSfxFile = _curMusicTheme = -1;
57 
58 	_gameToLoad = -1;
59 
60 	_mouseState = -1;
61 	_deathHandler = -1;
62 
63 	memset(_flagsTable, 0, sizeof(_flagsTable));
64 
65 	_isSaveAllowed = false;
66 
67 	_mouseX = _mouseY = 0;
68 	_asciiCodeEvents = _kbEventSkip = false;
69 
70 	// sets up all engine specific debug levels
71 	DebugMan.addDebugChannel(kDebugLevelScriptFuncs, "ScriptFuncs", "Script function debug level");
72 	DebugMan.addDebugChannel(kDebugLevelScript, "Script", "Script interpreter debug level");
73 	DebugMan.addDebugChannel(kDebugLevelSprites, "Sprites", "Sprite debug level");
74 	DebugMan.addDebugChannel(kDebugLevelScreen, "Screen", "Screen debug level");
75 	DebugMan.addDebugChannel(kDebugLevelSound, "Sound", "Sound debug level");
76 	DebugMan.addDebugChannel(kDebugLevelAnimator, "Animator", "Animator debug level");
77 	DebugMan.addDebugChannel(kDebugLevelMain, "Main", "Generic debug level");
78 	DebugMan.addDebugChannel(kDebugLevelGUI, "GUI", "GUI debug level");
79 	DebugMan.addDebugChannel(kDebugLevelSequence, "Sequence", "Sequence debug level");
80 	DebugMan.addDebugChannel(kDebugLevelMovie, "Movie", "Movie debug level");
81 	DebugMan.addDebugChannel(kDebugLevelTimer, "Timer", "Timer debug level");
82 }
83 
getDebugger()84 ::GUI::Debugger *KyraEngine_v1::getDebugger() {
85 	return _debugger;
86 }
87 
pauseEngineIntern(bool pause)88 void KyraEngine_v1::pauseEngineIntern(bool pause) {
89 	Engine::pauseEngineIntern(pause);
90 	if (_sound)
91 		_sound->pause(pause);
92 	if (_timer)
93 		_timer->pause(pause);
94 }
95 
init()96 Common::Error KyraEngine_v1::init() {
97 	// Setup mixer
98 	syncSoundSettings();
99 
100 	if (!_flags.useDigSound) {
101 		if (_flags.platform == Common::kPlatformFMTowns) {
102 			if (_flags.gameID == GI_KYRA1)
103 				_sound = new SoundTowns_LoK(this, _mixer);
104 			else
105 				_sound = new SoundTownsPC98_v2(this, _mixer);
106 		} else if (_flags.platform == Common::kPlatformPC98) {
107 			if (_flags.gameID == GI_KYRA1)
108 				_sound = new SoundPC98_LoK(this, _mixer);
109 			else
110 				_sound = new SoundTownsPC98_v2(this, _mixer);
111 		} else if (_flags.platform == Common::kPlatformAmiga) {
112 			_sound = new SoundAmiga_LoK(this, _mixer);
113 		} else {
114 			// In Kyra 1 users who have specified a default MT-32 device in the launcher settings
115 			// will get MT-32 music, otherwise AdLib. In Kyra 2 and LoL users who have specified a
116 			// default GM device in the launcher will get GM music, otherwise AdLib. Users who want
117 			// MT-32 music in Kyra2 or LoL have to select this individually (since we assume that
118 			// most users rather have a GM device than a MT-32 device).
119 			// Users who want PC speaker sound always have to select this individually for all
120 			// Kyra games.
121 			MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : MDT_PREFER_MT32));
122 			if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
123 				_sound = new SoundAdLibPC(this, _mixer);
124 			} else {
125 				Sound::kType type;
126 				const MusicType midiType = MidiDriver::getMusicType(dev);
127 
128 				if (midiType == MT_PCSPK || midiType == MT_NULL)
129 					type = Sound::kPCSpkr;
130 				else if (midiType == MT_MT32 || ConfMan.getBool("native_mt32"))
131 					type = Sound::kMidiMT32;
132 				else
133 					type = Sound::kMidiGM;
134 
135 				MidiDriver *driver = 0;
136 
137 				if (MidiDriver::getMusicType(dev) == MT_PCSPK) {
138 					driver = new MidiDriver_PCSpeaker(_mixer);
139 				} else {
140 					driver = MidiDriver::createMidi(dev);
141 					if (type == Sound::kMidiMT32)
142 						driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
143 				}
144 
145 				assert(driver);
146 
147 				SoundMidiPC *soundMidiPc = new SoundMidiPC(this, _mixer, driver, type);
148 				_sound = soundMidiPc;
149 				assert(_sound);
150 
151 				// Unlike some SCUMM games, it's not that the MIDI sounds are
152 				// missing. It's just that at least at the time of writing they
153 				// are decidedly inferior to the AdLib ones.
154 				if (ConfMan.getBool("multi_midi")) {
155 					SoundAdLibPC *adlib = new SoundAdLibPC(this, _mixer);
156 					assert(adlib);
157 
158 					_sound = new MixedSoundDriver(this, _mixer, soundMidiPc, adlib);
159 				}
160 			}
161 		}
162 
163 		assert(_sound);
164 	}
165 
166 	if (_sound)
167 		_sound->updateVolumeSettings();
168 
169 	if (ConfMan.hasKey("render_mode"))
170 		_configRenderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
171 
172 	_res = new Resource(this);
173 	assert(_res);
174 	_res->reset();
175 
176 	_staticres = new StaticResource(this);
177 	assert(_staticres);
178 	if (!_staticres->init())
179 		error("_staticres->init() failed");
180 	assert(screen());
181 	if (!screen()->init())
182 		error("screen()->init() failed");
183 	_timer = new TimerManager(this, _system);
184 	assert(_timer);
185 	setupTimers();
186 	_emc = new EMCInterpreter(this);
187 	assert(_emc);
188 
189 	setupOpcodeTable();
190 	readSettings();
191 
192 	if (ConfMan.hasKey("save_slot")) {
193 		_gameToLoad = ConfMan.getInt("save_slot");
194 		if (!saveFileLoadable(_gameToLoad))
195 			_gameToLoad = -1;
196 	}
197 
198 	setupKeyMap();
199 
200 	// Prevent autosave on game startup
201 	_lastAutosave = _system->getMillis();
202 
203 	return Common::kNoError;
204 }
205 
~KyraEngine_v1()206 KyraEngine_v1::~KyraEngine_v1() {
207 	for (Common::Array<const Opcode *>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i)
208 		delete *i;
209 	_opcodes.clear();
210 	_keyMap.clear();
211 
212 	delete _res;
213 	delete _staticres;
214 	delete _sound;
215 	delete _text;
216 	delete _timer;
217 	delete _emc;
218 	delete _debugger;
219 }
220 
getMousePos()221 Common::Point KyraEngine_v1::getMousePos() {
222 	Common::Point mouse = _eventMan->getMousePos();
223 
224 	if (_flags.useHiRes) {
225 		mouse.x >>= 1;
226 		mouse.y >>= 1;
227 	}
228 
229 	return mouse;
230 }
231 
setMousePos(int x,int y)232 void KyraEngine_v1::setMousePos(int x, int y) {
233 	if (_flags.useHiRes) {
234 		x <<= 1;
235 		y <<= 1;
236 	}
237 	_system->warpMouse(x, y);
238 
239 	// Feed the event manager an artficial mouse move event, since warpMouse() won't generate one.
240 	// From the warpMouse comments I gather that this behavior is intentional due to requirements of
241 	// the SCUMM engine. In KYRA we need to get the same coordinates from _eventMan->getMousePos()
242 	// that we send via warpMouse(). We have script situations in Kyra (like the Alchemists' crystals
243 	// scene) where a new mouse cursor position is set and then immediately read. This function would
244 	// then get wrong coordinates.
245 	Common::Event event;
246 	event.type = Common::EVENT_MOUSEMOVE;
247 	event.mouse.x = x;
248 	event.mouse.y = y;
249 	_eventMan->pushEvent(event);
250 	updateInput();
251 }
252 
checkInput(Button * buttonList,bool mainLoop,int eventFlag)253 int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag) {
254 	_isSaveAllowed = mainLoop;
255 	updateInput();
256 	_isSaveAllowed = false;
257 
258 	if (mainLoop)
259 		checkAutosave();
260 
261 	int keys = 0;
262 	int8 mouseWheel = 0;
263 
264 	while (!_eventList.empty()) {
265 		Common::Event event = *_eventList.begin();
266 		bool breakLoop = false;
267 
268 		switch (event.type) {
269 		case Common::EVENT_KEYDOWN:
270 			if (event.kbd.keycode >= Common::KEYCODE_1 && event.kbd.keycode <= Common::KEYCODE_9 &&
271 			        (event.kbd.hasFlags(Common::KBD_CTRL) || event.kbd.hasFlags(Common::KBD_ALT)) && mainLoop) {
272 				int saveLoadSlot = 9 - (event.kbd.keycode - Common::KEYCODE_0) + 990;
273 
274 				if (event.kbd.hasFlags(Common::KBD_CTRL)) {
275 					if (saveFileLoadable(saveLoadSlot))
276 						loadGameStateCheck(saveLoadSlot);
277 					_eventList.clear();
278 					breakLoop = true;
279 				} else {
280 					char savegameName[14];
281 					sprintf(savegameName, "Quicksave %d", event.kbd.keycode - Common::KEYCODE_0);
282 					saveGameStateIntern(saveLoadSlot, savegameName, 0);
283 				}
284 			} else if (event.kbd.hasFlags(Common::KBD_CTRL)) {
285 				if (event.kbd.keycode == Common::KEYCODE_d) {
286 					if (_debugger)
287 						_debugger->attach();
288 					breakLoop = true;
289 				} else if (event.kbd.keycode == Common::KEYCODE_q) {
290 					quitGame();
291 				}
292 			} else {
293 				KeyMap::const_iterator keycode = _keyMap.find(event.kbd.keycode);
294 				if (_asciiCodeEvents) {
295 					keys = event.kbd.ascii;
296 				} else if (keycode != _keyMap.end()) {
297 					keys = keycode->_value;
298 					if (event.kbd.flags & Common::KBD_SHIFT)
299 						keys |= 0x100;
300 				} else {
301 					keys = 0;
302 				}
303 
304 				// When we got an keypress, which we might need to handle,
305 				// break the event loop and pass it to GUI code.
306 				if (keys)
307 					breakLoop = true;
308 			}
309 			break;
310 
311 		case Common::EVENT_LBUTTONDOWN:
312 		case Common::EVENT_LBUTTONUP: {
313 			_mouseX = event.mouse.x;
314 			_mouseY = event.mouse.y;
315 			if (_flags.useHiRes) {
316 				_mouseX >>= 1;
317 				_mouseY >>= 1;
318 			}
319 			keys = (event.type == Common::EVENT_LBUTTONDOWN ? 199 : (200 | 0x800));
320 			breakLoop = true;
321 			} break;
322 
323 		case Common::EVENT_RBUTTONDOWN:
324 		case Common::EVENT_RBUTTONUP: {
325 			_mouseX = event.mouse.x;
326 			_mouseY = event.mouse.y;
327 			if (_flags.useHiRes) {
328 				_mouseX >>= 1;
329 				_mouseY >>= 1;
330 			}
331 			keys = (event.type == Common::EVENT_RBUTTONDOWN ? 201 : (202 | 0x800));
332 			breakLoop = true;
333 			} break;
334 
335 		case Common::EVENT_WHEELUP:
336 			mouseWheel = -1;
337 			break;
338 
339 		case Common::EVENT_WHEELDOWN:
340 			mouseWheel = 1;
341 			break;
342 
343 		default:
344 			break;
345 		}
346 
347 		if (_debugger)
348 			_debugger->onFrame();
349 
350 		if (breakLoop)
351 			break;
352 
353 		_eventList.erase(_eventList.begin());
354 	}
355 
356 	GUI *guiInstance = gui();
357 	if (guiInstance) {
358 		if (keys)
359 			return guiInstance->processButtonList(buttonList, keys | eventFlag, mouseWheel);
360 		else
361 			return guiInstance->processButtonList(buttonList, 0, mouseWheel);
362 	} else {
363 		return keys;
364 	}
365 }
366 
setupKeyMap()367 void KyraEngine_v1::setupKeyMap() {
368 	struct KeyCodeMapEntry {
369 		Common::KeyCode kcScummVM;
370 		int16 kcDOS;
371 		int16 kcPC98;
372 		int16 kcFMTowns;
373 	};
374 
375 #define UNKNOWN_KEYCODE -1
376 #define KC(x) Common::KEYCODE_##x
377 	static const KeyCodeMapEntry keys[] = {
378 		{ KC(SPACE), 61, 53, 32 },
379 		{ KC(RETURN), 43, 29, 13 },
380 		{ KC(UP), 96, 68, 30 },
381 		{ KC(KP8), 96, 68, 30 },
382 		{ KC(RIGHT), 102, 73, 28 },
383 		{ KC(KP6), 102, 73, 28 },
384 		{ KC(DOWN), 98, 76, 31 },
385 		{ KC(KP2), 98, 76, 31 },
386 		{ KC(KP5), 97, 72, UNKNOWN_KEYCODE },
387 		{ KC(LEFT), 92, 71, 29 },
388 		{ KC(KP4), 92, 71, 29 },
389 		{ KC(HOME), 91, 67, 127 },
390 		{ KC(KP7), 91, 67, 127 },
391 		{ KC(PAGEUP), 101, 69, 18 },
392 		{ KC(KP9), 101, 69, 18 },
393 		{ KC(END), 93, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE },
394 		{ KC(KP1), 93, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE },
395 		{ KC(PAGEDOWN), 103, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE },
396 		{ KC(KP3), 103, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE },
397 		{ KC(F1), 112, 99, 93 },
398 		{ KC(F2), 113, 100, 94 },
399 		{ KC(F3), 114, 101, 95 },
400 		{ KC(F4), 115, 102, 96 },
401 		{ KC(F5), 116, 103, 97 },
402 		{ KC(F6), 117, 104, 98 },
403 		{ KC(a), 31, 31, UNKNOWN_KEYCODE },
404 		{ KC(b), 50, 50, 66 },
405 		{ KC(c), 48, 48, 67 },
406 		{ KC(d), 33, 33, 68 },
407 		{ KC(e), 19, 19, UNKNOWN_KEYCODE },
408 		{ KC(f), 34, 34, 70 },
409 		{ KC(i), 24, 24, 24 },
410 		{ KC(k), 38, 38, 75 },
411 		{ KC(m), 52, 52, 77 },
412 		{ KC(n), 51, 51, UNKNOWN_KEYCODE },
413 		{ KC(o), 25, 25, 79 },
414 		{ KC(p), 26, 26, 80 },
415 		{ KC(r), 20, 20, 82 },
416 		{ KC(s), 32, 32, UNKNOWN_KEYCODE },
417 		{ KC(w), 18, 18, UNKNOWN_KEYCODE },
418 		{ KC(y), 22, 22, UNKNOWN_KEYCODE },
419 		{ KC(z), 46, 46, UNKNOWN_KEYCODE },
420 		{ KC(0), UNKNOWN_KEYCODE, UNKNOWN_KEYCODE, 48 },
421 		{ KC(1), 2, UNKNOWN_KEYCODE, 49 },
422 		{ KC(2), 3, UNKNOWN_KEYCODE, 50 },
423 		{ KC(3), 4, UNKNOWN_KEYCODE, 51 },
424 		{ KC(4), 5, UNKNOWN_KEYCODE, 52 },
425 		{ KC(5), 6, UNKNOWN_KEYCODE, 53 },
426 		{ KC(6), 7, UNKNOWN_KEYCODE, 54 },
427 		{ KC(7), 8, UNKNOWN_KEYCODE, 55 },
428 		{ KC(SLASH), 55, 55, 47 },
429 		{ KC(ESCAPE), 110, 1, 27 },
430 		{ KC(MINUS), 12, UNKNOWN_KEYCODE, 45 },
431 		{ KC(KP_MINUS), 105, UNKNOWN_KEYCODE, 45 },
432 		{ KC(PLUS), 13, UNKNOWN_KEYCODE, 43 },
433 		{ KC(KP_PLUS), 106, UNKNOWN_KEYCODE, 43 },
434 
435 		// Multiple mappings for the keys to the right of the 'M' key,
436 		// since these are different for QWERTZ, QWERTY and AZERTY keyboards.
437 		// QWERTZ
438 		{ KC(COMMA), 53, UNKNOWN_KEYCODE, 60 },
439 		{ KC(PERIOD), 54, UNKNOWN_KEYCODE, 62 },
440 		// AZERTY
441 		{ KC(SEMICOLON), 53, UNKNOWN_KEYCODE, 60 },
442 		{ KC(COLON), 54, UNKNOWN_KEYCODE, 62 },
443 		// QWERTY
444 		{ KC(LESS), 53, UNKNOWN_KEYCODE, 60 },
445 		{ KC(GREATER), 54, UNKNOWN_KEYCODE, 62 }
446 	};
447 #undef KC
448 #undef UNKNOWN_KEYCODE
449 
450 	_keyMap.clear();
451 
452 	// If we have an engine that wants ASCII codes instead of key codes, we can skip the setup of the key map.
453 	// In that case we simply return the ASCII codes from the event manager. At least until I know better I
454 	// trust that the ASCII codes we get from our event manager are the same identical codes. If that assumption
455 	// turns out to be wrong I can still implement the original conversion method...
456 	if (_asciiCodeEvents)
457 		return;
458 
459 	for (int i = 0; i < ARRAYSIZE(keys); i++)
460 		_keyMap[keys[i].kcScummVM] = (_flags.platform == Common::kPlatformPC98) ? keys[i].kcPC98 : ((_flags.platform == Common::kPlatformFMTowns) ? keys[i].kcFMTowns : keys[i].kcDOS);
461 }
462 
updateInput()463 void KyraEngine_v1::updateInput() {
464 	Common::Event event;
465 
466 	bool updateScreen = false;
467 
468 	while (_eventMan->pollEvent(event)) {
469 		switch (event.type) {
470 		case Common::EVENT_KEYDOWN:
471 			if (event.kbd.keycode == Common::KEYCODE_PERIOD || event.kbd.keycode == Common::KEYCODE_ESCAPE ||
472 			        event.kbd.keycode == Common::KEYCODE_SPACE || event.kbd.keycode == Common::KEYCODE_RETURN ||
473 			        event.kbd.keycode == Common::KEYCODE_UP || event.kbd.keycode == Common::KEYCODE_RIGHT ||
474 			        event.kbd.keycode == Common::KEYCODE_DOWN || event.kbd.keycode == Common::KEYCODE_LEFT)
475 				_eventList.push_back(Event(event, true));
476 			else if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL))
477 				quitGame();
478 			else
479 				_eventList.push_back(Event(event, _kbEventSkip));
480 			break;
481 
482 		case Common::EVENT_LBUTTONDOWN:
483 		case Common::EVENT_RBUTTONDOWN:
484 			_eventList.push_back(Event(event, true));
485 			break;
486 
487 		case Common::EVENT_MOUSEMOVE:
488 			if (screen()->isMouseVisible())
489 				updateScreen = true;
490 			break;
491 
492 		case Common::EVENT_LBUTTONUP:
493 		case Common::EVENT_RBUTTONUP:
494 		case Common::EVENT_WHEELUP:
495 		case Common::EVENT_WHEELDOWN:
496 			_eventList.push_back(event);
497 			break;
498 
499 		default:
500 			break;
501 		}
502 	}
503 
504 	if (updateScreen)
505 		_system->updateScreen();
506 }
507 
removeInputTop()508 void KyraEngine_v1::removeInputTop() {
509 	if (!_eventList.empty())
510 		_eventList.erase(_eventList.begin());
511 }
512 
skipFlag() const513 bool KyraEngine_v1::skipFlag() const {
514 	for (Common::List<Event>::const_iterator i = _eventList.begin(); i != _eventList.end(); ++i) {
515 		if (i->causedSkip)
516 			return true;
517 	}
518 	return false;
519 }
520 
resetSkipFlag(bool removeEvent)521 void KyraEngine_v1::resetSkipFlag(bool removeEvent) {
522 	for (Common::List<Event>::iterator i = _eventList.begin(); i != _eventList.end(); ++i) {
523 		if (i->causedSkip) {
524 			if (removeEvent)
525 				_eventList.erase(i);
526 			else
527 				i->causedSkip = false;
528 			return;
529 		}
530 	}
531 }
532 
setGameFlag(int flag)533 int KyraEngine_v1::setGameFlag(int flag) {
534 	assert((flag >> 3) >= 0 && (flag >> 3) <= ARRAYSIZE(_flagsTable));
535 	_flagsTable[flag >> 3] |= (1 << (flag & 7));
536 	return 1;
537 }
538 
queryGameFlag(int flag) const539 int KyraEngine_v1::queryGameFlag(int flag) const {
540 	assert((flag >> 3) >= 0 && (flag >> 3) <= ARRAYSIZE(_flagsTable));
541 	return ((_flagsTable[flag >> 3] >> (flag & 7)) & 1);
542 }
543 
resetGameFlag(int flag)544 int KyraEngine_v1::resetGameFlag(int flag) {
545 	assert((flag >> 3) >= 0 && (flag >> 3) <= ARRAYSIZE(_flagsTable));
546 	_flagsTable[flag >> 3] &= ~(1 << (flag & 7));
547 	return 0;
548 }
549 
delayUntil(uint32 timestamp,bool updateTimers,bool update,bool isMainLoop)550 void KyraEngine_v1::delayUntil(uint32 timestamp, bool updateTimers, bool update, bool isMainLoop) {
551 	const uint32 curTime = _system->getMillis();
552 	if (curTime > timestamp)
553 		return;
554 
555 	uint32 del = timestamp - curTime;
556 	while (del && !shouldQuit()) {
557 		uint32 step = MIN<uint32>(del, _tickLength);
558 		delay(step, update, isMainLoop);
559 		del -= step;
560 	}
561 }
562 
delay(uint32 amount,bool update,bool isMainLoop)563 void KyraEngine_v1::delay(uint32 amount, bool update, bool isMainLoop) {
564 	_system->delayMillis(amount);
565 }
566 
delayWithTicks(int ticks)567 void KyraEngine_v1::delayWithTicks(int ticks) {
568 	delay(ticks * _tickLength);
569 }
570 
registerDefaultSettings()571 void KyraEngine_v1::registerDefaultSettings() {
572 	if (_flags.platform == Common::kPlatformFMTowns)
573 		ConfMan.registerDefault("cdaudio", true);
574 	if (_flags.fanLang != Common::UNK_LANG) {
575 		// HACK/WORKAROUND: Since we can't use registerDefault here to overwrite
576 		// the global subtitles settings, we're using this hack to enable subtitles
577 		// for fan translations
578 		const Common::ConfigManager::Domain *cur = ConfMan.getActiveDomain();
579 		if (!cur || (cur && cur->getVal("subtitles").empty()))
580 			ConfMan.setBool("subtitles", true);
581 	}
582 }
583 
readSettings()584 void KyraEngine_v1::readSettings() {
585 	_configWalkspeed = ConfMan.getInt("walkspeed");
586 	_configMusic = 0;
587 
588 	if (!ConfMan.getBool("music_mute")) {
589 		if (_flags.platform == Common::kPlatformFMTowns)
590 			_configMusic = ConfMan.getBool("cdaudio") ? 2 : 1;
591 		else
592 			_configMusic = 1;
593 	}
594 	_configSounds = ConfMan.getBool("sfx_mute") ? 0 : 1;
595 
596 	if (_sound) {
597 		_sound->enableMusic(_configMusic);
598 		_sound->enableSFX(_configSounds);
599 	}
600 
601 	bool speechMute = ConfMan.getBool("speech_mute");
602 	bool subtitles = ConfMan.getBool("subtitles");
603 
604 	if (!speechMute && subtitles)
605 		_configVoice = 2;   // Voice & Text
606 	else if (!speechMute && !subtitles)
607 		_configVoice = 1;   // Voice only
608 	else
609 		_configVoice = 0;   // Text only
610 
611 	setWalkspeed(_configWalkspeed);
612 }
613 
writeSettings()614 void KyraEngine_v1::writeSettings() {
615 	bool speechMute, subtitles;
616 
617 	ConfMan.setInt("walkspeed", _configWalkspeed);
618 	ConfMan.setBool("music_mute", _configMusic == 0);
619 	if (_flags.platform == Common::kPlatformFMTowns)
620 		ConfMan.setBool("cdaudio", _configMusic == 2);
621 	ConfMan.setBool("sfx_mute", _configSounds == 0);
622 
623 	switch (_configVoice) {
624 	case 0:     // Text only
625 		speechMute = true;
626 		subtitles = true;
627 		break;
628 	case 1:     // Voice only
629 		speechMute = false;
630 		subtitles = false;
631 		break;
632 	default:    // Voice & Text
633 		speechMute = false;
634 		subtitles = true;
635 	}
636 
637 	if (_sound) {
638 		if (!_configMusic)
639 			_sound->beginFadeOut();
640 		_sound->enableMusic(_configMusic);
641 		_sound->enableSFX(_configSounds);
642 	}
643 
644 	ConfMan.setBool("speech_mute", speechMute);
645 	ConfMan.setBool("subtitles", subtitles);
646 
647 	ConfMan.flushToDisk();
648 }
649 
speechEnabled()650 bool KyraEngine_v1::speechEnabled() {
651 	return _flags.isTalkie && (_configVoice == 1 || _configVoice == 2);
652 }
653 
textEnabled()654 bool KyraEngine_v1::textEnabled() {
655 	return !_flags.isTalkie || (_configVoice == 0 || _configVoice == 2);
656 }
657 
convertVolumeToMixer(int value)658 int KyraEngine_v1::convertVolumeToMixer(int value) {
659 	value -= 2;
660 	return (value * Audio::Mixer::kMaxMixerVolume) / 95;
661 }
662 
convertVolumeFromMixer(int value)663 int KyraEngine_v1::convertVolumeFromMixer(int value) {
664 	return (value * 95) / Audio::Mixer::kMaxMixerVolume + 2;
665 }
666 
setVolume(kVolumeEntry vol,uint8 value)667 void KyraEngine_v1::setVolume(kVolumeEntry vol, uint8 value) {
668 	switch (vol) {
669 	case kVolumeMusic:
670 		ConfMan.setInt("music_volume", convertVolumeToMixer(value));
671 		break;
672 
673 	case kVolumeSfx:
674 		ConfMan.setInt("sfx_volume", convertVolumeToMixer(value));
675 		break;
676 
677 	case kVolumeSpeech:
678 		ConfMan.setInt("speech_volume", convertVolumeToMixer(value));
679 		break;
680 	}
681 
682 	// Resetup mixer
683 	_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
684 	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
685 	_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
686 	if (_sound)
687 		_sound->updateVolumeSettings();
688 }
689 
getVolume(kVolumeEntry vol)690 uint8 KyraEngine_v1::getVolume(kVolumeEntry vol) {
691 	switch (vol) {
692 	case kVolumeMusic:
693 		return convertVolumeFromMixer(ConfMan.getInt("music_volume"));
694 
695 	case kVolumeSfx:
696 		return convertVolumeFromMixer(ConfMan.getInt("sfx_volume"));
697 
698 	case kVolumeSpeech:
699 		if (speechEnabled())
700 			return convertVolumeFromMixer(ConfMan.getInt("speech_volume"));
701 		else
702 			return 2;
703 		break;
704 	}
705 
706 	return 2;
707 }
708 
syncSoundSettings()709 void KyraEngine_v1::syncSoundSettings() {
710 	Engine::syncSoundSettings();
711 
712 	// We need to use this here to allow the subtitle options to be changed
713 	// through the GMM's options dialog.
714 	readSettings();
715 
716 	if (_sound)
717 		_sound->updateVolumeSettings();
718 }
719 
720 } // End of namespace Kyra
721