aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/script/script_tim.cpp
diff options
context:
space:
mode:
authorathrxx2019-01-26 01:31:34 +0100
committerathrxx2019-03-06 20:48:15 +0100
commit1dfdcc7252ac83643cae7a7447c025da2af63843 (patch)
treeb6736d006bf67d5264dd171c336f0915695d1f88 /engines/kyra/script/script_tim.cpp
parent8b53d20b51771680c3d31aa02c0285b7a8be4e85 (diff)
downloadscummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.gz
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.bz2
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.zip
KYRA: cleanup dir
Reorganize all files in sub directories. The file placement isn't as intuitive as it might be for other engines, which is probably the reason why this hasn't been done before.
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