aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/script/script_tim.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/script/script_tim.cpp')
-rw-r--r--engines/kyra/script/script_tim.cpp1101
1 files changed, 1101 insertions, 0 deletions
diff --git a/engines/kyra/script/script_tim.cpp b/engines/kyra/script/script_tim.cpp
new file mode 100644
index 0000000000..7afa35c339
--- /dev/null
+++ b/engines/kyra/script/script_tim.cpp
@@ -0,0 +1,1101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/script/script_tim.h"
+#include "kyra/resource/resource.h"
+#include "kyra/sound/sound.h"
+
+#ifdef ENABLE_LOL
+#include "kyra/engine/lol.h"
+#include "kyra/graphics/screen_lol.h"
+#endif // ENABLE_LOL
+
+#include "common/iff_container.h"
+#include "common/system.h"
+
+namespace Kyra {
+
+TIMInterpreter::TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system) : _vm(engine), _screen(screen_v2), _system(system), _currentTim(0) {
+#define COMMAND(x) { &TIMInterpreter::x, #x }
+#define COMMAND_UNIMPL() { 0, 0 }
+#define cmd_return(n) cmd_return_##n
+ static const CommandEntry commandProcs[] = {
+ // 0x00
+ COMMAND(cmd_initFunc0),
+ COMMAND(cmd_stopCurFunc),
+ COMMAND(cmd_initWSA),
+ COMMAND(cmd_uninitWSA),
+ // 0x04
+ COMMAND(cmd_initFunc),
+ COMMAND(cmd_stopFunc),
+ COMMAND(cmd_wsaDisplayFrame),
+ COMMAND(cmd_displayText),
+ // 0x08
+ COMMAND(cmd_loadVocFile),
+ COMMAND(cmd_unloadVocFile),
+ COMMAND(cmd_playVocFile),
+ COMMAND_UNIMPL(),
+ // 0x0C
+ COMMAND(cmd_loadSoundFile),
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_playMusicTrack),
+ COMMAND_UNIMPL(),
+ // 0x10
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_return(1)),
+ COMMAND_UNIMPL(),
+ COMMAND_UNIMPL(),
+ // 0x14
+ COMMAND(cmd_setLoopIp),
+ COMMAND(cmd_continueLoop),
+ COMMAND(cmd_resetLoopIp),
+ COMMAND(cmd_resetAllRuntimes),
+ // 0x18
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_execOpcode),
+ COMMAND(cmd_initFuncNow),
+ COMMAND(cmd_stopFuncNow),
+ // 0x1C
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_return(n1))
+ };
+#undef cmd_return
+#undef COMMAND_UNIMPL
+#undef COMMAND
+
+ _commands = commandProcs;
+ _commandsSize = ARRAYSIZE(commandProcs);
+
+ _langData = 0;
+ _textDisplayed = false;
+ _textAreaBuffer = new uint8[320*40];
+ assert(_textAreaBuffer);
+ if ((_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().isDemo) && _vm->game() == GI_LOL)
+ _drawPage2 = 0;
+ else
+ _drawPage2 = 8;
+
+ _animator = new TimAnimator(0, screen_v2, 0, false);
+
+ _palDelayInc = _palDiff = _palDelayAcc = 0;
+ _abortFlag = 0;
+ _tim = 0;
+}
+
+TIMInterpreter::~TIMInterpreter() {
+ delete[] _langData;
+ delete[] _textAreaBuffer;
+ delete _animator;
+}
+
+bool TIMInterpreter::callback(Common::IFFChunk &chunk) {
+ switch (chunk._type) {
+ case MKTAG('T','E','X','T'):
+ _tim->text = new byte[chunk._size];
+ assert(_tim->text);
+ if (chunk._stream->read(_tim->text, chunk._size) != chunk._size)
+ error("Couldn't read TEXT chunk from file '%s'", _filename);
+ break;
+
+ case MKTAG('A','V','T','L'):
+ _avtlChunkSize = chunk._size >> 1;
+ _tim->avtl = new uint16[_avtlChunkSize];
+ assert(_tim->avtl);
+ if (chunk._stream->read(_tim->avtl, chunk._size) != chunk._size)
+ error("Couldn't read AVTL chunk from file '%s'", _filename);
+
+ for (int i = _avtlChunkSize - 1; i >= 0; --i)
+ _tim->avtl[i] = READ_LE_UINT16(&_tim->avtl[i]);
+ break;
+
+ default:
+ warning("Unexpected chunk '%s' of size %d found in file '%s'", tag2str(chunk._type), chunk._size, _filename);
+ }
+
+ return false;
+}
+
+TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes) {
+ if (!_vm->resource()->exists(filename))
+ return 0;
+
+ Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
+ if (!stream)
+ error("Couldn't open TIM file '%s'", filename);
+
+ _avtlChunkSize = 0;
+ _filename = filename;
+
+ _tim = new TIM;
+ assert(_tim);
+ memset(_tim, 0, sizeof(TIM));
+
+ _tim->procFunc = -1;
+ _tim->opcodes = opcodes;
+
+ IFFParser iff(*stream);
+ Common::Functor1Mem<Common::IFFChunk &, bool, TIMInterpreter> c(this, &TIMInterpreter::callback);
+ iff.parse(c);
+
+ if (!_tim->avtl)
+ error("No AVTL chunk found in file: '%s'", filename);
+
+ if (stream->err())
+ error("Read error while parsing file '%s'", filename);
+
+ delete stream;
+
+ const int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs;
+ for (int i = 0; i < num; ++i)
+ _tim->func[i].avtl = _tim->avtl + _tim->avtl[i];
+
+ Common::strlcpy(_tim->filename, filename, 13);
+
+ _tim->isLoLOutro = (_vm->game() == GI_LOL) && !scumm_stricmp(filename, "LOLFINAL.TIM");
+ _tim->lolCharacter = 0;
+
+ TIM *r = _tim;
+ _tim = 0;
+ return r;
+}
+
+void TIMInterpreter::unload(TIM *&tim) const {
+ if (!tim)
+ return;
+
+ delete[] tim->text;
+ delete[] tim->avtl;
+ delete tim;
+ tim = 0;
+}
+
+void TIMInterpreter::setLangData(const char *filename) {
+ delete[] _langData;
+ _langData = _vm->resource()->fileData(filename, 0);
+}
+
+int TIMInterpreter::exec(TIM *tim, bool loop) {
+ if (!tim)
+ return 0;
+
+ _currentTim = tim;
+ if (!_currentTim->func[0].ip) {
+ _currentTim->func[0].ip = _currentTim->func[0].avtl;
+ _currentTim->func[0].nextTime = _currentTim->func[0].lastTime = _system->getMillis();
+ }
+
+ do {
+ update();
+
+ for (_currentFunc = 0; _currentFunc < TIM::kCountFuncs; ++_currentFunc) {
+ TIM::Function &cur = _currentTim->func[_currentFunc];
+
+ if (_currentTim->procFunc != -1)
+ execCommand(28, &_currentTim->procParam);
+
+ update();
+ checkSpeechProgress();
+
+ bool running = true;
+ int cnt = 0;
+ while (cur.ip && cur.nextTime <= _system->getMillis() && running) {
+ if (cnt++ > 0) {
+ if (_currentTim->procFunc != -1)
+ execCommand(28, &_currentTim->procParam);
+ update();
+ }
+
+ int8 opcode = int8(cur.ip[2] & 0xFF);
+
+ switch (execCommand(opcode, cur.ip + 3)) {
+ case -1:
+ loop = false;
+ running = false;
+ _currentFunc = 11;
+ break;
+
+ case -2:
+ running = false;
+ break;
+
+ case -3:
+ _currentTim->procFunc = _currentFunc;
+ _currentTim->dlgFunc = -1;
+ break;
+
+ case 22:
+ cur.loopIp = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ if (cur.ip) {
+ cur.ip += cur.ip[0];
+ cur.lastTime = cur.nextTime;
+ cur.nextTime += cur.ip[1] * _vm->tickLength();
+ }
+ }
+ }
+ } while (loop && !_vm->shouldQuit());
+
+ return _currentTim->clickedButton;
+}
+
+void TIMInterpreter::refreshTimersAfterPause(uint32 elapsedTime) {
+ if (!_currentTim)
+ return;
+
+ for (int i = 0; i < TIM::kCountFuncs; i++) {
+ if (_currentTim->func[i].lastTime)
+ _currentTim->func[i].lastTime += elapsedTime;
+ if (_currentTim->func[i].nextTime)
+ _currentTim->func[i].nextTime += elapsedTime;
+ }
+}
+
+void TIMInterpreter::displayText(uint16 textId, int16 flags) {
+ char *text = getTableEntry(textId);
+
+ if (_textDisplayed) {
+ _screen->copyBlockToPage(0, 0, 160, 320, 40, _textAreaBuffer);
+ _textDisplayed = false;
+ }
+
+ if (!text)
+ return;
+ if (!text[0])
+ return;
+
+ char filename[16];
+ memset(filename, 0, sizeof(filename));
+
+ if (text[0] == '$') {
+ const char *end = strchr(text+1, '$');
+ if (end)
+ memcpy(filename, text+1, end-1-text);
+ }
+
+ const bool sjisMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode);
+ if (filename[0] && (_vm->speechEnabled() || !_vm->gameFlags().isTalkie))
+ _vm->sound()->voicePlay(filename, 0, 255, 255, !_vm->gameFlags().isTalkie);
+
+ if (text[0] == '$')
+ text = strchr(text + 1, '$') + 1;
+
+ if (!_vm->gameFlags().use16ColorMode)
+ setupTextPalette((flags < 0) ? 1 : flags, 0);
+
+ if (flags < 0) {
+ static const uint8 colorMap[] = { 0x00, 0xF0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+ _screen->setTextColorMap(colorMap);
+ _screen->_charWidth = -2;
+ }
+
+ _screen->_charOffset = -4;
+ _screen->copyRegionToBuffer(0, 0, 160, 320, 40, _textAreaBuffer);
+ _textDisplayed = true;
+
+ char backupChar = 0;
+ char *str = text;
+ int heightAdd = 0;
+
+ while (str[0] && _vm->textEnabled()) {
+ char *nextLine = strchr(str, '\r');
+
+ backupChar = 0;
+ if (nextLine) {
+ backupChar = nextLine[0];
+ nextLine[0] = '\0';
+ }
+
+ int width = _screen->getTextWidth(str);
+
+ if (flags >= 0) {
+ if (_vm->gameFlags().use16ColorMode) {
+ static const uint8 colorMap[] = { 0xE1, 0xE1, 0xC1, 0xA1, 0x81, 0x61 };
+ _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, colorMap[flags], 0x00);
+ } else {
+ _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, 0xF0, 0x00);
+ }
+ } else {
+ _screen->printText(str, (320 - width) >> 1, 188, 0xF0, 0x00);
+ }
+
+ heightAdd += _screen->getFontHeight();
+ str += strlen(str);
+
+ if (backupChar) {
+ nextLine[0] = backupChar;
+ ++str;
+ }
+ }
+
+ _screen->_charOffset = 0;
+
+ if (flags < 0) {
+ static const uint8 colorMap[] = { 0x00, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00 };
+
+ _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT);
+ _screen->setTextColorMap(colorMap);
+ _screen->_charWidth = 0;
+ }
+}
+
+void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) {
+ if (!_vm->textEnabled() && !(textId & 0x8000))
+ return;
+
+ char *text = getTableEntry(textId & 0x7FFF);
+
+ if (flags > 0)
+ _screen->copyBlockToPage(0, 0, 0, 320, 40, _textAreaBuffer);
+
+ if (flags == 255)
+ return;
+
+ _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT);
+
+ static const uint8 colorMap[] = { 0x00, 0xA0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ _screen->setTextColorMap(colorMap);
+ _screen->_charWidth = 0;
+ if (!_vm->gameFlags().use16ColorMode)
+ _screen->_charOffset = -4;
+
+ if (!flags)
+ _screen->copyRegionToBuffer(0, 0, 0, 320, 40, _textAreaBuffer);
+
+ char backupChar = 0;
+ char *str = text;
+ int y = 0;
+
+ if (_vm->gameFlags().use16ColorMode) {
+ if (color == 0xDA)
+ color = 0xA1;
+ else if (color == 0xF2)
+ color = 0xE1;
+ else if (flags < 0)
+ color = 0xE1;
+ else
+ color = 0xC1;
+ }
+
+ while (str[0]) {
+ char *nextLine = strchr(str, '\r');
+
+ backupChar = 0;
+ if (nextLine) {
+ backupChar = nextLine[0];
+ nextLine[0] = '\0';
+ }
+
+ int width = _screen->getTextWidth(str);
+
+ if (flags >= 0)
+ _screen->printText(str, (320 - width) >> 1, y, color, 0x00);
+ else
+ _screen->printText(str, 0, y, color, 0x00);
+
+ y += (_vm->gameFlags().use16ColorMode ? 16 : (_screen->getFontHeight() - 4));
+ str += strlen(str);
+
+ if (backupChar) {
+ nextLine[0] = backupChar;
+ ++str;
+ }
+ }
+}
+
+void TIMInterpreter::setupTextPalette(uint index, int fadePalette) {
+ static const uint16 palTable[] = {
+ 0x00, 0x00, 0x00,
+ 0x64, 0x64, 0x64,
+ 0x61, 0x51, 0x30,
+ 0x29, 0x48, 0x64,
+ 0x00, 0x4B, 0x3B,
+ 0x64, 0x1E, 0x1E,
+ };
+
+ for (int i = 0; i < 15; ++i) {
+ uint8 *palette = _screen->getPalette(0).getData() + (240 + i) * 3;
+
+ uint8 c1 = (((15 - i) << 2) * palTable[index*3+0]) / 100;
+ uint8 c2 = (((15 - i) << 2) * palTable[index*3+1]) / 100;
+ uint8 c3 = (((15 - i) << 2) * palTable[index*3+2]) / 100;
+
+ palette[0] = c1;
+ palette[1] = c2;
+ palette[2] = c3;
+ }
+
+ if (!fadePalette && !_palDiff) {
+ _screen->setScreenPalette(_screen->getPalette(0));
+ } else {
+ _screen->getFadeParams(_screen->getPalette(0), fadePalette, _palDelayInc, _palDiff);
+ _palDelayAcc = 0;
+ }
+}
+
+int TIMInterpreter::initAnimStruct(int index, const char *filename, int x, int y, int, int offscreenBuffer, uint16 wsaFlags) {
+ Movie *wsa = 0;
+
+ const bool isLoLDemo = _vm->gameFlags().isDemo && _vm->game() == GI_LOL;
+
+ if (isLoLDemo || _vm->gameFlags().platform == Common::kPlatformPC98 || _currentTim->isLoLOutro)
+ _drawPage2 = 0;
+ else
+ _drawPage2 = 8;
+
+ uint16 wsaOpenFlags = 0;
+ if (isLoLDemo) {
+ if (!(wsaFlags & 0x10))
+ wsaOpenFlags |= 1;
+ } else {
+ if (wsaFlags & 0x10)
+ wsaOpenFlags |= 2;
+ wsaOpenFlags |= 1;
+
+ if (offscreenBuffer == 2)
+ wsaOpenFlags = 1;
+ }
+
+ Common::String file = Common::String::format("%s.WSA", filename);
+
+ if (_vm->resource()->exists(file.c_str())) {
+ if (isLoLDemo)
+ wsa = new WSAMovie_v1(_vm);
+ else
+ wsa = new WSAMovie_v2(_vm);
+ assert(wsa);
+
+ wsa->open(file.c_str(), wsaOpenFlags, (index == 1) ? &_screen->getPalette(0) : 0);
+ }
+
+ if (wsa && wsa->opened()) {
+ if (isLoLDemo) {
+ if (x == -1) {
+ int16 t = int8(320 - wsa->width());
+ uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8);
+ v >>= 1;
+ x = v;
+ }
+
+ if (y == -1) {
+ int16 t = int8(200 - wsa->height());
+ uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8);
+ v >>= 1;
+ y = v;
+ }
+ } else {
+ if (x == -1)
+ x = 0;
+ if (y == -1)
+ y = 0;
+ }
+
+ if (wsaFlags & 2) {
+ _screen->fadePalette(_screen->getPalette(1), 15, 0);
+ _screen->clearPage(_drawPage2);
+ if (_drawPage2)
+ _screen->checkedPageUpdate(8, 4);
+ _screen->updateScreen();
+ }
+
+ if (wsaFlags & 4) {
+ file = Common::String::format("%s.CPS", filename);
+
+ if (_vm->resource()->exists(file.c_str())) {
+ _screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0));
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK);
+ if (_drawPage2)
+ _screen->checkedPageUpdate(8, 4);
+ _screen->updateScreen();
+ }
+
+ wsa->displayFrame(0, 0, x, y, 0, 0, 0);
+ }
+
+ if (wsaFlags & 2)
+ _screen->fadePalette(_screen->getPalette(0), 30, 0);
+ } else {
+ if (wsaFlags & 2) {
+ _screen->fadePalette(_screen->getPalette(1), 15, 0);
+ _screen->clearPage(_drawPage2);
+ if (_drawPage2)
+ _screen->checkedPageUpdate(8, 4);
+ _screen->updateScreen();
+ }
+
+ file = Common::String::format("%s.CPS", filename);
+
+ if (_vm->resource()->exists(file.c_str())) {
+ _screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0));
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK);
+ if (_drawPage2)
+ _screen->checkedPageUpdate(8, 4);
+ _screen->updateScreen();
+ }
+
+ if (wsaFlags & 2)
+ _screen->fadePalette(_screen->getPalette(0), 30, 0);
+ }
+
+ _animator->init(index, wsa, x, y, wsaFlags, 0);
+
+ return index + 1;
+}
+
+int TIMInterpreter::freeAnimStruct(int index) {
+ _animator->reset(index, true);
+ return 1;
+}
+
+char *TIMInterpreter::getTableEntry(uint idx) {
+ if (!_langData)
+ return 0;
+ else
+ return (char *)(_langData + READ_LE_UINT16(_langData + (idx<<1)));
+}
+
+const char *TIMInterpreter::getCTableEntry(uint idx) const {
+ if (!_langData)
+ return 0;
+ else
+ return (const char *)(_langData + READ_LE_UINT16(_langData + (idx<<1)));
+}
+
+int TIMInterpreter::execCommand(int cmd, const uint16 *param) {
+ if (cmd < 0 || cmd >= _commandsSize) {
+ warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
+ return 0;
+ }
+
+ if (_commands[cmd].proc == 0) {
+ warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
+ return 0;
+ }
+
+ debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param);
+ return (this->*_commands[cmd].proc)(param);
+}
+
+int TIMInterpreter::cmd_initFunc0(const uint16 *param) {
+ for (int i = 0; i < TIM::kWSASlots; ++i)
+ memset(&_currentTim->wsa[i], 0, sizeof(TIM::WSASlot));
+
+ _currentTim->func[0].ip = _currentTim->func[0].avtl;
+ _currentTim->func[0].lastTime = _system->getMillis();
+ return 1;
+}
+
+int TIMInterpreter::cmd_stopCurFunc(const uint16 *param) {
+ if (_currentFunc < TIM::kCountFuncs)
+ _currentTim->func[_currentFunc].ip = 0;
+ if (!_currentFunc)
+ _finished = true;
+ return -2;
+}
+
+void TIMInterpreter::stopAllFuncs(TIM *tim) {
+ for (int i = 0; i < TIM::kCountFuncs; ++i)
+ tim->func[i].ip = 0;
+}
+
+int TIMInterpreter::cmd_initWSA(const uint16 *param) {
+ const int index = param[0];
+
+ TIM::WSASlot &slot = _currentTim->wsa[index];
+
+ slot.x = int16(param[2]);
+ slot.y = int16(param[3]);
+ slot.offscreen = param[4];
+ slot.wsaFlags = param[5];
+ const char *filename = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[1]<<1)));
+
+ slot.anim = initAnimStruct(index, filename, slot.x, slot.y, 10, slot.offscreen, slot.wsaFlags);
+ return 1;
+}
+
+int TIMInterpreter::cmd_uninitWSA(const uint16 *param) {
+ const int index = param[0];
+
+ TIM::WSASlot &slot = _currentTim->wsa[index];
+
+ if (!slot.anim)
+ return 0;
+
+ if (slot.offscreen) {
+ _animator->reset(index, false);
+ slot.anim = 0;
+ } else {
+ //XXX
+ _animator->reset(index, true);
+ memset(&slot, 0, sizeof(TIM::WSASlot));
+ }
+
+ return 1;
+}
+
+int TIMInterpreter::cmd_initFunc(const uint16 *param) {
+ uint16 func = *param;
+ assert(func < TIM::kCountFuncs);
+ if (_currentTim->func[func].avtl)
+ _currentTim->func[func].ip = _currentTim->func[func].avtl;
+ else
+ _currentTim->func[func].avtl = _currentTim->func[func].ip = _currentTim->avtl + _currentTim->avtl[func];
+ return 1;
+}
+
+int TIMInterpreter::cmd_stopFunc(const uint16 *param) {
+ uint16 func = *param;
+ assert(func < TIM::kCountFuncs);
+ _currentTim->func[func].ip = 0;
+ return 1;
+}
+
+int TIMInterpreter::cmd_wsaDisplayFrame(const uint16 *param) {
+ _animator->displayFrame(param[0], _drawPage2, param[1]);
+ return 1;
+}
+
+int TIMInterpreter::cmd_displayText(const uint16 *param) {
+ if (_currentTim->isLoLOutro)
+ displayText(param[0], param[1], 0xF2);
+ else
+ displayText(param[0], param[1]);
+ return 1;
+}
+
+int TIMInterpreter::cmd_loadVocFile(const uint16 *param) {
+ const int stringId = param[0];
+ const int index = param[1];
+
+ _vocFiles[index] = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (stringId << 1)));
+
+ if (index == 2 && _currentTim->isLoLOutro && _vm->gameFlags().isTalkie) {
+ _vocFiles[index] = "CONGRATA.VOC";
+
+ switch (_currentTim->lolCharacter) {
+ case 0:
+ _vocFiles[index].setChar('K', 7);
+ break;
+
+ case 1:
+ _vocFiles[index].setChar('A', 7);
+ break;
+
+ case 2:
+ _vocFiles[index].setChar('M', 7);
+ break;
+
+ case 3:
+ _vocFiles[index].setChar('C', 7);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ for (int i = 0; i < 4; ++i)
+ _vocFiles[index].deleteLastChar();
+ return 1;
+}
+
+int TIMInterpreter::cmd_unloadVocFile(const uint16 *param) {
+ const int index = param[0];
+ _vocFiles[index].clear();
+ return 1;
+}
+
+int TIMInterpreter::cmd_playVocFile(const uint16 *param) {
+ const int index = param[0];
+ const int volume = (param[1] * 255) / 100;
+
+ if (index < ARRAYSIZE(_vocFiles) && !_vocFiles[index].empty())
+ _vm->sound()->voicePlay(_vocFiles[index].c_str(), 0, volume, 255, true);
+ else if (index == 7 && !_vm->gameFlags().isTalkie)
+ _vm->sound()->playTrack(index);
+ else
+ _vm->sound()->playSoundEffect(index);
+
+ return 1;
+}
+
+int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) {
+ const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1)));
+
+ _vm->sound()->loadSoundFile(file);
+ if (_vm->game() == GI_LOL)
+ _vm->sound()->loadSfxFile(file);
+
+ return 1;
+}
+
+int TIMInterpreter::cmd_playMusicTrack(const uint16 *param) {
+ _vm->sound()->playTrack(param[0]);
+ return 1;
+}
+
+int TIMInterpreter::cmd_setLoopIp(const uint16 *param) {
+ _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
+ return 1;
+}
+
+int TIMInterpreter::cmd_continueLoop(const uint16 *param) {
+ TIM::Function &func = _currentTim->func[_currentFunc];
+
+ if (!func.loopIp)
+ return -2;
+
+ func.ip = func.loopIp;
+
+ uint16 factor = param[0];
+ if (factor) {
+ const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000);
+ uint32 waitTime = (random * factor) / 0x8000;
+ func.nextTime += waitTime * _vm->tickLength();
+ }
+
+ return -2;
+}
+
+int TIMInterpreter::cmd_resetLoopIp(const uint16 *param) {
+ _currentTim->func[_currentFunc].loopIp = 0;
+ return 1;
+}
+
+int TIMInterpreter::cmd_resetAllRuntimes(const uint16 *param) {
+ for (int i = 0; i < TIM::kCountFuncs; ++i) {
+ if (_currentTim->func[i].ip)
+ _currentTim->func[i].nextTime = _system->getMillis();
+ }
+ return 1;
+}
+
+int TIMInterpreter::cmd_execOpcode(const uint16 *param) {
+ const uint16 opcode = *param++;
+
+ if (!_currentTim->opcodes) {
+ warning("Trying to execute TIM opcode %d without opcode list (file '%s')", opcode, _currentTim->filename);
+ return 0;
+ }
+
+ if (opcode > _currentTim->opcodes->size()) {
+ warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename);
+ return 0;
+ }
+
+ if (!(*_currentTim->opcodes)[opcode]->isValid()) {
+ warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename);
+ return 0;
+ }
+
+ return (*(*_currentTim->opcodes)[opcode])(_currentTim, param);
+}
+
+int TIMInterpreter::cmd_initFuncNow(const uint16 *param) {
+ uint16 func = *param;
+ assert(func < TIM::kCountFuncs);
+ _currentTim->func[func].ip = _currentTim->func[func].avtl;
+ _currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis();
+ return 1;
+}
+
+int TIMInterpreter::cmd_stopFuncNow(const uint16 *param) {
+ uint16 func = *param;
+ assert(func < TIM::kCountFuncs);
+ _currentTim->func[func].ip = 0;
+ _currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis();
+ return 1;
+}
+
+// TODO: Consider moving to another file
+
+#ifdef ENABLE_LOL
+// LOL version of the TIM interpreter
+
+TIMInterpreter_LoL::TIMInterpreter_LoL(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system) :
+ TIMInterpreter(engine, screen_v2, system), _vm(engine) {
+ #define COMMAND(x) { &TIMInterpreter_LoL::x, #x }
+ #define COMMAND_UNIMPL() { 0, 0 }
+ #define cmd_return(n) cmd_return_##n
+ static const CommandEntry commandProcs[] = {
+ // 0x00
+ COMMAND(cmd_initFunc0),
+ COMMAND(cmd_stopAllFuncs),
+ COMMAND(cmd_initWSA),
+ COMMAND(cmd_uninitWSA),
+ // 0x04
+ COMMAND(cmd_initFunc),
+ COMMAND(cmd_stopFunc),
+ COMMAND(cmd_wsaDisplayFrame),
+ COMMAND_UNIMPL(),
+ // 0x08
+ COMMAND(cmd_loadVocFile),
+ COMMAND(cmd_unloadVocFile),
+ COMMAND(cmd_playVocFile),
+ COMMAND_UNIMPL(),
+ // 0x0C
+ COMMAND(cmd_loadSoundFile),
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_playMusicTrack),
+ COMMAND_UNIMPL(),
+ // 0x10
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_return(1)),
+ COMMAND_UNIMPL(),
+ COMMAND_UNIMPL(),
+ // 0x14
+ COMMAND(cmd_setLoopIp),
+ COMMAND(cmd_continueLoop),
+ COMMAND(cmd_resetLoopIp),
+ COMMAND(cmd_resetAllRuntimes),
+ // 0x18
+ COMMAND(cmd_return(1)),
+ COMMAND(cmd_execOpcode),
+ COMMAND(cmd_initFuncNow),
+ COMMAND(cmd_stopFuncNow),
+ // 0x1C
+ COMMAND(cmd_processDialogue),
+ COMMAND(cmd_dialogueBox),
+ COMMAND(cmd_return(n1))
+ };
+ #undef cmd_return
+ #undef COMMAND_UNIMPL
+ #undef COMMAND
+
+ _commands = commandProcs;
+ _commandsSize = ARRAYSIZE(commandProcs);
+
+ _screen = engine->_screen;
+
+ delete _animator;
+ _animator = new TimAnimator(engine, screen_v2, system, true);
+
+ _drawPage2 = 0;
+}
+
+int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaFlags) {
+ Movie *wsa = 0;
+ uint16 wsaOpenFlags = 0;
+ if (wsaFlags & 0x10)
+ wsaOpenFlags |= 2;
+ if (wsaFlags & 8)
+ wsaOpenFlags |= 1;
+
+ Common::String file = Common::String::format("%s.WSA", filename);
+
+ if (_vm->resource()->exists(file.c_str())) {
+ wsa = new WSAMovie_v2(_vm);
+ assert(wsa);
+ wsa->open(file.c_str(), wsaOpenFlags, &_screen->getPalette(3));
+ }
+
+ if (!_vm->_flags.use16ColorMode) {
+ if (wsaFlags & 1) {
+ if (_screen->_fadeFlag != 1)
+ _screen->fadeClearSceneWindow(10);
+ _screen->getPalette(3).copy(_screen->getPalette(0), 128, 128);
+ } else if (wsaFlags & 2) {
+ _screen->fadeToBlack(10);
+ }
+ }
+
+ if (wsa && (wsaFlags & 7))
+ wsa->displayFrame(0, 0, x, y, 0, 0, 0);
+
+ if (wsaFlags & 3) {
+ if (_vm->_flags.use16ColorMode) {
+ _vm->setPaletteBrightness(_screen->getPalette(0), _vm->_brightness, _vm->_lampEffect);
+ } else {
+ _screen->loadSpecialColors(_screen->getPalette(3));
+ _screen->fadePalette(_screen->getPalette(3), 10);
+ }
+ _screen->_fadeFlag = 0;
+ }
+
+ _animator->init(index, wsa, x, y, wsaFlags, frameDelay);
+
+ return index + 1;
+}
+
+int TIMInterpreter_LoL::freeAnimStruct(int index) {
+ _animator->reset(index, true);
+ return 1;
+}
+
+void TIMInterpreter_LoL::advanceToOpcode(int opcode) {
+ TIM::Function *f = &_currentTim->func[_currentTim->dlgFunc];
+ uint16 len = f->ip[0];
+
+ while ((f->ip[2] & 0xFF) != opcode) {
+ if ((f->ip[2] & 0xFF) == 1) {
+ f->ip[0] = len;
+ break;
+ }
+ len = f->ip[0];
+ f->ip += len;
+ }
+
+ f->nextTime = _system->getMillis();
+}
+
+void TIMInterpreter_LoL::resetDialogueState(TIM *tim) {
+ if (!tim)
+ return;
+
+ tim->procFunc = 0;
+ tim->procParam = _vm->_dialogueNumButtons ? _vm->_dialogueNumButtons : 1;
+ tim->clickedButton = 0;
+ tim->dlgFunc = -1;
+}
+
+void TIMInterpreter_LoL::update() {
+ _vm->update();
+}
+
+void TIMInterpreter_LoL::checkSpeechProgress() {
+ if (_vm->speechEnabled() && _currentTim->procParam > 1 && _currentTim->func[_currentFunc].loopIp) {
+ if (_vm->snd_updateCharacterSpeech() != 2) {
+ _currentTim->func[_currentFunc].loopIp = 0;
+ _currentTim->dlgFunc = _currentFunc;
+ advanceToOpcode(21);
+ _currentTim->dlgFunc = -1;
+ _animator->reset(5, false);
+ }
+ }
+}
+
+char *TIMInterpreter_LoL::getTableString(int id) {
+ return _vm->getLangString(id);
+}
+
+int TIMInterpreter_LoL::execCommand(int cmd, const uint16 *param) {
+ if (cmd < 0 || cmd >= _commandsSize) {
+ warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
+ return 0;
+ }
+
+ if (_commands[cmd].proc == 0) {
+ warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
+ return 0;
+ }
+
+ debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param);
+ return (this->*_commands[cmd].proc)(param);
+}
+
+int TIMInterpreter_LoL::cmd_stopAllFuncs(const uint16 *param) {
+ while (_currentTim->dlgFunc == -1 && _currentTim->clickedButton == 0 && !_vm->shouldQuit()) {
+ update();
+ _currentTim->clickedButton = _vm->processDialogue();
+ }
+
+ for (int i = 0; i < TIM::kCountFuncs; ++i)
+ _currentTim->func[i].ip = 0;
+
+ return -1;
+}
+
+int TIMInterpreter_LoL::cmd_setLoopIp(const uint16 *param) {
+ if (_vm->speechEnabled()) {
+ if (_vm->snd_updateCharacterSpeech() == 2)
+ _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
+ else
+ advanceToOpcode(21);
+ } else {
+ _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
+ }
+ return 1;
+}
+
+int TIMInterpreter_LoL::cmd_continueLoop(const uint16 *param) {
+ TIM::Function &func = _currentTim->func[_currentFunc];
+
+ if (!func.loopIp)
+ return -2;
+
+ func.ip = func.loopIp;
+
+ if (_vm->snd_updateCharacterSpeech() != 2) {
+ uint16 factor = param[0];
+ if (factor) {
+ const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000);
+ uint32 waitTime = (random * factor) / 0x8000;
+ func.nextTime += waitTime * _vm->tickLength();
+ }
+ }
+
+ return -2;
+}
+
+int TIMInterpreter_LoL::cmd_processDialogue(const uint16 *param) {
+ int res = _vm->processDialogue();
+ if (!res || !_currentTim->procParam)
+ return res;
+
+ _vm->snd_stopSpeech(false);
+
+ _currentTim->func[_currentTim->procFunc].loopIp = 0;
+ _currentTim->dlgFunc = _currentTim->procFunc;
+ _currentTim->procFunc = -1;
+ _currentTim->clickedButton = res;
+
+ _animator->reset(5, false);
+
+ if (_currentTim->procParam)
+ advanceToOpcode(21);
+
+ return res;
+}
+
+int TIMInterpreter_LoL::cmd_dialogueBox(const uint16 *param) {
+ uint16 func = *param;
+ assert(func < TIM::kCountFuncs);
+ _currentTim->procParam = func;
+ _currentTim->clickedButton = 0;
+
+ const char *tmpStr[3];
+ int cnt = 0;
+
+ for (int i = 1; i < 4; i++) {
+ if (param[i] != 0xFFFF) {
+ tmpStr[i-1] = getTableString(param[i]);
+ cnt++;
+ } else {
+ tmpStr[i-1] = 0;
+ }
+ }
+
+ _vm->setupDialogueButtons(cnt, tmpStr[0], tmpStr[1], tmpStr[2]);
+ _vm->gui_notifyButtonListChanged();
+
+ return -3;
+}
+#endif // ENABLE_LOL
+
+} // End of namespace Kyra