From 9b9fa7e0a5f92c1b3b3a1252af5ed82a5911505d Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Tue, 2 Aug 2016 18:56:55 +0200 Subject: MOHAWK: Refactor the script manager to read data to Command classes --- engines/mohawk/console.cpp | 6 +- engines/mohawk/riven.cpp | 6 +- engines/mohawk/riven_scripts.cpp | 526 ++++++++++++++++++++------------------- engines/mohawk/riven_scripts.h | 129 +++++++--- 4 files changed, 361 insertions(+), 306 deletions(-) (limited to 'engines/mohawk') diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index f08eee9677..82173c2670 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -617,10 +617,9 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) { debugN("==================================\n\n"); Common::SeekableReadStream *cardStream = _vm->getResource(MKTAG('C','A','R','D'), (uint16)atoi(argv[3])); cardStream->seek(4); - RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream, false); + RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream); for (uint32 i = 0; i < scriptList.size(); i++) { scriptList[i]->dumpScript(varNames, xNames, 0); - delete scriptList[i]; } delete cardStream; } else if (!scumm_stricmp(argv[2], "HSPT")) { @@ -635,10 +634,9 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) { for (uint16 i = 0; i < hotspotCount; i++) { debugN("Hotspot %d:\n", i); hsptStream->seek(22, SEEK_CUR); // Skip non-script related stuff - RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream, false); + RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream); for (uint32 j = 0; j < scriptList.size(); j++) { scriptList[j]->dumpScript(varNames, xNames, 1); - delete scriptList[j]; } } diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index 12b4851a9c..61ceb4130c 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -697,7 +697,8 @@ void MohawkEngine_Riven::runCardScript(uint16 scriptType) { assert(_cardData.hasData); for (uint16 i = 0; i < _cardData.scripts.size(); i++) if (_cardData.scripts[i]->getScriptType() == scriptType) { - _cardData.scripts[i]->runScript(); + RivenScriptPtr script = _cardData.scripts[i]; + script->runScript(); break; } } @@ -706,7 +707,8 @@ void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) { assert(hotspot < _hotspotCount); for (uint16 i = 0; i < _hotspots[hotspot].scripts.size(); i++) if (_hotspots[hotspot].scripts[i]->getScriptType() == scriptType) { - _hotspots[hotspot].scripts[i]->runScript(); + RivenScriptPtr script = _hotspots[hotspot].scripts[i]; + script->runScript(); break; } } diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index 3655452603..8e5207f90e 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -34,60 +34,151 @@ namespace Mohawk { -RivenScript::RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard) - : _vm(vm), _stream(stream), _scriptType(scriptType), _parentStack(parentStack), _parentCard(parentCard) { - setupOpcodes(); - _isRunning = _continueRunning = false; +static void printTabs(byte tabs) { + for (byte i = 0; i < tabs; i++) + debugN("\t"); } -RivenScript::~RivenScript() { - delete _stream; -} - -uint32 RivenScript::calculateCommandSize(Common::SeekableReadStream* script) { - uint16 command = script->readUint16BE(); - uint32 commandSize = 2; - if (command == 8) { - if (script->readUint16BE() != 2) // Arg count? - warning ("if-then-else unknown value is not 2"); - script->readUint16BE(); // variable to check against - uint16 logicBlockCount = script->readUint16BE(); // number of logic blocks - commandSize += 6; // 2 + variable + logicBlocks - - for (uint16 i = 0; i < logicBlockCount; i++) { - script->readUint16BE(); // Block variable - uint16 logicBlockLength = script->readUint16BE(); - commandSize += 4; - for (uint16 j = 0; j < logicBlockLength; j++) - commandSize += calculateCommandSize(script); - } +RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) { + _vm = vm; + _storedMovieOpcode.time = 0; + _storedMovieOpcode.id = 0; +} + +RivenScriptManager::~RivenScriptManager() { + clearStoredMovieOpcode(); +} + +RivenScriptPtr RivenScriptManager::readScript(Common::ReadStream *stream, uint16 scriptType) { + RivenScriptPtr script = RivenScriptPtr(new RivenScript(_vm, scriptType)); + + uint16 commandCount = stream->readUint16BE(); + + for (uint16 i = 0; i < commandCount; i++) { + RivenCommand *command = readCommand(stream); + script->addCommand(command); + } + + return script; +} + +RivenCommand *RivenScriptManager::readCommand(Common::ReadStream *stream) { + uint16 type = stream->readUint16BE(); + + if (type == 8) { + return RivenSwitchCommand::createFromStream(_vm, type, stream); } else { - uint16 argCount = script->readUint16BE(); - commandSize += 2; - for (uint16 i = 0; i < argCount; i++) { - script->readUint16BE(); - commandSize += 2; - } + return RivenSimpleCommand::createFromStream(_vm, type, stream); + } +} + +RivenScriptList RivenScriptManager::readScripts(Common::ReadStream *stream) { + RivenScriptList scriptList; + + uint16 scriptCount = stream->readUint16BE(); + for (uint16 i = 0; i < scriptCount; i++) { + uint16 scriptType = stream->readUint16BE(); + RivenScriptPtr script = readScript(stream, scriptType); + scriptList.push_back(script); } - return commandSize; + return scriptList; +} + +void RivenScriptManager::stopAllScripts() { +// TODO: Restore +// for (uint32 i = 0; i < _currentScripts.size(); i++) +// _currentScripts[i]->stopRunning(); +} + +void RivenScriptManager::setStoredMovieOpcode(const StoredMovieOpcode &op) { + clearStoredMovieOpcode(); + _storedMovieOpcode.script = op.script; + _storedMovieOpcode.id = op.id; + _storedMovieOpcode.time = op.time; +} + +void RivenScriptManager::runStoredMovieOpcode() { + if (_storedMovieOpcode.script) { + _storedMovieOpcode.script->runScript(); + clearStoredMovieOpcode(); + } +} + +void RivenScriptManager::clearStoredMovieOpcode() { + _storedMovieOpcode.script = RivenScriptPtr(); + _storedMovieOpcode.time = 0; + _storedMovieOpcode.id = 0; +} + +RivenScript::RivenScript(MohawkEngine_Riven *vm, uint16 scriptType) : + _vm(vm), + _scriptType(scriptType) { + _continueRunning = true; +} + +RivenScript::~RivenScript() { + for (uint i = 0; i < _commands.size(); i ++) { + delete _commands[i]; + } +} + +void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) { + printTabs(tabs); debugN("Stream Type %d:\n", _scriptType); + dumpCommands(varNames, xNames, tabs + 1); +} + +void RivenScript::dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) { + for (uint16 i = 0; i < _commands.size(); i++) { + _commands[i]->dump(varNames, xNames, tabs); + } +} + +void RivenScript::runScript() { + for (uint16 i = 0; i < _commands.size() && _continueRunning; i++) { + _commands[i]->execute(); + } +} + +void RivenScript::addCommand(RivenCommand *command) { + _commands.push_back(command); +} + +RivenCommand::RivenCommand(MohawkEngine_Riven *vm) : + _vm(vm) { + +} + +RivenCommand::~RivenCommand() { + +} + +RivenSimpleCommand::RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments) : + RivenCommand(vm), + _type(type), + _arguments(arguments) { + setupOpcodes(); +} + +RivenSimpleCommand::~RivenSimpleCommand() { } -uint32 RivenScript::calculateScriptSize(Common::SeekableReadStream* script) { - uint32 oldPos = script->pos(); - uint16 commandCount = script->readUint16BE(); - uint16 scriptSize = 2; // 2 for command count +RivenSimpleCommand *RivenSimpleCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) { + uint16 argCount = stream->readUint16BE(); - for (uint16 i = 0; i < commandCount; i++) - scriptSize += calculateCommandSize(script); + Common::Array arguments; + arguments.resize(argCount); - script->seek(oldPos); - return scriptSize; + for (uint16 i = 0; i < argCount; i++) { + arguments[i] = stream->readUint16BE(); + } + + return new RivenSimpleCommand(vm, type, arguments); } -#define OPCODE(x) { &RivenScript::x, #x } +#define OPCODE(x) { &RivenSimpleCommand::x, #x } -void RivenScript::setupOpcodes() { +void RivenSimpleCommand::setupOpcodes() { static const RivenOpcode riven_opcodes[] = { // 0x00 (0 decimal) OPCODE(empty), @@ -154,139 +245,12 @@ void RivenScript::setupOpcodes() { _opcodes = riven_opcodes; } -static void printTabs(byte tabs) { - for (byte i = 0; i < tabs; i++) - debugN("\t"); -} - -void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) { - if (_stream->pos() != 0) - _stream->seek(0); - - printTabs(tabs); debugN("Stream Type %d:\n", _scriptType); - dumpCommands(varNames, xNames, tabs + 1); -} - -void RivenScript::dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) { - uint16 commandCount = _stream->readUint16BE(); - - for (uint16 i = 0; i < commandCount; i++) { - uint16 command = _stream->readUint16BE(); - - if (command == 8) { // "Switch" Statement - if (_stream->readUint16BE() != 2) - warning ("if-then-else unknown value is not 2"); - uint16 var = _stream->readUint16BE(); - printTabs(tabs); debugN("switch (%s) {\n", varNames[var].c_str()); - uint16 logicBlockCount = _stream->readUint16BE(); - for (uint16 j = 0; j < logicBlockCount; j++) { - uint16 varCheck = _stream->readUint16BE(); - printTabs(tabs + 1); - if (varCheck == 0xFFFF) - debugN("default:\n"); - else - debugN("case %d:\n", varCheck); - dumpCommands(varNames, xNames, tabs + 2); - printTabs(tabs + 2); debugN("break;\n"); - } - printTabs(tabs); debugN("}\n"); - } else if (command == 7) { // Use the variable name - _stream->readUint16BE(); // Skip the opcode var count - printTabs(tabs); - uint16 var = _stream->readUint16BE(); - debugN("%s = %d;\n", varNames[var].c_str(), _stream->readUint16BE()); - } else if (command == 17) { // Use the external command name - _stream->readUint16BE(); // Skip the opcode var count - printTabs(tabs); - debugN("%s(", xNames[_stream->readUint16BE()].c_str()); - uint16 varCount = _stream->readUint16BE(); - for (uint16 j = 0; j < varCount; j++) { - debugN("%d", _stream->readUint16BE()); - if (j != varCount - 1) - debugN(", "); - } - debugN(");\n"); - } else if (command == 24) { // Use the variable name - _stream->readUint16BE(); // Skip the opcode var count - printTabs(tabs); - uint16 var = _stream->readUint16BE(); - debugN("%s += %d;\n", varNames[var].c_str(), _stream->readUint16BE()); - } else { - printTabs(tabs); - uint16 varCount = _stream->readUint16BE(); - debugN("%s(", _opcodes[command].desc); - for (uint16 j = 0; j < varCount; j++) { - debugN("%d", _stream->readUint16BE()); - if (j != varCount - 1) - debugN(", "); - } - debugN(");\n"); - } - } -} - -void RivenScript::runScript() { - _isRunning = _continueRunning = true; - - if (_stream->pos() != 0) - _stream->seek(0); - - processCommands(true); - _isRunning = false; -} - -void RivenScript::processCommands(bool runCommands) { - bool runBlock = true; - - uint16 commandCount = _stream->readUint16BE(); - - for (uint16 j = 0; j < commandCount && !_vm->shouldQuit() && _stream->pos() < _stream->size() && _continueRunning; j++) { - uint16 command = _stream->readUint16BE(); - - if (command == 8) { - // Command 8 contains a conditional branch, similar to switch statements - if (_stream->readUint16BE() != 2) - warning("if-then-else unknown value is not 2"); - - uint16 var = _stream->readUint16BE(); // variable to check against - uint16 logicBlockCount = _stream->readUint16BE(); // number of logic blocks - bool anotherBlockEvaluated = false; - - for (uint16 k = 0; k < logicBlockCount; k++) { - uint16 checkValue = _stream->readUint16BE(); // variable for this logic block - - // Run the following block if the block's variable is equal to the variable to check against - // Don't run it if the parent block is not executed - // And don't run it if another block has already evaluated to true (needed for the default case) - runBlock = (_vm->getStackVar(var) == checkValue || checkValue == 0xffff) && runCommands && !anotherBlockEvaluated; - processCommands(runBlock); - - if (runBlock) - anotherBlockEvaluated = true; - } - } else { - uint16 argCount = _stream->readUint16BE(); - uint16 *argValues = new uint16[argCount]; - - for (uint16 k = 0; k < argCount; k++) - argValues[k] = _stream->readUint16BE(); - - if (runCommands) { - debug (4, "Running opcode %04x, argument count %d", command, argCount); - (this->*(_opcodes[command].proc)) (command, argCount, argValues); - } - - delete[] argValues; - } - } -} - //////////////////////////////// // Opcodes //////////////////////////////// // Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3) -void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { if (argc < 5) // Copy the image to the whole screen, ignoring the rest of the parameters _vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392); else // Copy the image to a certain part of the screen @@ -297,18 +261,19 @@ void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { } // Command 2: go to card (card id) -void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::switchCard(uint16 op, uint16 argc, uint16 *argv) { _vm->changeToCard(argv[0]); // WORKAROUND: If we changed card on a mouse down event, // we want to ignore the next mouse up event so we don't // change card when lifting the mouse on the next card. - if (_scriptType == kMouseDownScript) - _vm->ignoreNextMouseUp(); +// TODO: Restore +// if (_scriptType == kMouseDownScript) +// _vm->ignoreNextMouseUp(); } // Command 3: play an SLST from the script -void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) { int offset = 0, j = 0; uint16 soundCount = argv[offset++]; @@ -342,7 +307,7 @@ void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) { } // Command 4: play local tWAV resource (twav_id, volume, block) -void RivenScript::playSound(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::playSound(uint16 op, uint16 argc, uint16 *argv) { uint16 volume = argv[1]; bool playOnDraw = argv[2] == 1; @@ -350,17 +315,17 @@ void RivenScript::playSound(uint16 op, uint16 argc, uint16 *argv) { } // Command 7: set variable value (variable, value) -void RivenScript::setVariable(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::setVariable(uint16 op, uint16 argc, uint16 *argv) { _vm->getStackVar(argv[0]) = argv[1]; } // Command 8: conditional branch -void RivenScript::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::mohawkSwitch(uint16 op, uint16 argc, uint16 *argv) { // dummy function, this opcode does logic checking in processCommands() } // Command 9: enable hotspot (blst_id) -void RivenScript::enableHotspot(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::enableHotspot(uint16 op, uint16 argc, uint16 *argv) { for (uint16 i = 0; i < _vm->getHotspotCount(); i++) { if (_vm->_hotspots[i].blstID == argv[0]) { debug(2, "Enabling hotspot with BLST ID %d", argv[0]); @@ -373,7 +338,7 @@ void RivenScript::enableHotspot(uint16 op, uint16 argc, uint16 *argv) { } // Command 10: disable hotspot (blst_id) -void RivenScript::disableHotspot(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::disableHotspot(uint16 op, uint16 argc, uint16 *argv) { for (uint16 i = 0; i < _vm->getHotspotCount(); i++) { if (_vm->_hotspots[i].blstID == argv[0]) { debug(2, "Disabling hotspot with BLST ID %d", argv[0]); @@ -386,7 +351,7 @@ void RivenScript::disableHotspot(uint16 op, uint16 argc, uint16 *argv) { } // Command 12: stop sounds (flags) -void RivenScript::stopSound(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::stopSound(uint16 op, uint16 argc, uint16 *argv) { // WORKAROUND: The Play Riven/Visit Riven/Start New Game buttons // in the main menu call this function to stop ambient sounds // after the change stack call to Temple Island. However, this @@ -408,21 +373,21 @@ void RivenScript::stopSound(uint16 op, uint16 argc, uint16 *argv) { } // Command 13: set mouse cursor (cursor_id) -void RivenScript::changeCursor(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::changeCursor(uint16 op, uint16 argc, uint16 *argv) { debug(2, "Change to cursor %d", argv[0]); _vm->_cursor->setCursor(argv[0]); _vm->_system->updateScreen(); } // Command 14: pause script execution (delay in ms, u1) -void RivenScript::delay(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::delay(uint16 op, uint16 argc, uint16 *argv) { debug(2, "Delay %dms", argv[0]); if (argv[0] > 0) _vm->delayAndUpdate(argv[0]); } // Command 17: call external command -void RivenScript::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) { _vm->_externalScriptHandler->runCommand(argc, argv); } @@ -430,7 +395,7 @@ void RivenScript::runExternalCommand(uint16 op, uint16 argc, uint16 *argv) { // Note that this opcode has 1 or 5 parameters, depending on argc // Parameter 0: transition type // Parameters 1-4: transition rectangle -void RivenScript::transition(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::transition(uint16 op, uint16 argc, uint16 *argv) { if (argc == 1) _vm->_gfx->scheduleTransition(argv[0]); else @@ -438,31 +403,31 @@ void RivenScript::transition(uint16 op, uint16 argc, uint16 *argv) { } // Command 19: reload card -void RivenScript::refreshCard(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::refreshCard(uint16 op, uint16 argc, uint16 *argv) { debug(2, "Refreshing card"); _vm->refreshCard(); } // Command 20: disable screen update -void RivenScript::disableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::disableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) { debug(2, "Screen update disabled"); _vm->_gfx->_updatesEnabled = false; } // Command 21: enable screen update -void RivenScript::enableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::enableScreenUpdate(uint16 op, uint16 argc, uint16 *argv) { debug(2, "Screen update enabled"); _vm->_gfx->_updatesEnabled = true; _vm->_gfx->updateScreen(); } // Command 24: increment variable (variable, value) -void RivenScript::incrementVariable(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::incrementVariable(uint16 op, uint16 argc, uint16 *argv) { _vm->getStackVar(argv[0]) += argv[1]; } // Command 27: go to stack (stack name, code high, code low) -void RivenScript::changeStack(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::changeStack(uint16 op, uint16 argc, uint16 *argv) { Common::String stackName = _vm->getName(StackNames, argv[0]); int8 index = -1; @@ -482,54 +447,54 @@ void RivenScript::changeStack(uint16 op, uint16 argc, uint16 *argv) { } // Command 28: disable a movie -void RivenScript::disableMovie(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::disableMovie(uint16 op, uint16 argc, uint16 *argv) { VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]); if (handle) handle->setEnabled(false); } // Command 29: disable all movies -void RivenScript::disableAllMovies(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::disableAllMovies(uint16 op, uint16 argc, uint16 *argv) { _vm->_video->disableAllMovies(); } // Command 31: enable a movie -void RivenScript::enableMovie(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::enableMovie(uint16 op, uint16 argc, uint16 *argv) { VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]); if (handle) handle->setEnabled(true); } // Command 32: play foreground movie - blocking (movie_id) -void RivenScript::playMovieBlocking(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::playMovieBlocking(uint16 op, uint16 argc, uint16 *argv) { _vm->_cursor->hideCursor(); _vm->_video->playMovieBlockingRiven(argv[0]); _vm->_cursor->showCursor(); } // Command 33: play background movie - nonblocking (movie_id) -void RivenScript::playMovie(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::playMovie(uint16 op, uint16 argc, uint16 *argv) { _vm->_video->playMovieRiven(argv[0]); } // Command 34: stop a movie -void RivenScript::stopMovie(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::stopMovie(uint16 op, uint16 argc, uint16 *argv) { _vm->_video->stopMovieRiven(argv[0]); } // Command 36: unknown -void RivenScript::unk_36(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::unk_36(uint16 op, uint16 argc, uint16 *argv) { debug(0, "unk_36: Ignoring"); } // Command 37: fade ambient sounds -void RivenScript::fadeAmbientSounds(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::fadeAmbientSounds(uint16 op, uint16 argc, uint16 *argv) { // Similar to stopSound(), but does fading _vm->_sound->stopAllSLST(true); } // Command 38: Store an opcode for use when playing a movie (movie id, time high, time low, opcode, arguments...) -void RivenScript::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) { // This opcode is used to delay an opcode's usage based on the elapsed // time of a specified movie. However, every use in the game is for // delaying an activateSLST opcode. @@ -547,7 +512,7 @@ void RivenScript::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) { // Build a script out of 'er Common::SeekableReadStream *scriptStream = new Common::MemoryReadStream(scriptBuf, scriptSize, DisposeAfterUse::YES); - RivenScript *script = new RivenScript(_vm, scriptStream, kStoredOpcodeScript, getParentStack(), getParentCard()); + RivenScriptPtr script = _vm->_scriptMan->readScript(scriptStream, kStoredOpcodeScript); uint32 delayTime = (argv[1] << 16) + argv[2]; @@ -563,21 +528,23 @@ void RivenScript::storeMovieOpcode(uint16 op, uint16 argc, uint16 *argv) { } else { // Run immediately if we have no delay script->runScript(); - delete script; } + + delete scriptStream; } // Command 39: activate PLST record (card picture lists) -void RivenScript::activatePLST(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::activatePLST(uint16 op, uint16 argc, uint16 *argv) { _vm->_gfx->drawPLST(argv[0]); // An update is automatically sent here as long as it's not a load or update script and updates are enabled. - if (_scriptType != kCardLoadScript && _scriptType != kCardUpdateScript) + // TODO: Fix the graphics manager + //if (_scriptType != kCardLoadScript && _scriptType != kCardUpdateScript) _vm->_gfx->updateScreen(); } // Command 40: activate SLST record (card ambient sound lists) -void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::activateSLST(uint16 op, uint16 argc, uint16 *argv) { // WORKAROUND: Disable the SLST that is played during Riven's intro. // Riven X does this too (spoke this over with Jeff) if (_vm->getCurStack() == kStackTspit && _vm->getCurCardRMAP() == 0x6e9a && argv[0] == 2) @@ -588,13 +555,13 @@ void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) { } // Command 41: activate MLST record and play -void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) { _vm->_video->activateMLST(argv[0], _vm->getCurCard()); _vm->_video->playMovieRiven(argv[0]); } // Command 43: activate BLST record (card hotspot enabling lists) -void RivenScript::activateBLST(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::activateBLST(uint16 op, uint16 argc, uint16 *argv) { Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, _vm->getCurCard()); uint16 recordCount = blst->readUint16BE(); @@ -616,7 +583,7 @@ void RivenScript::activateBLST(uint16 op, uint16 argc, uint16 *argv) { } // Command 44: activate FLST record (information on which SFXE resource this card should use) -void RivenScript::activateFLST(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::activateFLST(uint16 op, uint16 argc, uint16 *argv) { Common::SeekableReadStream* flst = _vm->getResource(ID_FLST, _vm->getCurCard()); uint16 recordCount = flst->readUint16BE(); @@ -637,7 +604,7 @@ void RivenScript::activateFLST(uint16 op, uint16 argc, uint16 *argv) { } // Command 45: do zip mode -void RivenScript::zipMode(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::zipMode(uint16 op, uint16 argc, uint16 *argv) { // Check the ZIPS records to see if we have a match to the hotspot name Common::String hotspotName = _vm->getHotspotName(_vm->getCurHotspot()); @@ -649,81 +616,120 @@ void RivenScript::zipMode(uint16 op, uint16 argc, uint16 *argv) { } // Command 46: activate MLST record (movie lists) -void RivenScript::activateMLST(uint16 op, uint16 argc, uint16 *argv) { +void RivenSimpleCommand::activateMLST(uint16 op, uint16 argc, uint16 *argv) { _vm->_video->activateMLST(argv[0], _vm->getCurCard()); } -RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) { - _vm = vm; - _storedMovieOpcode.script = 0; - _storedMovieOpcode.time = 0; - _storedMovieOpcode.id = 0; +void RivenSimpleCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) { + printTabs(tabs); + + if (_type == 7) { // Use the variable name + uint16 var = _arguments[0]; + debugN("%s = %d;\n", varNames[var].c_str(), _arguments[1]); + } else if (_type == 17) { // Use the external command name + debugN("%s(", xNames[_arguments[0]].c_str()); + uint16 varCount = _arguments[1]; + for (uint16 j = 0; j < varCount; j++) { + debugN("%d", _arguments[1 + j]); + if (j != varCount - 1) + debugN(", "); + } + debugN(");\n"); + } else if (_type == 24) { // Use the variable name + uint16 var = _arguments[0]; + debugN("%s += %d;\n", varNames[var].c_str(), _arguments[1]); + } else { + debugN("%s(", _opcodes[_type].desc); + for (uint16 j = 0; j < _arguments.size(); j++) { + debugN("%d", _arguments[j]); + if (j != _arguments.size() - 1) + debugN(", "); + } + debugN(");\n"); + } } -RivenScriptManager::~RivenScriptManager() { - for (uint32 i = 0; i < _currentScripts.size(); i++) - delete _currentScripts[i]; +void RivenSimpleCommand::execute() { + uint16 *argValues = new uint16[_arguments.size()]; - clearStoredMovieOpcode(); + for (uint16 k = 0; k < _arguments.size(); k++) + argValues[k] = _arguments[k]; + + debug (4, "Running opcode %04x, argument count %d", _type, _arguments.size()); + (this->*(_opcodes[_type].proc)) (_type, _arguments.size(), argValues); + + delete[] argValues; } -RivenScriptList RivenScriptManager::readScripts(Common::SeekableReadStream *stream, bool garbageCollect) { - if (garbageCollect) - unloadUnusedScripts(); // Garbage collect! +RivenSwitchCommand::RivenSwitchCommand(MohawkEngine_Riven *vm) : + RivenCommand(vm), + _variableId(0) { - RivenScriptList scriptList; +} - uint16 scriptCount = stream->readUint16BE(); - for (uint16 i = 0; i < scriptCount; i++) { - uint16 scriptType = stream->readUint16BE(); - uint32 scriptSize = RivenScript::calculateScriptSize(stream); - RivenScript *script = new RivenScript(_vm, stream->readStream(scriptSize), scriptType, _vm->getCurStack(), _vm->getCurCard()); - scriptList.push_back(script); +RivenSwitchCommand::~RivenSwitchCommand() { + +} + +RivenSwitchCommand *RivenSwitchCommand::createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream) { + RivenSwitchCommand *command = new RivenSwitchCommand(vm); - // Only add it to the scripts that we will free later if it is requested. - // (ie. we don't want to store scripts from the dumpScript console command) - if (garbageCollect) - _currentScripts.push_back(script); + if (stream->readUint16BE() != 2) { + // This value is not used in the original engine + warning("if-then-else unknown value is not 2"); } - return scriptList; -} + // variable to check against + command->_variableId = stream->readUint16BE(); -void RivenScriptManager::stopAllScripts() { - for (uint32 i = 0; i < _currentScripts.size(); i++) - _currentScripts[i]->stopRunning(); -} + // number of logic blocks + uint16 logicBlockCount = stream->readUint16BE(); + command->_branches.resize(logicBlockCount); -void RivenScriptManager::unloadUnusedScripts() { - // Free any scripts that aren't part of the current card and aren't running - for (uint32 i = 0; i < _currentScripts.size(); i++) { - if ((_vm->getCurStack() != _currentScripts[i]->getParentStack() || _vm->getCurCard() != _currentScripts[i]->getParentCard()) && !_currentScripts[i]->isRunning()) { - delete _currentScripts[i]; - _currentScripts.remove_at(i); - i--; - } + for (uint16 i = 0; i < logicBlockCount; i++) { + Branch &branch = command->_branches[i]; + + // Value for this logic block + branch.value = stream->readUint16BE(); + branch.script = vm->_scriptMan->readScript(stream, kStoredOpcodeScript); } -} -void RivenScriptManager::setStoredMovieOpcode(const StoredMovieOpcode &op) { - clearStoredMovieOpcode(); - _storedMovieOpcode.script = op.script; - _storedMovieOpcode.id = op.id; - _storedMovieOpcode.time = op.time; + return command; } -void RivenScriptManager::runStoredMovieOpcode() { - if (_storedMovieOpcode.script) { - _storedMovieOpcode.script->runScript(); - clearStoredMovieOpcode(); +void RivenSwitchCommand::dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) { + printTabs(tabs); debugN("switch (%s) {\n", varNames[_variableId].c_str()); + for (uint16 j = 0; j < _branches.size(); j++) { + printTabs(tabs + 1); + if (_branches[j].value == 0xFFFF) + debugN("default:\n"); + else + debugN("case %d:\n", _branches[j].value); + _branches[j].script->dumpScript(varNames, xNames, tabs + 2); + printTabs(tabs + 2); debugN("break;\n"); } + printTabs(tabs); debugN("}\n"); } -void RivenScriptManager::clearStoredMovieOpcode() { - delete _storedMovieOpcode.script; - _storedMovieOpcode.script = 0; - _storedMovieOpcode.time = 0; - _storedMovieOpcode.id = 0; +void RivenSwitchCommand::execute() { + // Get the switch variable value + uint32 value = _vm->getStackVar(_variableId); + + // Look for a case matching the value + for (uint i = 0; i < _branches.size(); i++) { + if (_branches[i].value == value) { + _branches[i].script->runScript(); + return; + } + } + + // Look for the default case if any + for (uint i = 0; i < _branches.size(); i++) { + if (_branches[i].value == 0Xffff) { + _branches[i].script->runScript(); + return; + } + } } } // End of namespace Mohawk diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h index b669cd5081..9163345a64 100644 --- a/engines/mohawk/riven_scripts.h +++ b/engines/mohawk/riven_scripts.h @@ -49,25 +49,93 @@ enum { }; class MohawkEngine_Riven; -class RivenScript; +class RivenCommand; class RivenScript { public: - RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard); + RivenScript(MohawkEngine_Riven *vm, uint16 scriptType); ~RivenScript(); + void addCommand(RivenCommand *command); + void runScript(); void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs); uint16 getScriptType() { return _scriptType; } - uint16 getParentStack() { return _parentStack; } - uint16 getParentCard() { return _parentCard; } - bool isRunning() { return _isRunning; } void stopRunning() { _continueRunning = false; } - static uint32 calculateScriptSize(Common::SeekableReadStream *script); +private: + MohawkEngine_Riven *_vm; + + Common::Array _commands; + uint16 _scriptType; + bool _continueRunning; + + void dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs); +}; + +typedef Common::SharedPtr RivenScriptPtr; +typedef Common::Array RivenScriptList; + +class RivenScriptManager { +public: + RivenScriptManager(MohawkEngine_Riven *vm); + ~RivenScriptManager(); + + RivenScriptPtr readScript(Common::ReadStream *stream, uint16 scriptType); + RivenScriptList readScripts(Common::ReadStream *stream); + void stopAllScripts(); + + struct StoredMovieOpcode { + RivenScriptPtr script; + uint32 time; + uint16 id; + }; + + uint16 getStoredMovieOpcodeID() { return _storedMovieOpcode.id; } + uint32 getStoredMovieOpcodeTime() { return _storedMovieOpcode.time; } + void setStoredMovieOpcode(const StoredMovieOpcode &op); + void runStoredMovieOpcode(); + void clearStoredMovieOpcode(); private: - typedef void (RivenScript::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv); + MohawkEngine_Riven *_vm; + + StoredMovieOpcode _storedMovieOpcode; + + RivenCommand *readCommand(Common::ReadStream *stream); +}; + +class RivenCommand { +public: + RivenCommand(MohawkEngine_Riven *vm); + virtual ~RivenCommand(); + + virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) = 0; + + virtual void execute() = 0; + +protected: + MohawkEngine_Riven *_vm; +}; + +class RivenSimpleCommand : public RivenCommand { +public: + static RivenSimpleCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream); + virtual ~RivenSimpleCommand(); + + // RivenCommand API + virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) override; + virtual void execute() override; + +private: + typedef Common::Array ArgumentArray; + + RivenSimpleCommand(MohawkEngine_Riven *vm, int type, const ArgumentArray &arguments); + + int _type; + ArgumentArray _arguments; + + typedef void (RivenSimpleCommand::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv); struct RivenOpcode { OpcodeProcRiven proc; const char *desc; @@ -75,16 +143,6 @@ private: const RivenOpcode *_opcodes; void setupOpcodes(); - MohawkEngine_Riven *_vm; - Common::SeekableReadStream *_stream; - uint16 _scriptType, _parentStack, _parentCard; - bool _isRunning, _continueRunning; - - void dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs); - void processCommands(bool runCommands); - - static uint32 calculateCommandSize(Common::SeekableReadStream *script); - DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); } //Opcodes @@ -124,34 +182,25 @@ private: DECLARE_OPCODE(activateMLST); }; -typedef Common::Array RivenScriptList; - -class RivenScriptManager { +class RivenSwitchCommand : public RivenCommand { public: - RivenScriptManager(MohawkEngine_Riven *vm); - ~RivenScriptManager(); + static RivenSwitchCommand *createFromStream(MohawkEngine_Riven *vm, int type, Common::ReadStream *stream); + virtual ~RivenSwitchCommand(); - RivenScriptList readScripts(Common::SeekableReadStream *stream, bool garbageCollect = true); - void stopAllScripts(); - - struct StoredMovieOpcode { - RivenScript *script; - uint32 time; - uint16 id; - }; - - uint16 getStoredMovieOpcodeID() { return _storedMovieOpcode.id; } - uint32 getStoredMovieOpcodeTime() { return _storedMovieOpcode.time; } - void setStoredMovieOpcode(const StoredMovieOpcode &op); - void runStoredMovieOpcode(); - void clearStoredMovieOpcode(); + // RivenCommand API + virtual void dump(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) override; + virtual void execute() override; private: - void unloadUnusedScripts(); - RivenScriptList _currentScripts; - MohawkEngine_Riven *_vm; + RivenSwitchCommand(MohawkEngine_Riven *vm); - StoredMovieOpcode _storedMovieOpcode; + struct Branch { + uint16 value; + RivenScriptPtr script; + }; + + uint16 _variableId; + Common::Array _branches; }; } // End of namespace Mohawk -- cgit v1.2.3