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/script/script_tim.h"
24 #include "kyra/resource/resource.h"
25 #include "kyra/sound/sound.h"
26 
27 #ifdef ENABLE_LOL
28 #include "kyra/engine/lol.h"
29 #include "kyra/graphics/screen_lol.h"
30 #endif // ENABLE_LOL
31 
32 #include "common/iff_container.h"
33 #include "common/system.h"
34 
35 namespace Kyra {
36 
TIMInterpreter(KyraEngine_v1 * engine,Screen_v2 * screen_v2,OSystem * system)37 TIMInterpreter::TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system) : _vm(engine), _screen(screen_v2), _system(system), _currentTim(0) {
38 #define COMMAND(x) { &TIMInterpreter::x, #x }
39 #define COMMAND_UNIMPL() { 0, 0 }
40 #define cmd_return(n) cmd_return_##n
41 	static const CommandEntry commandProcs[] = {
42 		// 0x00
43 		COMMAND(cmd_initFunc0),
44 		COMMAND(cmd_stopCurFunc),
45 		COMMAND(cmd_initWSA),
46 		COMMAND(cmd_uninitWSA),
47 		// 0x04
48 		COMMAND(cmd_initFunc),
49 		COMMAND(cmd_stopFunc),
50 		COMMAND(cmd_wsaDisplayFrame),
51 		COMMAND(cmd_displayText),
52 		// 0x08
53 		COMMAND(cmd_loadVocFile),
54 		COMMAND(cmd_unloadVocFile),
55 		COMMAND(cmd_playVocFile),
56 		COMMAND_UNIMPL(),
57 		// 0x0C
58 		COMMAND(cmd_loadSoundFile),
59 		COMMAND(cmd_return(1)),
60 		COMMAND(cmd_playMusicTrack),
61 		COMMAND_UNIMPL(),
62 		// 0x10
63 		COMMAND(cmd_return(1)),
64 		COMMAND(cmd_return(1)),
65 		COMMAND_UNIMPL(),
66 		COMMAND_UNIMPL(),
67 		// 0x14
68 		COMMAND(cmd_setLoopIp),
69 		COMMAND(cmd_continueLoop),
70 		COMMAND(cmd_resetLoopIp),
71 		COMMAND(cmd_resetAllRuntimes),
72 		// 0x18
73 		COMMAND(cmd_return(1)),
74 		COMMAND(cmd_execOpcode),
75 		COMMAND(cmd_initFuncNow),
76 		COMMAND(cmd_stopFuncNow),
77 		// 0x1C
78 		COMMAND(cmd_return(1)),
79 		COMMAND(cmd_return(1)),
80 		COMMAND(cmd_return(n1))
81 	};
82 #undef cmd_return
83 #undef COMMAND_UNIMPL
84 #undef COMMAND
85 
86 	_commands = commandProcs;
87 	_commandsSize = ARRAYSIZE(commandProcs);
88 
89 	_langData = 0;
90 	_textDisplayed = false;
91 	_textAreaBuffer = new uint8[320*40];
92 	assert(_textAreaBuffer);
93 	if ((_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().isDemo) && _vm->game() == GI_LOL)
94 		_drawPage2 = 0;
95 	else
96 		_drawPage2 = 8;
97 
98 	_animator = new TimAnimator(0, screen_v2, 0, false);
99 
100 	_palDelayInc = _palDiff = _palDelayAcc = 0;
101 	_abortFlag = 0;
102 	_tim = 0;
103 }
104 
~TIMInterpreter()105 TIMInterpreter::~TIMInterpreter() {
106 	delete[] _langData;
107 	delete[] _textAreaBuffer;
108 	delete _animator;
109 }
110 
callback(Common::IFFChunk & chunk)111 bool TIMInterpreter::callback(Common::IFFChunk &chunk) {
112 	switch (chunk._type) {
113 	case MKTAG('T','E','X','T'):
114 		_tim->text = new byte[chunk._size];
115 		assert(_tim->text);
116 		if (chunk._stream->read(_tim->text, chunk._size) != chunk._size)
117 			error("Couldn't read TEXT chunk from file '%s'", _filename);
118 		break;
119 
120 	case MKTAG('A','V','T','L'):
121 		_avtlChunkSize = chunk._size >> 1;
122 		_tim->avtl = new uint16[_avtlChunkSize];
123 		assert(_tim->avtl);
124 		if (chunk._stream->read(_tim->avtl, chunk._size) != chunk._size)
125 			error("Couldn't read AVTL chunk from file '%s'", _filename);
126 
127 		for (int i = _avtlChunkSize - 1; i >= 0; --i)
128 			_tim->avtl[i] = READ_LE_UINT16(&_tim->avtl[i]);
129 		break;
130 
131 	default:
132 		warning("Unexpected chunk '%s' of size %d found in file '%s'", tag2str(chunk._type), chunk._size, _filename);
133 	}
134 
135 	return false;
136 }
137 
load(const char * filename,const Common::Array<const TIMOpcode * > * opcodes)138 TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes) {
139 	if (!_vm->resource()->exists(filename))
140 		return 0;
141 
142 	Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
143 	if (!stream)
144 		error("Couldn't open TIM file '%s'", filename);
145 
146 	_avtlChunkSize = 0;
147 	_filename = filename;
148 
149 	_tim = new TIM;
150 	assert(_tim);
151 	memset(_tim, 0, sizeof(TIM));
152 
153 	_tim->procFunc = -1;
154 	_tim->opcodes = opcodes;
155 
156 	IFFParser iff(*stream);
157 	Common::Functor1Mem<Common::IFFChunk &, bool, TIMInterpreter> c(this, &TIMInterpreter::callback);
158 	iff.parse(c);
159 
160 	if (!_tim->avtl)
161 		error("No AVTL chunk found in file: '%s'", filename);
162 
163 	if (stream->err())
164 		error("Read error while parsing file '%s'", filename);
165 
166 	delete stream;
167 
168 	const int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs;
169 	for (int i = 0; i < num; ++i)
170 		_tim->func[i].avtl = _tim->avtl + _tim->avtl[i];
171 
172 	Common::strlcpy(_tim->filename, filename, 13);
173 
174 	_tim->isLoLOutro = (_vm->game() == GI_LOL) && !scumm_stricmp(filename, "LOLFINAL.TIM");
175 	_tim->lolCharacter = 0;
176 
177 	TIM *r = _tim;
178 	_tim = 0;
179 	return r;
180 }
181 
unload(TIM * & tim) const182 void TIMInterpreter::unload(TIM *&tim) const {
183 	if (!tim)
184 		return;
185 
186 	delete[] tim->text;
187 	delete[] tim->avtl;
188 	delete tim;
189 	tim = 0;
190 }
191 
setLangData(const char * filename)192 void TIMInterpreter::setLangData(const char *filename) {
193 	delete[] _langData;
194 	_langData = _vm->resource()->fileData(filename, 0);
195 }
196 
exec(TIM * tim,bool loop)197 int TIMInterpreter::exec(TIM *tim, bool loop) {
198 	if (!tim)
199 		return 0;
200 
201 	_currentTim = tim;
202 	if (!_currentTim->func[0].ip) {
203 		_currentTim->func[0].ip = _currentTim->func[0].avtl;
204 		_currentTim->func[0].nextTime = _currentTim->func[0].lastTime = _system->getMillis();
205 	}
206 
207 	do {
208 		update();
209 
210 		for (_currentFunc = 0; _currentFunc < TIM::kCountFuncs; ++_currentFunc) {
211 			TIM::Function &cur = _currentTim->func[_currentFunc];
212 
213 			if (_currentTim->procFunc != -1)
214 				execCommand(28, &_currentTim->procParam);
215 
216 			update();
217 			checkSpeechProgress();
218 
219 			bool running = true;
220 			int cnt = 0;
221 			while (cur.ip && cur.nextTime <= _system->getMillis() && running) {
222 				if (cnt++ > 0) {
223 					if (_currentTim->procFunc != -1)
224 						execCommand(28, &_currentTim->procParam);
225 					update();
226 				}
227 
228 				int8 opcode = int8(cur.ip[2] & 0xFF);
229 
230 				switch (execCommand(opcode, cur.ip + 3)) {
231 				case -1:
232 					loop = false;
233 					running = false;
234 					_currentFunc = 11;
235 					break;
236 
237 				case -2:
238 					running = false;
239 					break;
240 
241 				case -3:
242 					_currentTim->procFunc = _currentFunc;
243 					_currentTim->dlgFunc = -1;
244 					break;
245 
246 				case 22:
247 					cur.loopIp = 0;
248 					break;
249 
250 				default:
251 					break;
252 				}
253 
254 				if (cur.ip) {
255 					cur.ip += cur.ip[0];
256 					cur.lastTime = cur.nextTime;
257 					cur.nextTime += cur.ip[1] * _vm->tickLength();
258 				}
259 			}
260 		}
261 	} while (loop && !_vm->shouldQuit());
262 
263 	return _currentTim->clickedButton;
264 }
265 
refreshTimersAfterPause(uint32 elapsedTime)266 void TIMInterpreter::refreshTimersAfterPause(uint32 elapsedTime) {
267 	if (!_currentTim)
268 		return;
269 
270 	for (int i = 0; i < TIM::kCountFuncs; i++) {
271 		if (_currentTim->func[i].lastTime)
272 			_currentTim->func[i].lastTime += elapsedTime;
273 		if (_currentTim->func[i].nextTime)
274 			_currentTim->func[i].nextTime += elapsedTime;
275 	}
276 }
277 
displayText(uint16 textId,int16 flags)278 void TIMInterpreter::displayText(uint16 textId, int16 flags) {
279 	char *text = getTableEntry(textId);
280 
281 	if (_textDisplayed) {
282 		_screen->copyBlockToPage(0, 0, 160, 320, 40, _textAreaBuffer);
283 		_textDisplayed = false;
284 	}
285 
286 	if (!text)
287 		return;
288 	if (!text[0])
289 		return;
290 
291 	char filename[16];
292 	memset(filename, 0, sizeof(filename));
293 
294 	if (text[0] == '$') {
295 		const char *end = strchr(text+1, '$');
296 		if (end)
297 			memcpy(filename, text+1, end-1-text);
298 	}
299 
300 	const bool sjisMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode);
301 	if (filename[0] && (_vm->speechEnabled() || !_vm->gameFlags().isTalkie))
302 		_vm->sound()->voicePlay(filename, 0, 255, 255, !_vm->gameFlags().isTalkie);
303 
304 	if (text[0] == '$')
305 		text = strchr(text + 1, '$') + 1;
306 
307 	if (!_vm->gameFlags().use16ColorMode)
308 		setupTextPalette((flags < 0) ? 1 : flags, 0);
309 
310 	if (flags < 0) {
311 		static const uint8 colorMap[] = { 0x00, 0xF0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
312 
313 		_screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
314 		_screen->setTextColorMap(colorMap);
315 		_screen->_charWidth = -2;
316 	}
317 
318 	_screen->_charOffset = -4;
319 	_screen->copyRegionToBuffer(0, 0, 160, 320, 40, _textAreaBuffer);
320 	_textDisplayed = true;
321 
322 	char backupChar = 0;
323 	char *str = text;
324 	int heightAdd = 0;
325 
326 	while (str[0] && _vm->textEnabled()) {
327 		char *nextLine = strchr(str, '\r');
328 
329 		backupChar = 0;
330 		if (nextLine) {
331 			backupChar = nextLine[0];
332 			nextLine[0] = '\0';
333 		}
334 
335 		int width = _screen->getTextWidth(str);
336 
337 		if (flags >= 0) {
338 			if (_vm->gameFlags().use16ColorMode) {
339 				static const uint8 colorMap[] = { 0xE1, 0xE1, 0xC1, 0xA1, 0x81, 0x61 };
340 				_screen->printText(str, (320 - width) >> 1, 160 + heightAdd, colorMap[flags], 0x00);
341 			} else {
342 				_screen->printText(str, (320 - width) >> 1, 160 + heightAdd, 0xF0, 0x00);
343 			}
344 		} else {
345 			_screen->printText(str, (320 - width) >> 1, 188, 0xF0, 0x00);
346 		}
347 
348 		heightAdd += _screen->getFontHeight();
349 		str += strlen(str);
350 
351 		if (backupChar) {
352 			nextLine[0] = backupChar;
353 			++str;
354 		}
355 	}
356 
357 	_screen->_charOffset = 0;
358 
359 	if (flags < 0) {
360 		static const uint8 colorMap[] = { 0x00, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00 };
361 
362 		_screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT);
363 		_screen->setTextColorMap(colorMap);
364 		_screen->_charWidth = 0;
365 	}
366 }
367 
displayText(uint16 textId,int16 flags,uint8 color)368 void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) {
369 	if (!_vm->textEnabled() && !(textId & 0x8000))
370 		return;
371 
372 	char *text = getTableEntry(textId & 0x7FFF);
373 
374 	if (flags > 0)
375 		_screen->copyBlockToPage(0, 0, 0, 320, 40, _textAreaBuffer);
376 
377 	if (flags == 255)
378 		return;
379 
380 	_screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT);
381 
382 	static const uint8 colorMap[] = { 0x00, 0xA0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
383 	_screen->setTextColorMap(colorMap);
384 	_screen->_charWidth = 0;
385 	if (!_vm->gameFlags().use16ColorMode)
386 		_screen->_charOffset = -4;
387 
388 	if (!flags)
389 		_screen->copyRegionToBuffer(0, 0, 0, 320, 40, _textAreaBuffer);
390 
391 	char backupChar = 0;
392 	char *str = text;
393 	int y = 0;
394 
395 	if (_vm->gameFlags().use16ColorMode) {
396 		if (color == 0xDA)
397 			color = 0xA1;
398 		else if (color == 0xF2)
399 			color = 0xE1;
400 		else if (flags < 0)
401 			color = 0xE1;
402 		else
403 			color = 0xC1;
404 	}
405 
406 	while (str[0]) {
407 		char *nextLine = strchr(str, '\r');
408 
409 		backupChar = 0;
410 		if (nextLine) {
411 			backupChar = nextLine[0];
412 			nextLine[0] = '\0';
413 		}
414 
415 		int width = _screen->getTextWidth(str);
416 
417 		if (flags >= 0)
418 			_screen->printText(str, (320 - width) >> 1, y, color, 0x00);
419 		else
420 			_screen->printText(str, 0, y, color, 0x00);
421 
422 		y += (_vm->gameFlags().use16ColorMode ? 16 : (_screen->getFontHeight() - 4));
423 		str += strlen(str);
424 
425 		if (backupChar) {
426 			nextLine[0] = backupChar;
427 			++str;
428 		}
429 	}
430 }
431 
setupTextPalette(uint index,int fadePalette)432 void TIMInterpreter::setupTextPalette(uint index, int fadePalette) {
433 	static const uint16 palTable[] = {
434 		0x00, 0x00, 0x00,
435 		0x64, 0x64, 0x64,
436 		0x61, 0x51, 0x30,
437 		0x29, 0x48, 0x64,
438 		0x00, 0x4B, 0x3B,
439 		0x64, 0x1E, 0x1E,
440 	};
441 
442 	for (int i = 0; i < 15; ++i) {
443 		uint8 *palette = _screen->getPalette(0).getData() + (240 + i) * 3;
444 
445 		uint8 c1 = (((15 - i) << 2) * palTable[index*3+0]) / 100;
446 		uint8 c2 = (((15 - i) << 2) * palTable[index*3+1]) / 100;
447 		uint8 c3 = (((15 - i) << 2) * palTable[index*3+2]) / 100;
448 
449 		palette[0] = c1;
450 		palette[1] = c2;
451 		palette[2] = c3;
452 	}
453 
454 	if (!fadePalette && !_palDiff) {
455 		_screen->setScreenPalette(_screen->getPalette(0));
456 	} else {
457 		_screen->getFadeParams(_screen->getPalette(0), fadePalette, _palDelayInc, _palDiff);
458 		_palDelayAcc = 0;
459 	}
460 }
461 
initAnimStruct(int index,const char * filename,int x,int y,int,int offscreenBuffer,uint16 wsaFlags)462 int TIMInterpreter::initAnimStruct(int index, const char *filename, int x, int y, int, int offscreenBuffer, uint16 wsaFlags) {
463 	Movie *wsa = 0;
464 
465 	const bool isLoLDemo = _vm->gameFlags().isDemo && _vm->game() == GI_LOL;
466 
467 	if (isLoLDemo || _vm->gameFlags().platform == Common::kPlatformPC98 || _currentTim->isLoLOutro)
468 		_drawPage2 = 0;
469 	else
470 		_drawPage2 = 8;
471 
472 	uint16 wsaOpenFlags = 0;
473 	if (isLoLDemo) {
474 		if (!(wsaFlags & 0x10))
475 			wsaOpenFlags |= 1;
476 	} else {
477 		if (wsaFlags & 0x10)
478 			wsaOpenFlags |= 2;
479 		wsaOpenFlags |= 1;
480 
481 		if (offscreenBuffer == 2)
482 			wsaOpenFlags = 1;
483 	}
484 
485 	Common::String file = Common::String::format("%s.WSA", filename);
486 
487 	if (_vm->resource()->exists(file.c_str())) {
488 		if (isLoLDemo)
489 			wsa = new WSAMovie_v1(_vm);
490 		else
491 			wsa = new WSAMovie_v2(_vm);
492 		assert(wsa);
493 
494 		wsa->open(file.c_str(), wsaOpenFlags, (index == 1) ? &_screen->getPalette(0) : 0);
495 	}
496 
497 	if (wsa && wsa->opened()) {
498 		if (isLoLDemo) {
499 			if (x == -1) {
500 				int16 t = int8(320 - wsa->width());
501 				uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8);
502 				v >>= 1;
503 				x = v;
504 			}
505 
506 			if (y == -1) {
507 				int16 t = int8(200 - wsa->height());
508 				uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8);
509 				v >>= 1;
510 				y = v;
511 			}
512 		} else {
513 			if (x == -1)
514 				x = 0;
515 			if (y == -1)
516 				y = 0;
517 		}
518 
519 		if (wsaFlags & 2) {
520 			_screen->fadePalette(_screen->getPalette(1), 15, 0);
521 			_screen->clearPage(_drawPage2);
522 			if (_drawPage2)
523 				_screen->checkedPageUpdate(8, 4);
524 			_screen->updateScreen();
525 		}
526 
527 		if (wsaFlags & 4) {
528 			file = Common::String::format("%s.CPS", filename);
529 
530 			if (_vm->resource()->exists(file.c_str())) {
531 				_screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0));
532 				_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK);
533 				if (_drawPage2)
534 					_screen->checkedPageUpdate(8, 4);
535 				_screen->updateScreen();
536 			}
537 
538 			wsa->displayFrame(0, 0, x, y, 0, 0, 0);
539 		}
540 
541 		if (wsaFlags & 2)
542 			_screen->fadePalette(_screen->getPalette(0), 30, 0);
543 	} else {
544 		if (wsaFlags & 2) {
545 			_screen->fadePalette(_screen->getPalette(1), 15, 0);
546 			_screen->clearPage(_drawPage2);
547 			if (_drawPage2)
548 				_screen->checkedPageUpdate(8, 4);
549 			_screen->updateScreen();
550 		}
551 
552 		file = Common::String::format("%s.CPS", filename);
553 
554 		if (_vm->resource()->exists(file.c_str())) {
555 			_screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0));
556 			_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK);
557 			if (_drawPage2)
558 				_screen->checkedPageUpdate(8, 4);
559 			_screen->updateScreen();
560 		}
561 
562 		if (wsaFlags & 2)
563 			_screen->fadePalette(_screen->getPalette(0), 30, 0);
564 	}
565 
566 	_animator->init(index, wsa, x, y, wsaFlags, 0);
567 
568 	return index + 1;
569 }
570 
freeAnimStruct(int index)571 int TIMInterpreter::freeAnimStruct(int index) {
572 	_animator->reset(index, true);
573 	return 1;
574 }
575 
getTableEntry(uint idx)576 char *TIMInterpreter::getTableEntry(uint idx) {
577 	if (!_langData)
578 		return 0;
579 	else
580 		return (char *)(_langData + READ_LE_UINT16(_langData + (idx<<1)));
581 }
582 
getCTableEntry(uint idx) const583 const char *TIMInterpreter::getCTableEntry(uint idx) const {
584 	if (!_langData)
585 		return 0;
586 	else
587 		return (const char *)(_langData + READ_LE_UINT16(_langData + (idx<<1)));
588 }
589 
execCommand(int cmd,const uint16 * param)590 int TIMInterpreter::execCommand(int cmd, const uint16 *param) {
591 	if (cmd < 0 || cmd >= _commandsSize) {
592 		warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
593 		return 0;
594 	}
595 
596 	if (_commands[cmd].proc == 0) {
597 		warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
598 		return 0;
599 	}
600 
601 	debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param);
602 	return (this->*_commands[cmd].proc)(param);
603 }
604 
cmd_initFunc0(const uint16 * param)605 int TIMInterpreter::cmd_initFunc0(const uint16 *param) {
606 	for (int i = 0; i < TIM::kWSASlots; ++i)
607 		memset(&_currentTim->wsa[i], 0, sizeof(TIM::WSASlot));
608 
609 	_currentTim->func[0].ip = _currentTim->func[0].avtl;
610 	_currentTim->func[0].lastTime = _system->getMillis();
611 	return 1;
612 }
613 
cmd_stopCurFunc(const uint16 * param)614 int TIMInterpreter::cmd_stopCurFunc(const uint16 *param) {
615 	if (_currentFunc < TIM::kCountFuncs)
616 		_currentTim->func[_currentFunc].ip = 0;
617 	if (!_currentFunc)
618 		_finished = true;
619 	return -2;
620 }
621 
stopAllFuncs(TIM * tim)622 void TIMInterpreter::stopAllFuncs(TIM *tim) {
623 	for (int i = 0; i < TIM::kCountFuncs; ++i)
624 		tim->func[i].ip = 0;
625 }
626 
cmd_initWSA(const uint16 * param)627 int TIMInterpreter::cmd_initWSA(const uint16 *param) {
628 	const int index = param[0];
629 
630 	TIM::WSASlot &slot = _currentTim->wsa[index];
631 
632 	slot.x = int16(param[2]);
633 	slot.y = int16(param[3]);
634 	slot.offscreen = param[4];
635 	slot.wsaFlags = param[5];
636 	const char *filename = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[1]<<1)));
637 
638 	slot.anim = initAnimStruct(index, filename, slot.x, slot.y, 10, slot.offscreen, slot.wsaFlags);
639 	return 1;
640 }
641 
cmd_uninitWSA(const uint16 * param)642 int TIMInterpreter::cmd_uninitWSA(const uint16 *param) {
643 	const int index = param[0];
644 
645 	TIM::WSASlot &slot = _currentTim->wsa[index];
646 
647 	if (!slot.anim)
648 		return 0;
649 
650 	if (slot.offscreen) {
651 		_animator->reset(index, false);
652 		slot.anim = 0;
653 	} else {
654 		//XXX
655 		_animator->reset(index, true);
656 		memset(&slot, 0, sizeof(TIM::WSASlot));
657 	}
658 
659 	return 1;
660 }
661 
cmd_initFunc(const uint16 * param)662 int TIMInterpreter::cmd_initFunc(const uint16 *param) {
663 	uint16 func = *param;
664 	assert(func < TIM::kCountFuncs);
665 	if (_currentTim->func[func].avtl)
666 		_currentTim->func[func].ip = _currentTim->func[func].avtl;
667 	else
668 		_currentTim->func[func].avtl = _currentTim->func[func].ip = _currentTim->avtl + _currentTim->avtl[func];
669 	return 1;
670 }
671 
cmd_stopFunc(const uint16 * param)672 int TIMInterpreter::cmd_stopFunc(const uint16 *param) {
673 	uint16 func = *param;
674 	assert(func < TIM::kCountFuncs);
675 	_currentTim->func[func].ip = 0;
676 	return 1;
677 }
678 
cmd_wsaDisplayFrame(const uint16 * param)679 int TIMInterpreter::cmd_wsaDisplayFrame(const uint16 *param) {
680 	_animator->displayFrame(param[0], _drawPage2, param[1]);
681 	return 1;
682 }
683 
cmd_displayText(const uint16 * param)684 int TIMInterpreter::cmd_displayText(const uint16 *param) {
685 	if (_currentTim->isLoLOutro)
686 		displayText(param[0], param[1], 0xF2);
687 	else
688 		displayText(param[0], param[1]);
689 	return 1;
690 }
691 
cmd_loadVocFile(const uint16 * param)692 int TIMInterpreter::cmd_loadVocFile(const uint16 *param) {
693 	const int stringId = param[0];
694 	const int index = param[1];
695 
696 	_vocFiles[index] = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (stringId << 1)));
697 
698 	if (index == 2 && _currentTim->isLoLOutro && _vm->gameFlags().isTalkie) {
699 		_vocFiles[index] = "CONGRATA.VOC";
700 
701 		switch (_currentTim->lolCharacter) {
702 		case 0:
703 			_vocFiles[index].setChar('K', 7);
704 			break;
705 
706 		case 1:
707 			_vocFiles[index].setChar('A', 7);
708 			break;
709 
710 		case 2:
711 			_vocFiles[index].setChar('M', 7);
712 			break;
713 
714 		case 3:
715 			_vocFiles[index].setChar('C', 7);
716 			break;
717 
718 		default:
719 			break;
720 		}
721 	}
722 
723 	for (int i = 0; i < 4; ++i)
724 		_vocFiles[index].deleteLastChar();
725 	return 1;
726 }
727 
cmd_unloadVocFile(const uint16 * param)728 int TIMInterpreter::cmd_unloadVocFile(const uint16 *param) {
729 	const int index = param[0];
730 	_vocFiles[index].clear();
731 	return 1;
732 }
733 
cmd_playVocFile(const uint16 * param)734 int TIMInterpreter::cmd_playVocFile(const uint16 *param) {
735 	const int index = param[0];
736 	const int volume = (param[1] * 255) / 100;
737 
738 	if (index < ARRAYSIZE(_vocFiles) && !_vocFiles[index].empty())
739 		_vm->sound()->voicePlay(_vocFiles[index].c_str(), 0, volume, 255, true);
740 	else if (index == 7 && !_vm->gameFlags().isTalkie)
741 		_vm->sound()->playTrack(index);
742 	else
743 		_vm->sound()->playSoundEffect(index);
744 
745 	return 1;
746 }
747 
cmd_loadSoundFile(const uint16 * param)748 int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) {
749 	const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1)));
750 
751 	_vm->sound()->loadSoundFile(file);
752 	if (_vm->game() == GI_LOL)
753 		_vm->sound()->loadSfxFile(file);
754 
755 	return 1;
756 }
757 
cmd_playMusicTrack(const uint16 * param)758 int TIMInterpreter::cmd_playMusicTrack(const uint16 *param) {
759 	_vm->sound()->playTrack(param[0]);
760 	return 1;
761 }
762 
cmd_setLoopIp(const uint16 * param)763 int TIMInterpreter::cmd_setLoopIp(const uint16 *param) {
764 	_currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
765 	return 1;
766 }
767 
cmd_continueLoop(const uint16 * param)768 int TIMInterpreter::cmd_continueLoop(const uint16 *param) {
769 	TIM::Function &func = _currentTim->func[_currentFunc];
770 
771 	if (!func.loopIp)
772 		return -2;
773 
774 	func.ip = func.loopIp;
775 
776 	uint16 factor = param[0];
777 	if (factor) {
778 		const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000);
779 		uint32 waitTime = (random * factor) / 0x8000;
780 		func.nextTime += waitTime * _vm->tickLength();
781 	}
782 
783 	return -2;
784 }
785 
cmd_resetLoopIp(const uint16 * param)786 int TIMInterpreter::cmd_resetLoopIp(const uint16 *param) {
787 	_currentTim->func[_currentFunc].loopIp = 0;
788 	return 1;
789 }
790 
cmd_resetAllRuntimes(const uint16 * param)791 int TIMInterpreter::cmd_resetAllRuntimes(const uint16 *param) {
792 	for (int i = 0; i < TIM::kCountFuncs; ++i) {
793 		if (_currentTim->func[i].ip)
794 			_currentTim->func[i].nextTime = _system->getMillis();
795 	}
796 	return 1;
797 }
798 
cmd_execOpcode(const uint16 * param)799 int TIMInterpreter::cmd_execOpcode(const uint16 *param) {
800 	const uint16 opcode = *param++;
801 
802 	if (!_currentTim->opcodes) {
803 		warning("Trying to execute TIM opcode %d without opcode list (file '%s')", opcode, _currentTim->filename);
804 		return 0;
805 	}
806 
807 	if (opcode > _currentTim->opcodes->size()) {
808 		warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename);
809 		return 0;
810 	}
811 
812 	if (!(*_currentTim->opcodes)[opcode]->isValid()) {
813 		warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename);
814 		return 0;
815 	}
816 
817 	return (*(*_currentTim->opcodes)[opcode])(_currentTim, param);
818 }
819 
cmd_initFuncNow(const uint16 * param)820 int TIMInterpreter::cmd_initFuncNow(const uint16 *param) {
821 	uint16 func = *param;
822 	assert(func < TIM::kCountFuncs);
823 	_currentTim->func[func].ip = _currentTim->func[func].avtl;
824 	_currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis();
825 	return 1;
826 }
827 
cmd_stopFuncNow(const uint16 * param)828 int TIMInterpreter::cmd_stopFuncNow(const uint16 *param) {
829 	uint16 func = *param;
830 	assert(func < TIM::kCountFuncs);
831 	_currentTim->func[func].ip = 0;
832 	_currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis();
833 	return 1;
834 }
835 
836 // TODO: Consider moving to another file
837 
838 #ifdef ENABLE_LOL
839 // LOL version of the TIM interpreter
840 
TIMInterpreter_LoL(LoLEngine * engine,Screen_v2 * screen_v2,OSystem * system)841 TIMInterpreter_LoL::TIMInterpreter_LoL(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system) :
842 	TIMInterpreter(engine, screen_v2, system), _vm(engine)  {
843 	#define COMMAND(x) { &TIMInterpreter_LoL::x, #x }
844 	#define COMMAND_UNIMPL() { 0, 0 }
845 	#define cmd_return(n) cmd_return_##n
846 	static const CommandEntry commandProcs[] = {
847 		// 0x00
848 		COMMAND(cmd_initFunc0),
849 		COMMAND(cmd_stopAllFuncs),
850 		COMMAND(cmd_initWSA),
851 		COMMAND(cmd_uninitWSA),
852 		// 0x04
853 		COMMAND(cmd_initFunc),
854 		COMMAND(cmd_stopFunc),
855 		COMMAND(cmd_wsaDisplayFrame),
856 		COMMAND_UNIMPL(),
857 		// 0x08
858 		COMMAND(cmd_loadVocFile),
859 		COMMAND(cmd_unloadVocFile),
860 		COMMAND(cmd_playVocFile),
861 		COMMAND_UNIMPL(),
862 		// 0x0C
863 		COMMAND(cmd_loadSoundFile),
864 		COMMAND(cmd_return(1)),
865 		COMMAND(cmd_playMusicTrack),
866 		COMMAND_UNIMPL(),
867 		// 0x10
868 		COMMAND(cmd_return(1)),
869 		COMMAND(cmd_return(1)),
870 		COMMAND_UNIMPL(),
871 		COMMAND_UNIMPL(),
872 		// 0x14
873 		COMMAND(cmd_setLoopIp),
874 		COMMAND(cmd_continueLoop),
875 		COMMAND(cmd_resetLoopIp),
876 		COMMAND(cmd_resetAllRuntimes),
877 		// 0x18
878 		COMMAND(cmd_return(1)),
879 		COMMAND(cmd_execOpcode),
880 		COMMAND(cmd_initFuncNow),
881 		COMMAND(cmd_stopFuncNow),
882 		// 0x1C
883 		COMMAND(cmd_processDialogue),
884 		COMMAND(cmd_dialogueBox),
885 		COMMAND(cmd_return(n1))
886 	};
887 	#undef cmd_return
888 	#undef COMMAND_UNIMPL
889 	#undef COMMAND
890 
891 	_commands = commandProcs;
892 	_commandsSize = ARRAYSIZE(commandProcs);
893 
894 	_screen = engine->_screen;
895 
896 	delete _animator;
897 	_animator = new TimAnimator(engine, screen_v2, system, true);
898 
899 	_drawPage2 = 0;
900 }
901 
initAnimStruct(int index,const char * filename,int x,int y,int frameDelay,int,uint16 wsaFlags)902 int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaFlags) {
903 	Movie *wsa = 0;
904 	uint16 wsaOpenFlags = 0;
905 	if (wsaFlags & 0x10)
906 		wsaOpenFlags |= 2;
907 	if (wsaFlags & 8)
908 		wsaOpenFlags |= 1;
909 
910 	Common::String file = Common::String::format("%s.WSA", filename);
911 
912 	if (_vm->resource()->exists(file.c_str())) {
913 		wsa = new WSAMovie_v2(_vm);
914 		assert(wsa);
915 		wsa->open(file.c_str(), wsaOpenFlags, &_screen->getPalette(3));
916 	}
917 
918 	if (!_vm->_flags.use16ColorMode) {
919 		if (wsaFlags & 1) {
920 			if (_screen->_fadeFlag != 1)
921 				_screen->fadeClearSceneWindow(10);
922 			_screen->getPalette(3).copy(_screen->getPalette(0), 128, 128);
923 		} else if (wsaFlags & 2) {
924 			_screen->fadeToBlack(10);
925 		}
926 	}
927 
928 	if (wsa && (wsaFlags & 7))
929 		wsa->displayFrame(0, 0, x, y, 0, 0, 0);
930 
931 	if (wsaFlags & 3) {
932 		if (_vm->_flags.use16ColorMode) {
933 			_vm->setPaletteBrightness(_screen->getPalette(0), _vm->_brightness, _vm->_lampEffect);
934 		} else {
935 			_screen->loadSpecialColors(_screen->getPalette(3));
936 			_screen->fadePalette(_screen->getPalette(3), 10);
937 		}
938 		_screen->_fadeFlag = 0;
939 	}
940 
941 	_animator->init(index, wsa, x, y, wsaFlags, frameDelay);
942 
943 	return index + 1;
944 }
945 
freeAnimStruct(int index)946 int TIMInterpreter_LoL::freeAnimStruct(int index) {
947 	_animator->reset(index, true);
948 	return 1;
949 }
950 
advanceToOpcode(int opcode)951 void TIMInterpreter_LoL::advanceToOpcode(int opcode) {
952 	TIM::Function *f = &_currentTim->func[_currentTim->dlgFunc];
953 	uint16 len = f->ip[0];
954 
955 	while ((f->ip[2] & 0xFF) != opcode) {
956 		if ((f->ip[2] & 0xFF) == 1) {
957 			f->ip[0] = len;
958 			break;
959 		}
960 		len = f->ip[0];
961 		f->ip += len;
962 	}
963 
964 	f->nextTime = _system->getMillis();
965 }
966 
resetDialogueState(TIM * tim)967 void TIMInterpreter_LoL::resetDialogueState(TIM *tim) {
968 	if (!tim)
969 		return;
970 
971 	tim->procFunc = 0;
972 	tim->procParam = _vm->_dialogueNumButtons ? _vm->_dialogueNumButtons : 1;
973 	tim->clickedButton = 0;
974 	tim->dlgFunc = -1;
975 }
976 
update()977 void TIMInterpreter_LoL::update() {
978 	_vm->update();
979 }
980 
checkSpeechProgress()981 void TIMInterpreter_LoL::checkSpeechProgress() {
982 	if (_vm->speechEnabled() && _currentTim->procParam > 1 && _currentTim->func[_currentFunc].loopIp) {
983 		if (_vm->snd_updateCharacterSpeech() != 2) {
984 			_currentTim->func[_currentFunc].loopIp = 0;
985 			_currentTim->dlgFunc = _currentFunc;
986 			advanceToOpcode(21);
987 			_currentTim->dlgFunc = -1;
988 			_animator->reset(5, false);
989 		}
990 	}
991 }
992 
getTableString(int id)993 char *TIMInterpreter_LoL::getTableString(int id) {
994 	return _vm->getLangString(id);
995 }
996 
execCommand(int cmd,const uint16 * param)997 int TIMInterpreter_LoL::execCommand(int cmd, const uint16 *param) {
998 	if (cmd < 0 || cmd >= _commandsSize) {
999 		warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
1000 		return 0;
1001 	}
1002 
1003 	if (_commands[cmd].proc == 0) {
1004 		warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
1005 		return 0;
1006 	}
1007 
1008 	debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param);
1009 	return (this->*_commands[cmd].proc)(param);
1010 }
1011 
cmd_stopAllFuncs(const uint16 * param)1012 int TIMInterpreter_LoL::cmd_stopAllFuncs(const uint16 *param) {
1013 	while (_currentTim->dlgFunc == -1 && _currentTim->clickedButton == 0 && !_vm->shouldQuit()) {
1014 		update();
1015 		_currentTim->clickedButton = _vm->processDialogue();
1016 	}
1017 
1018 	for (int i = 0; i < TIM::kCountFuncs; ++i)
1019 		_currentTim->func[i].ip = 0;
1020 
1021 	return -1;
1022 }
1023 
cmd_setLoopIp(const uint16 * param)1024 int TIMInterpreter_LoL::cmd_setLoopIp(const uint16 *param) {
1025 	if (_vm->speechEnabled()) {
1026 		if (_vm->snd_updateCharacterSpeech() == 2)
1027 			_currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
1028 		else
1029 			advanceToOpcode(21);
1030 	} else {
1031 		_currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
1032 	}
1033 	return 1;
1034 }
1035 
cmd_continueLoop(const uint16 * param)1036 int TIMInterpreter_LoL::cmd_continueLoop(const uint16 *param) {
1037 	TIM::Function &func = _currentTim->func[_currentFunc];
1038 
1039 	if (!func.loopIp)
1040 		return -2;
1041 
1042 	func.ip = func.loopIp;
1043 
1044 	if (_vm->snd_updateCharacterSpeech() != 2) {
1045 		uint16 factor = param[0];
1046 		if (factor) {
1047 			const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000);
1048 			uint32 waitTime = (random * factor) / 0x8000;
1049 			func.nextTime += waitTime * _vm->tickLength();
1050 		}
1051 	}
1052 
1053 	return -2;
1054 }
1055 
cmd_processDialogue(const uint16 * param)1056 int TIMInterpreter_LoL::cmd_processDialogue(const uint16 *param) {
1057 	int res = _vm->processDialogue();
1058 	if (!res || !_currentTim->procParam)
1059 		return res;
1060 
1061 	_vm->snd_stopSpeech(false);
1062 
1063 	_currentTim->func[_currentTim->procFunc].loopIp = 0;
1064 	_currentTim->dlgFunc = _currentTim->procFunc;
1065 	_currentTim->procFunc = -1;
1066 	_currentTim->clickedButton = res;
1067 
1068 	_animator->reset(5, false);
1069 
1070 	if (_currentTim->procParam)
1071 		advanceToOpcode(21);
1072 
1073 	return res;
1074 }
1075 
cmd_dialogueBox(const uint16 * param)1076 int TIMInterpreter_LoL::cmd_dialogueBox(const uint16 *param) {
1077 	uint16 func = *param;
1078 	assert(func < TIM::kCountFuncs);
1079 	_currentTim->procParam = func;
1080 	_currentTim->clickedButton = 0;
1081 
1082 	const char *tmpStr[3];
1083 	int cnt = 0;
1084 
1085 	for (int i = 1; i < 4; i++) {
1086 		if (param[i] != 0xFFFF) {
1087 			tmpStr[i-1] = getTableString(param[i]);
1088 			cnt++;
1089 		} else {
1090 			tmpStr[i-1] = 0;
1091 		}
1092 	}
1093 
1094 	_vm->setupDialogueButtons(cnt, tmpStr[0], tmpStr[1], tmpStr[2]);
1095 	_vm->gui_notifyButtonListChanged();
1096 
1097 	return -3;
1098 }
1099 #endif // ENABLE_LOL
1100 
1101 } // End of namespace Kyra
1102