aboutsummaryrefslogtreecommitdiff
path: root/engines/groovie/script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/groovie/script.cpp')
-rw-r--r--engines/groovie/script.cpp1566
1 files changed, 1566 insertions, 0 deletions
diff --git a/engines/groovie/script.cpp b/engines/groovie/script.cpp
new file mode 100644
index 0000000000..fc4a7fd4f1
--- /dev/null
+++ b/engines/groovie/script.cpp
@@ -0,0 +1,1566 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "groovie/debug.h"
+#include "groovie/music.h"
+#include "groovie/script.h"
+#include "groovie/groovie.h"
+
+#include "common/config-manager.h"
+#include "common/endian.h"
+#include "common/events.h"
+#include "common/savefile.h"
+#include "sound/audiocd.h"
+
+#define NUM_OPCODES 90
+
+namespace Groovie {
+
+void debugScript(int level, bool nl, const char *s, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+
+ uint32 engine_level = kGroovieDebugScript | kGroovieDebugAll;
+
+ if (gDebugLevel != 11)
+ if (!(Common::getEnabledSpecialDebugLevels() & engine_level))
+ return;
+
+ va_start(va, s);
+ vsnprintf(buf, STRINGBUFLEN, s, va);
+ va_end(va);
+
+ if (nl)
+ debug(level, buf);
+ else
+ debugN(level, buf);
+}
+
+Script::Script(GroovieEngine *vm) :
+ _code(NULL), _savedCode(NULL), _stacktop(0),
+ _debugger(NULL), _error(false), _vm(vm),
+ _videoFile(NULL), _videoRef(0), _font(NULL) {
+ // Initialize the random source
+ _vm->_system->getEventManager()->registerRandomSource(_random, "GroovieScripts");
+
+ // Prepare the variables
+ _bitflags = 0;
+ for (int i = 0; i < 0x400; i++) {
+ _variables[i] = 0;
+ }
+
+ // Initialize the music type variable
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
+ if (midiDriver == MD_ADLIB) {
+ // MIDI through AdLib
+ _variables[0x100] = 0;
+ } else if ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")) {
+ // MT-32
+ _variables[0x100] = 2;
+ } else {
+ // GM
+ _variables[0x100] = 1;
+ }
+
+ _hotspotTopAction = 0;
+ _hotspotBottomAction = 0;
+ _hotspotRightAction = 0;
+ _hotspotLeftAction = 0;
+ _hotspotCursorOldX = 1000;
+ _hotspotCursorOldY = 1000;
+}
+
+Script::~Script() {
+ delete[] _code;
+ delete[] _savedCode;
+
+ delete _font;
+ delete _videoFile;
+}
+
+void Script::setDebugger(Debugger *debugger) {
+ _debugger = debugger;
+}
+
+bool Script::loadScript(Common::String filename) {
+ // Try to open the script file
+ Common::File scriptfile;
+ if (!scriptfile.open(filename)) {
+ return false;
+ }
+
+ // Save the script filename
+ _scriptFile = filename;
+
+ // Load the code
+ _code = new byte[0x10000];
+ scriptfile.read(_code, 0x10000);
+ scriptfile.close();
+
+ // Initialize the script
+ _currentInstruction = 0;
+
+ return true;
+}
+
+void Script::directGameLoad(int slot) {
+ // Reject invalid slots
+ if (slot < 0 || slot > 9) {
+ return;
+ }
+
+ // TODO: Return to the main script, likely reusing most of o_returnscript()
+
+ // HACK: We set variable 0x19 to the slot to load, and set the current
+ // instruction to the one that actually loads the saved game specified
+ // in that variable. This will change in other versions of the game and
+ // in other games.
+ _variables[0x19] = slot;
+ _currentInstruction = 0x287;
+
+ // TODO: We'll probably need to start by running the beginning of the
+ // script to let it do the soundcard initialization and then do the
+ // actual loading.
+}
+
+void Script::step() {
+ // Reset the error status
+ _error = false;
+
+ // Prepare the base debug string
+ char debugstring[10];
+ sprintf(debugstring, "@0x%04X: ", _currentInstruction);
+ _debugString = _scriptFile + debugstring;
+
+ // Get the current opcode
+ byte opcode = readScript8bits();
+ _firstbit = ((opcode & 0x80) != 0);
+ opcode = opcode & 0x7F;
+
+ // Show the opcode debug string
+ sprintf(debugstring, "op 0x%02X: ", opcode);
+ _debugString += debugstring;
+ debugScript(1, false, _debugString.c_str());
+
+ // Detect invalid opcodes
+ if (opcode >= NUM_OPCODES) {
+ o_invalid();
+ return;
+ }
+
+ // Execute the current opcode
+ OpcodeFunc op = _opcodes[opcode];
+ (this->*op)();
+}
+
+void Script::setMouseClick() {
+ _eventMouseClicked = true;
+}
+
+void Script::setKbdChar(uint8 c) {
+ _eventKbdChar = c;
+}
+
+bool Script::haveError() {
+ return _error;
+}
+
+void Script::error(const char *msg) {
+ // Prepend the debugging info to the error
+ Common::String msg2 = _debugString + msg;
+
+ // Print the error message
+ ::error("ERROR: %s\n", msg2.c_str());
+
+ // Show it in the debugger
+ _debugger->attach(msg2.c_str());
+
+ // Set the error state
+ _error = true;
+}
+
+uint8 Script::readScript8bits() {
+ uint8 data = _code[_currentInstruction];
+ _currentInstruction++;
+ return data;
+}
+
+uint8 Script::readScriptVar() {
+ uint8 data = _variables[readScript8or16bits()];
+ return data;
+}
+
+uint16 Script::readScript16bits() {
+ uint16 data = READ_LE_UINT16(_code + _currentInstruction);
+ _currentInstruction += 2;
+ return data;
+}
+
+uint32 Script::readScript32bits() {
+ uint32 data = READ_LE_UINT32(_code + _currentInstruction);
+ _currentInstruction += 4;
+ return data;
+}
+
+uint16 Script::readScript8or16bits() {
+ if (_firstbit) {
+ return readScript8bits();
+ } else {
+ return readScript16bits();
+ }
+}
+
+uint8 Script::readScriptChar(bool allow7C, bool limitVal, bool limitVar) {
+ uint8 result;
+ uint8 data = readScript8bits();
+
+ if (limitVal) {
+ data &= 0x7F;
+ }
+
+ if (allow7C && (data == 0x7C)) {
+ // Index a bidimensional array
+ uint8 parta, partb;
+ parta = readScriptChar(false, false, false);
+ partb = readScriptChar(false, true, true);
+ result = _variables[0x0A * parta + partb + 0x19];
+ } else if (data == 0x23) {
+ // Index an array
+ data = readScript8bits();
+ if (limitVar) {
+ data &= 0x7F;
+ }
+ result = _variables[data - 0x61];
+ } else {
+ // Immediate value
+ result = data - 0x30;
+ }
+ return result;
+}
+
+uint16 Script::getVideoRefString() {
+ Common::String str;
+ byte c;
+
+ while ((c = readScript8bits())) {
+ switch (c) {
+ case 0x23:
+ c = readScript8bits();
+ c = _variables[c - 0x61] + 0x30;
+ if (c >= 0x41 && c <= 0x5A) {
+ c += 0x20;
+ }
+ break;
+ case 0x7C:
+ uint8 parta, partb;
+ parta = readScriptChar(false, false, false);
+ partb = readScriptChar(false, false, false);
+ c = _variables[0x0A * parta + partb + 0x19] + 0x30;
+ break;
+ default:
+ if (c >= 0x41 && c <= 0x5A) {
+ c += 0x20;
+ }
+ }
+ // Append the current character at the end of the string
+ str += c;
+ }
+
+ // Add a trailing dot
+ str += 0x2E;
+
+ debugScript(0, false, "%s", str.c_str());
+
+ // Extract the script name.
+ Common::String scriptname(_scriptFile.c_str(), _scriptFile.size() - 4);
+
+ // Get the fileref of the resource
+ return _vm->_resMan->getRef(str, scriptname);
+}
+
+bool Script::hotspot(Common::Rect rect, uint16 address, uint8 cursor) {
+ // Test if the current mouse position is contained in the specified rectangle
+ Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
+ bool contained = rect.contains(mousepos);
+
+ // Show hotspots when debugging
+ if (Common::getEnabledSpecialDebugLevels() & (kGroovieDebugHotspots | kGroovieDebugAll)) {
+ rect.translate(0, -80);
+ _vm->_graphicsMan->_foreground.frameRect(rect, 250);
+ _vm->_system->copyRectToScreen((byte*)_vm->_graphicsMan->_foreground.getBasePtr(0, 0), 640, 0, 80, 640, 320);
+ _vm->_system->updateScreen();
+ }
+
+ // If there's an already planned action, do nothing
+ if (_inputAction != -1) {
+ return false;
+ }
+
+ if (contained) {
+ // Change the mouse cursor
+ if (_newCursorStyle == 5) {
+ _newCursorStyle = cursor;
+ }
+
+ // If clicked with the mouse, jump to the specified address
+ if (_mouseClicked) {
+ _inputAction = address;
+ }
+ }
+
+ return contained;
+}
+
+void Script::loadgame(uint slot) {
+ Common::String filename = ConfMan.getActiveDomainName() + ".00" + ('0' + slot);
+ Common::InSaveFile *file = _vm->_system->getSavefileManager()->openForLoading(filename.c_str());
+
+ // Loading the variables. It is endian safe because they're byte variables
+ file->read(_variables, 0x400);
+
+ delete file;
+}
+
+void Script::savegame(uint slot) {
+ Common::String filename = ConfMan.getActiveDomainName() + ".00" + ('0' + slot);
+ Common::OutSaveFile *file = _vm->_system->getSavefileManager()->openForSaving(filename.c_str());
+
+ // Saving the variables. It is endian safe because they're byte variables
+ file->write(_variables, 0x400);
+
+ delete file;
+}
+
+// OPCODES
+
+void Script::o_invalid() {
+ error("Invalid opcode");
+}
+
+void Script::o_nop() {
+ debugScript(1, true, "NOP");
+}
+
+void Script::o_nop8() {
+ uint8 tmp = readScript8bits();
+ debugScript(1, true, "NOP8: 0x%02X", tmp);
+}
+
+void Script::o_nop16() {
+ uint16 tmp = readScript16bits();
+ debugScript(1, true, "NOP16: 0x%04X", tmp);
+}
+
+void Script::o_nop32() {
+ uint32 tmp = readScript32bits();
+ debugScript(1, true, "NOP32: 0x%08X", tmp);
+}
+
+void Script::o_nop8or16() {
+ uint16 tmp = readScript8or16bits();
+ debugScript(1, true, "NOP8OR16: 0x%04X", tmp);
+}
+
+void Script::o_playsong() { // 0x02
+ uint16 fileref = readScript16bits();
+ debugScript(1, true, "PlaySong(0x%04X): Play xmidi file", fileref);
+ if (fileref == 0x4C17) {
+ warning("this song is special somehow");
+ // don't save the reference?
+ }
+ _vm->_musicPlayer->playSong(fileref);
+}
+
+void Script::o_bf9on() { // 0x03
+ debugScript(1, true, "BF9ON: bitflag 9 turned on");
+ _bitflags |= 1 << 9;
+}
+
+void Script::o_palfadeout() {
+ debugScript(1, true, "PALFADEOUT");
+ _vm->_graphicsMan->fadeOut();
+}
+
+void Script::o_bf8on() { // 0x05
+ debugScript(1, true, "BF8ON: bitflag 8 turned on");
+ _bitflags |= 1 << 8;
+}
+
+void Script::o_bf6on() { // 0x06
+ debugScript(1, true, "BF6ON: bitflag 6 turned on");
+ _bitflags |= 1 << 6;
+}
+
+void Script::o_bf7on() { // 0x07
+ debugScript(1, true, "BF7ON: bitflag 7 turned on");
+ _bitflags |= 1 << 7;
+}
+
+void Script::o_setbackgroundsong() { // 0x08
+ uint16 fileref = readScript16bits();
+ debugScript(1, true, "SetBackgroundSong(0x%04X)", fileref);
+ _vm->_musicPlayer->setBackgroundSong(fileref);
+}
+
+void Script::o_videofromref() { // 0x09
+ uint16 fileref = readScript16bits();
+
+ // Show the debug information just when starting the playback
+ if (fileref != _videoRef) {
+ debugScript(1, false, "VIDEOFROMREF(0x%04X) (Not fully imp): Play video file from ref", fileref);
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Playing video 0x%04X via 0x09", fileref);
+ }
+ switch (fileref) {
+ case 0x1C03: // Trilobyte logo
+ case 0x1C04: // Virgin logo
+ case 0x1C05: // Credits
+ if (fileref != _videoRef) {
+ debugScript(1, true, "Use external file if available");
+ }
+ break;
+
+ case 0x400D: // floating objects in music room
+ case 0x5060: // a sound from gamwav?
+ case 0x5098: // a sound from gamwav?
+ case 0x2402: // House becomes book in intro?
+ case 0x1426: // Turn to face front in hall: played after intro
+ case 0x206D: // Cards on table puzzle (bedroom)
+ case 0x2001: // Coins on table puzzle (bedroom)
+ if (fileref != _videoRef) {
+ debugScript(1, false, " (This video is special somehow!)");
+ warning("(This video (0x%04X) is special somehow!)", fileref);
+ }
+ }
+ if (fileref != _videoRef) {
+ debugScript(1, true, "");
+ }
+ // Play the video
+ if (!playvideofromref(fileref)) {
+ // Move _currentInstruction back
+ _currentInstruction -= 3;
+ }
+}
+
+bool Script::playvideofromref(uint16 fileref) {
+ // It isn't the current video, open it
+ if (fileref != _videoRef) {
+
+ // Debug bitflags
+ debugScript(1, false, "Play video 0x%04X (bitflags:", fileref);
+ for (int i = 10; i >= 0; i--) {
+ debugScript(1, false, "%d", _bitflags & (1 << i)? 1 : 0);
+ }
+ debugScript(1, true, ")");
+
+ // Close the previous video file
+ if (_videoFile) {
+ _videoRef = 0;
+ delete _videoFile;
+ }
+
+ // Try to open the new file
+ _videoFile = _vm->_resMan->open(fileref);
+
+ if (_videoFile) {
+ _videoRef = fileref;
+ _vm->_videoPlayer->load(_videoFile, _bitflags);
+ } else {
+ error("Couldn't open file");
+ return true;
+ }
+
+ _bitflags = 0;
+ }
+
+ // Video available, play one frame
+ if (_videoFile) {
+ bool endVideo = _vm->_videoPlayer->playFrame();
+
+ if (endVideo) {
+ // Close the file
+ delete _videoFile;
+ _videoFile = NULL;
+ _videoRef = 0;
+
+ // Clear the input events while playing the video
+ _eventMouseClicked = false;
+ _eventKbdChar = 0;
+
+ // Newline
+ debugScript(1, true, "");
+ }
+
+ // Let the caller know if the video has ended
+ return endVideo;
+ }
+
+ // If the file is closed, finish the playback
+ return true;
+}
+
+void Script::o_bf5on() { // 0x0A
+ debugScript(1, true, "BF5ON: bitflag 5 turned on");
+ _bitflags |= 1 << 5;
+}
+
+void Script::o_inputloopstart() {
+ debugScript(5, true, "Input loop start");
+
+ // Reset the input action and the mouse cursor
+ _inputAction = -1;
+ _newCursorStyle = 5;
+
+ // Save the input loop address
+ _inputLoopAddress = _currentInstruction - 1;
+
+ // Save the current mouse state for the whole loop
+ _mouseClicked = _eventMouseClicked;
+ _eventMouseClicked = false;
+
+ // Save the current pressed character for the whole loop
+ _kbdChar = _eventKbdChar;
+ _eventKbdChar = 0;
+}
+
+void Script::o_keyboardaction() {
+ uint8 val = readScript8bits();
+ uint16 address = readScript16bits();
+
+ debugScript(5, true, "Test key == 0x%02X @0x%04X", val, address);
+
+ // If there's an already planned action, do nothing
+ if (_inputAction != -1) {
+ return;
+ }
+
+ // Check the typed key
+ if (_kbdChar == val) {
+ // Exit the input loop
+ _inputLoopAddress = 0;
+
+ // Save the action address
+ _inputAction = address;
+ }
+}
+
+void Script::o_hotspot_rect() {
+ uint16 left = readScript16bits();
+ uint16 top = readScript16bits();
+ uint16 right = readScript16bits();
+ uint16 bottom = readScript16bits();
+ uint16 address = readScript16bits();
+ uint8 cursor = readScript8bits();
+
+ debugScript(5, true, "HOTSPOT-RECT(%d,%d,%d,%d) @0x%04X cursor=%d", left, top, right, bottom, address, cursor);
+
+ // Mark the specified rectangle
+ Common::Rect rect(left, top, right, bottom);
+ hotspot(rect, address, cursor);
+}
+
+void Script::o_hotspot_left() {
+ uint16 address = readScript16bits();
+
+ debugScript(5, true, "HOTSPOT-LEFT @0x%04X", address);
+
+ // Mark the leftmost 100 pixels of the game area
+ Common::Rect rect(0, 80, 100, 400);
+ hotspot(rect, address, 1);
+}
+
+void Script::o_hotspot_right() {
+ uint16 address = readScript16bits();
+
+ debugScript(5, true, "HOTSPOT-RIGHT @0x%04X", address);
+
+ // Mark the rightmost 100 pixels of the game area
+ Common::Rect rect(540, 80, 640, 400);
+ hotspot(rect, address, 2);
+}
+
+void Script::o_hotspot_center() {
+ uint16 address = readScript16bits();
+
+ debugScript(5, true, "HOTSPOT-CENTER @0x%04X", address);
+
+ // Mark the centremost 240 pixels of the game area
+ Common::Rect rect(200, 80, 440, 400);
+ hotspot(rect, address, 0);
+}
+
+void Script::o_hotspot_current() {
+ uint16 address = readScript16bits();
+
+ debugScript(5, true, "HOTSPOT-CURRENT @0x%04X", address);
+
+ // The original interpreter doesn't check the position, so accept the
+ // whole screen
+ Common::Rect rect(0, 0, 640, 480);
+ hotspot(rect, address, 0);
+}
+
+void Script::o_inputloopend() {
+ debugScript(5, true, "Input loop end");
+
+ // Handle the predefined hotspots
+ if (_hotspotTopAction) {
+ Common::Rect rect(0, 0, 640, 80);
+ hotspot(rect, _hotspotTopAction, _hotspotTopCursor);
+ }
+ if (_hotspotBottomAction) {
+ Common::Rect rect(0, 400, 640, 480);
+ hotspot(rect, _hotspotBottomAction, _hotspotBottomCursor);
+ }
+ if (_hotspotRightAction) {
+ Common::Rect rect(560, 0, 640, 480);
+ hotspot(rect, _hotspotRightAction, 2);
+ }
+ if (_hotspotLeftAction) {
+ Common::Rect rect(0, 0, 80, 480);
+ hotspot(rect, _hotspotLeftAction, 1);
+ }
+
+ // Actually execute the planned action
+ if (_inputAction != -1) {
+ // Jump to the planned address
+ _currentInstruction = _inputAction;
+
+ // Exit the input loop
+ _inputLoopAddress = 0;
+ _vm->_system->showMouse(false);
+ }
+
+ // Nothing to do
+ if (_inputLoopAddress) {
+ if (_newCursorStyle != _vm->_cursorMan->getStyle()) {
+ _vm->_cursorMan->setStyle(_newCursorStyle);
+ }
+ _vm->_system->showMouse(true);
+
+ // Go back to the begining of the loop
+ _currentInstruction = _inputLoopAddress;
+
+ // There's nothing to do until we get some input
+ _vm->waitForInput();
+ }
+}
+
+void Script::o_random() {
+ uint16 varnum = readScript8or16bits();
+ uint8 maxnum = readScript8bits();
+
+ debugScript(1, true, "RANDOM: var[0x%04X] = rand(%d)", varnum, maxnum);
+
+ _variables[varnum] = _random.getRandomNumber(maxnum);
+}
+
+void Script::o_jmp() {
+ uint16 address = readScript16bits();
+
+ debugScript(1, true, "JMP @0x%04X", address);
+
+ // Set the current address
+ _currentInstruction = address;
+}
+
+void Script::o_loadstring() {
+ uint16 varnum = readScript8or16bits();
+
+ debugScript(1, false, "LOADSTRING var[0x%04X..] =", varnum);
+ do {
+ _variables[varnum++] = readScriptChar(true, true, true);
+ debugScript(1, false, " 0x%02X", _variables[varnum - 1]);
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+ debugScript(1, true, "");
+}
+
+void Script::o_ret() {
+ uint8 val = readScript8bits();
+
+ debugScript(1, true, "RET %d", val);
+
+ // Set the return value
+ _variables[0x102] = val;
+
+ // Get the return address
+ if (_stacktop > 0) {
+ _stacktop--;
+ _currentInstruction = _stack[_stacktop];
+ } else {
+ error("Return: Stack is empty");
+ }
+}
+
+void Script::o_call() {
+ uint16 address = readScript16bits();
+
+ debugScript(1, true, "CALL @0x%04X", address);
+
+ // Save return address in the call stack
+ _stack[_stacktop] = _currentInstruction;
+ _stacktop++;
+
+ // Change the current instruction
+ _currentInstruction = address;
+}
+
+void Script::o_sleep() {
+ uint16 time = readScript16bits();
+
+ debugScript(1, true, "SLEEP 0x%04X", time);
+
+ _vm->_system->delayMillis(time * 3);
+}
+
+void Script::o_strcmpnejmp() { // 0x1A
+ uint16 varnum = readScript8or16bits();
+ uint8 val;
+ uint8 result = 1;
+
+ debugScript(1, false, "STRCMP-NEJMP: var[0x%04X..],", varnum);
+
+ do {
+ val = readScriptChar(true, true, true);
+
+ if (_variables[varnum] != val) {
+ result = 0;
+ }
+ varnum++;
+ debugScript(1, false, " 0x%02X", val);
+
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+
+ uint16 address = readScript16bits();
+ if (!result) {
+ debugScript(1, true, " jumping to @0x%04X", address);
+ _currentInstruction = address;
+ } else {
+ debugScript(1, true, " not jumping");
+ }
+}
+
+void Script::o_xor_obfuscate() {
+ uint16 varnum = readScript8or16bits();
+
+ debugScript(1, false, "XOR OBFUSCATE: var[0x%04X..] = ", varnum);
+ do {
+ uint8 val = readScript8bits();
+ _firstbit = ((val & 0x80) != 0);
+ val &= 0x4F;
+
+ _variables[varnum] ^= val;
+ debugScript(1, false, "%c", _variables[varnum]);
+
+ varnum++;
+ } while (!_firstbit);
+ debugScript(1, true, "");
+}
+
+void Script::o_vdxtransition() { // 0x1C
+ uint16 fileref = readScript16bits();
+
+ // Show the debug information just when starting the playback
+ if (fileref != _videoRef) {
+ debugScript(1, true, "VDX transition fileref = 0x%04X", fileref);
+ debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Playing video 0x%04X with transition", fileref);
+ }
+
+ // Set bit 1
+ _bitflags |= 1 << 1;
+
+ // Clear bit 7
+ _bitflags &= ~(1 << 7);
+
+ // Set bit 2 if _firstbit
+ if (_firstbit) {
+ _bitflags |= 1 << 2;
+ }
+
+ // Play the video
+ if (!playvideofromref(fileref)) {
+ // Move _currentInstruction back
+ _currentInstruction -= 3;
+ }
+}
+
+void Script::o_swap() {
+ uint16 varnum1 = readScript8or16bits();
+ uint16 varnum2 = readScript16bits();
+
+ debugScript(1, true, "SWAP var[0x%04X] <-> var[0x%04X]", varnum1, varnum2);
+
+ uint8 tmp = _variables[varnum1];
+ _variables[varnum1] = _variables[varnum2];
+ _variables[varnum2] = tmp;
+}
+
+void Script::o_inc() {
+ uint16 varnum = readScript8or16bits();
+
+ debugScript(1, true, "INC var[0x%04X]", varnum);
+
+ _variables[varnum]++;
+}
+
+void Script::o_dec() {
+ uint16 varnum = readScript8or16bits();
+
+ debugScript(1, true, "DEC var[0x%04X]", varnum);
+
+ _variables[varnum]--;
+}
+
+void Script::o_strcmpnejmp_var() { // 0x21
+ uint16 data = readScriptVar();
+
+ if (data > 9) {
+ data -= 7;
+ }
+ data = _variables[data + 0x19];
+ bool stringsmatch = 1;
+ do {
+ if (_variables[data++] != readScriptChar(true, true, true)) {
+ stringsmatch = 0;
+ }
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+
+ uint16 offset = readScript16bits();
+ if (!stringsmatch) {
+ _currentInstruction = offset;
+ }
+}
+
+void Script::o_copybgtofg() { // 0x22
+ debugScript(1, true, "COPY_BG_TO_FG");
+ memcpy(_vm->_graphicsMan->_foreground.getBasePtr(0, 0), _vm->_graphicsMan->_background.getBasePtr(0, 0), 640 * 320);
+}
+
+void Script::o_strcmpeqjmp() { // 0x23
+ uint16 varnum = readScript8or16bits();
+ uint8 val;
+ uint8 result = 1;
+
+ debugScript(1, false, "STRCMP-EQJMP: var[0x%04X..],", varnum);
+ do {
+ val = readScriptChar(true, true, true);
+
+ if (_variables[varnum] != val) {
+ result = 0;
+ }
+ varnum++;
+ debugScript(1, false, " 0x%02X", val);
+
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+
+ uint16 address = readScript16bits();
+ if (result) {
+ debugScript(1, true, " jumping to @0x%04X", address);
+ _currentInstruction = address;
+ } else {
+ debugScript(1, true, " not jumping");
+ }
+}
+
+void Script::o_mov() {
+ uint16 varnum1 = readScript8or16bits();
+ uint16 varnum2 = readScript16bits();
+
+ debugScript(1, true, "MOV var[0x%04X] = var[0x%04X]", varnum1, varnum2);
+
+ _variables[varnum1] = _variables[varnum2];
+}
+
+void Script::o_add() {
+ uint16 varnum1 = readScript8or16bits();
+ uint16 varnum2 = readScript16bits();
+
+ debugScript(1, true, "ADD var[0x%04X] += var[0x%04X]", varnum1, varnum2);
+
+ _variables[varnum1] += _variables[varnum2];
+}
+
+void Script::o_videofromstring1() {
+ uint16 instStart = _currentInstruction;
+ uint16 fileref = getVideoRefString();
+
+ // Show the debug information just when starting the playback
+ if (fileref != _videoRef) {
+ debugScript(0, true, "VIDEOFROMSTRING1 0x%04X", fileref);
+ }
+
+ // Play the video
+ if (!playvideofromref(fileref)) {
+ // Move _currentInstruction back
+ _currentInstruction = instStart - 1;
+ }
+}
+
+void Script::o_videofromstring2() {
+ uint16 instStart = _currentInstruction;
+ uint16 fileref = getVideoRefString();
+
+ // Show the debug information just when starting the playback
+ if (fileref != _videoRef) {
+ debugScript(0, true, "VIDEOFROMSTRING2 0x%04X", fileref);
+ }
+
+ // Set bit 1
+ _bitflags |= 1 << 1;
+
+ // Set bit 2 if _firstbit
+ if (_firstbit) {
+ _bitflags |= 1 << 2;
+ }
+
+ // Play the video
+ if (!playvideofromref(fileref)) {
+ // Move _currentInstruction back
+ _currentInstruction = instStart - 1;
+ }
+}
+
+void Script::o_stopmidi() {
+ debugScript(1, true, "STOPMIDI (TODO)");
+}
+
+void Script::o_endscript() {
+ debugScript(1, true, "END OF SCRIPT");
+ _error = true;
+}
+
+void Script::o_sethotspottop() {
+ uint16 address = readScript16bits();
+ uint8 cursor = readScript8bits();
+
+ debugScript(5, true, "SETHOTSPOTTOP @0x%04X cursor=%d", address, cursor);
+
+ _hotspotTopAction = address;
+ _hotspotTopCursor = cursor;
+}
+
+void Script::o_sethotspotbottom() {
+ uint16 address = readScript16bits();
+ uint8 cursor = readScript8bits();
+
+ debugScript(5, true, "SETHOTSPOTBOTTOM @0x%04X cursor=%d", address, cursor);
+
+ _hotspotBottomAction = address;
+ _hotspotBottomCursor = cursor;
+}
+
+void Script::o_loadgame() {
+ uint16 varnum = readScript8or16bits();
+ uint8 slot = _variables[varnum];
+
+ debugScript(1, true, "LOADGAME var[0x%04X] -> slot=%d (TODO)", varnum, slot);
+
+ loadgame(slot);
+ _vm->_system->clearScreen();
+}
+
+void Script::o_savegame() {
+ uint16 varnum = readScript8or16bits();
+ uint8 slot = _variables[varnum];
+
+ debugScript(1, true, "SAVEGAME var[0x%04X] -> slot=%d (TODO)", varnum, slot);
+
+ savegame(slot);
+}
+
+void Script::o_hotspotbottom_4() { //0x30
+ uint16 address = readScript16bits();
+
+ debugScript(5, true, "HOTSPOT-BOTTOM @0x%04X", address);
+
+ // Mark the 80 pixels under the game area
+ Common::Rect rect(0, 400, 640, 480);
+ hotspot(rect, address, 4);
+}
+
+void Script::o_midivolume() {
+ uint16 arg1 = readScript16bits();
+ uint16 arg2 = readScript16bits();
+
+ debugScript(1, true, "MIDI volume: %d %d", arg1, arg2);
+ _vm->_musicPlayer->setGameVolume(arg1, arg2);
+}
+
+void Script::o_jne() {
+ int16 varnum1 = readScript8or16bits();
+ uint16 varnum2 = readScript16bits();
+ uint16 address = readScript16bits();
+
+ debugScript(1, false, "JNE: var[var[0x%04X] - 0x31] != var[0x%04X] @0x%04X", varnum1, varnum2, address);
+
+ if (_variables[_variables[varnum1] - 0x31] != _variables[varnum2]) {
+ _currentInstruction = address;
+ debugScript(1, true, " jumping to @0x%04X", address);
+ } else {
+ debugScript(1, true, " not jumping");
+ }
+}
+
+void Script::o_loadstringvar() {
+ uint16 varnum = readScript8or16bits();
+
+ varnum = _variables[varnum] - 0x31;
+ debugScript(1, false, "LOADSTRINGVAR var[0x%04X..] =", varnum);
+ do {
+ _variables[varnum++] = readScriptChar(true, true, true);
+ debugScript(1, false, " 0x%02X", _variables[varnum - 1]);
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+ debugScript(1, true, "");
+}
+
+void Script::o_chargreatjmp() {
+ uint16 varnum = readScript8or16bits();
+ uint8 val;
+ uint8 result = 0;
+
+ debugScript(1, false, "CHARGREAT-JMP: var[0x%04X..],", varnum);
+ do {
+ val = readScriptChar(true, true, true);
+
+ if (val < _variables[varnum]) {
+ result = 1;
+ }
+ varnum++;
+ debugScript(1, false, " 0x%02X", val);
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+
+ uint16 address = readScript16bits();
+ if (result) {
+ debugScript(1, true, " jumping to @0x%04X", address);
+ _currentInstruction = address;
+ } else {
+ debugScript(1, true, " not jumping");
+ }
+}
+
+void Script::o_bf7off() {
+ debugScript(1, true, "BF7OFF: bitflag 7 turned off");
+ _bitflags &= ~(1 << 7);
+}
+
+void Script::o_charlessjmp() {
+ uint16 varnum = readScript8or16bits();
+ uint8 val;
+ uint8 result = 0;
+
+ debugScript(1, false, "CHARLESS-JMP: var[0x%04X..],", varnum);
+ do {
+ val = readScriptChar(true, true, true);
+
+ if (val > _variables[varnum]) {
+ result = 1;
+ }
+ varnum++;
+ debugScript(1, false, " 0x%02X", val);
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+
+ uint16 address = readScript16bits();
+ if (result) {
+ debugScript(1, true, " jumping to @0x%04X", address);
+ _currentInstruction = address;
+ } else {
+ debugScript(1, true, " not jumping");
+ }
+}
+
+void Script::o_copyrecttobg() { // 0x37
+ uint16 left = readScript16bits();
+ uint16 top = readScript16bits();
+ uint16 right = readScript16bits();
+ uint16 bottom = readScript16bits();
+ uint16 i, width = right - left, height = bottom - top;
+ uint32 offset = 0;
+ byte *fg, *bg;
+
+ debugScript(1, true, "COPYRECT((%d,%d)->(%d,%d))", left, top, right, bottom);
+
+ fg = (byte *)_vm->_graphicsMan->_foreground.getBasePtr(left, top - 80);
+ bg = (byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - 80);
+ for (i = 0; i < height; i++) {
+ memcpy(bg + offset, fg + offset, width);
+ offset += 640;
+ }
+ _vm->_system->copyRectToScreen((byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - 80), 640, left, top, width, height);
+ _vm->_graphicsMan->change();
+}
+
+void Script::o_restorestkpnt() {
+ debugScript(1, true, "Restore stack pointer from saved (TODO)");
+}
+
+void Script::o_obscureswap() {
+ uint16 var1, var2, tmp;
+
+ debugScript(1, true, "OBSCSWAP");
+
+ // Read the first variable
+ var1 = readScriptChar(false, true, true) * 10;
+ var1 += readScriptChar(false, true, true) + 0x19;
+
+ // Read the second variable
+ var2 = readScriptChar(false, true, true) * 10;
+ var2 += readScriptChar(false, true, true) + 0x19;
+
+ // Swap the values
+ tmp = _variables[var1];
+ _variables[var1] = _variables[var2];
+ _variables[var2] = tmp;
+}
+
+void Script::o_printstring() {
+ char stringstorage[15], newchar;
+ uint8 counter = 0;
+
+ debugScript(1, true, "PRINTSTRING");
+
+ memset(stringstorage, 0, 15);
+ do {
+ newchar = readScriptChar(true, true, true) + 0x30;
+ if (newchar < 0x30 || newchar > 0x39) { // If character is invalid, chuck a space in
+ if (newchar < 0x41 || newchar > 0x7A) {
+ newchar = 0x20;
+ }
+ }
+
+ stringstorage[counter] = newchar;
+ counter++;
+ } while (!(_code[_currentInstruction - 1] & 0x80));
+
+ stringstorage[counter] = 0;
+
+ // Load the font if required
+ if (!_font) {
+ _font = new Font(_vm->_system);
+ }
+ _font->printstring(stringstorage);
+}
+
+void Script::o_hotspot_slot() {
+ uint16 slot = readScript8bits();
+ uint16 left = readScript16bits();
+ uint16 top = readScript16bits();
+ uint16 right = readScript16bits();
+ uint16 bottom = readScript16bits();
+ uint16 address = readScript16bits();
+ uint16 cursor = readScript8bits();
+
+ debugScript(1, true, "HOTSPOT-SLOT %d (%d,%d,%d,%d) @0x%04X cursor=%d (TODO)", slot, left, top, right, bottom, address, cursor);
+
+ Common::Rect rect(left, top, right, bottom);
+ if (hotspot(rect, address, cursor)) {
+ char savename[15];
+
+ Common::String filename = ConfMan.getActiveDomainName() + ".00" + ('0' + slot);
+ Common::StringList files = _vm->_system->getSavefileManager()->listSavefiles(filename.c_str());
+ if (!files.empty()) {
+ Common::InSaveFile *file = _vm->_system->getSavefileManager()->openForLoading(filename.c_str());
+ if (file) {
+ uint8 i;
+ char temp;
+
+ for (i = 0; i < 15; i++) {
+ file->read(&temp, 1);
+ savename[i] = temp + 0x30;
+ }
+
+ delete file;
+ } else {
+ strcpy(savename, "ERROR");
+ }
+ } else {
+ strcpy(savename, "E M P T Y");
+ }
+
+ // Load the font if required
+ if (!_font) {
+ _font = new Font(_vm->_system);
+ }
+ _font->printstring(savename);
+ } else {
+ Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
+ if (_hotspotCursorOldX != mousepos.x || _hotspotCursorOldY != mousepos.y ) {
+ Common::Rect topbar(640, 80);
+
+ Graphics::Surface *gamescreen;
+ gamescreen = _vm->_system->lockScreen();
+
+ gamescreen->fillRect(topbar, 0);
+
+ _vm->_system->unlockScreen();
+ _hotspotCursorOldX = mousepos.x;
+ _hotspotCursorOldY = mousepos.y;
+ }
+ }
+}
+
+void Script::o_checkvalidsaves() {
+ debugScript(1, true, "CHECKVALIDSAVES");
+
+ // Reset the array of valid saves
+ for (int i = 0; i < 10; i++) {
+ _variables[i] = 0;
+ }
+
+ // Get the list of savefiles
+ Common::String pattern = ConfMan.getActiveDomainName() + ".00?";
+ Common::StringList savefiles = _vm->_system->getSavefileManager()->listSavefiles(pattern.c_str());
+
+ // Mark the existing savefiles as valid
+ uint count = 0;
+ Common::StringList::iterator it = savefiles.begin();
+ while (it != savefiles.end()) {
+ int8 n = it->lastChar() - '0';
+ if (n >= 0 && n <= 9) {
+ // TODO: Check the contents of the file?
+ debugScript(2, true, " Found valid savegame: %s", it->c_str());
+ _variables[n] = 1;
+ count++;
+ }
+ it++;
+ }
+
+ // Save the number of valid saves
+ _variables[0x104] = count;
+ debugScript(1, true, " Found %d valid savegames", count);
+}
+
+void Script::o_resetvars() {
+ debugScript(1, true, "RESETVARS");
+ for (int i = 0; i < 0x100; i++) {
+ _variables[i] = 0;
+ }
+}
+
+void Script::o_mod() {
+ uint16 varnum = readScript8or16bits();
+ uint8 val = readScript8bits();
+
+ debugScript(1, true, "MOD var[0x%04X] %%= %d", varnum, val);
+
+ _variables[varnum] %= val;
+}
+
+void Script::o_loadscript() {
+ Common::String filename;
+ char c;
+
+ while ((c = readScript8bits())) {
+ filename += c;
+ }
+ debugScript(1, true, "LOADSCRIPT %s", filename.c_str());
+
+ // Just 1 level of sub-scripts are allowed
+ if (_savedCode) {
+ error("Tried to load a level 2 sub-script");
+ }
+
+ // Save the current code
+ _savedCode = _code;
+ _savedInstruction = _currentInstruction;
+
+ // Save the filename of the current script
+ _savedScriptFile = _scriptFile;
+
+ // Load the sub-script
+ if (!loadScript(filename)) {
+ error("Couldn't load sub-script");
+ }
+
+ // Save the current stack top
+ _savedStacktop = _stacktop;
+
+ // Save the variables
+ memcpy(_savedVariables, _variables + 0x107, 0x180);
+}
+
+void Script::o_setvideoorigin() {
+ // Read the two offset arguments
+ int16 origX = readScript16bits();
+ int16 origY = readScript16bits();
+
+ // Set bitflag 7
+ _bitflags |= 1 << 7;
+
+ debugScript(1, true, "SetVideoOrigin(0x%04X,0x%04X) (%d, %d)", origX, origY, origX, origY);
+ _vm->_videoPlayer->setOrigin(origX, origY);
+}
+
+void Script::o_sub() {
+ uint16 varnum1 = readScript8or16bits();
+ uint16 varnum2 = readScript16bits();
+
+ debugScript(1, true, "SUB var[0x%04X] -= var[0x%04X]", varnum1, varnum2);
+
+ _variables[varnum1] -= _variables[varnum2];
+}
+
+void Script::o_othello() {
+ uint16 arg = readScript8bits();
+ byte *scriptBoard = &_variables[0x19];
+ byte board[7][7];
+
+ debugScript(1, true, "OTHELLO var[0x%02X]", arg);
+
+ // Arguments used by the original implementation: (2, arg, scriptBoard)
+ for (int y = 0; y < 7; y++) {
+ for (int x = 0; x < 7; x++) {
+ board[x][y] = 0;
+ if (*scriptBoard == 0x32) board[x][y] = 1;
+ if (*scriptBoard == 0x42) board[x][y] = 2;
+ scriptBoard++;
+ debugScript(1, false, "%d", board[x][y]);
+ }
+ debugScript(1, false, "\n");
+ }
+
+ // Set the movement origin
+ _variables[0] = 6; // y
+ _variables[1] = 0; // x
+ // Set the movement destination
+ _variables[2] = 6;
+ _variables[3] = 1;
+}
+
+void Script::o_returnscript() {
+ uint8 val = readScript8bits();
+
+ debugScript(1, true, "RETURNSCRIPT @0x%02X", val);
+
+ // Are we returning from a sub-script?
+ if (!_savedCode) {
+ error("Tried to return from the main script");
+ }
+
+ // Set the return value
+ _variables[0x102] = val;
+
+ // Restore the code
+ delete[] _code;
+ _code = _savedCode;
+ _savedCode = NULL;
+ _currentInstruction = _savedInstruction;
+
+ // Restore the stack
+ _stacktop = _savedStacktop;
+
+ // Restore the variables
+ memcpy(_variables + 0x107, _savedVariables, 0x180);
+
+ // Restore the filename of the script
+ _scriptFile = _savedScriptFile;
+
+ //TODO: reset script flags and previous video's flag1?
+ _vm->_videoPlayer->setOrigin(0, 0);
+}
+
+void Script::o_sethotspotright() {
+ uint16 address = readScript16bits();
+
+ debugScript(1, true, "SETHOTSPOTRIGHT @0x%04X", address);
+
+ _hotspotRightAction = address;
+}
+
+void Script::o_sethotspotleft() {
+ uint16 address = readScript16bits();
+
+ debugScript(1, true, "SETHOTSPOTLEFT @0x%04X", address);
+
+ _hotspotLeftAction = address;
+}
+
+void Script::o_getcd() {
+ debugScript(1, true, "GETCD");
+
+ // By default set it to no CD available
+ int8 cd = -1;
+
+ // Try to open one file from each CD
+ Common::File cdfile;
+ if (cdfile.open("b.gjd")) {
+ cdfile.close();
+ cd = 1;
+ }
+ if (cdfile.open("at.gjd")) {
+ cdfile.close();
+ if (cd == 1) {
+ // Both CDs are available
+ cd = 0;
+ } else {
+ cd = 2;
+ }
+ }
+
+ _variables[0x106] = cd;
+}
+
+void Script::o_opcode4D() {
+ // TODO: play alternative vie logo, then playcd
+ uint8 val = readScript8bits();
+
+ debugScript(1, true, "PLAYCD? %d", val);
+
+ if (val == 2) {
+ AudioCD.play(1, 1, 0, 0);
+ }
+
+}
+
+void Script::o_hotspot_outrect() {
+ uint16 left = readScript16bits();
+ uint16 top = readScript16bits();
+ uint16 right = readScript16bits();
+ uint16 bottom = readScript16bits();
+ uint16 address = readScript16bits();
+
+ debugScript(1, true, "HOTSPOT-OUTRECT(%d,%d,%d,%d) @0x%04X (TODO)", left, top, right, bottom, address);
+
+ // Test if the current mouse position is outside the specified rectangle
+ Common::Rect rect(left, top, right, bottom);
+ Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
+ bool contained = rect.contains(mousepos);
+
+ if (!contained) {
+ error("hotspot-outrect unimplemented!");
+ // TODO: what to do with address?
+ }
+}
+
+void Script::o_stub56() {
+ uint32 val1 = readScript32bits();
+ uint8 val2 = readScript8bits();
+ uint8 val3 = readScript8bits();
+
+ debugScript(1, true, "STUB56: 0x%08X 0x%02X 0x%02X", val1, val2, val3);
+}
+
+void Script::o_stub59() {
+ uint16 val1 = readScript8or16bits();
+ uint8 val2 = readScript8bits();
+
+ debugScript(1, true, "STUB59: 0x%04X 0x%02X", val1, val2);
+}
+
+Script::OpcodeFunc Script::_opcodes[NUM_OPCODES] = {
+ &Script::o_nop, // 0x00
+ &Script::o_nop,
+ &Script::o_playsong,
+ &Script::o_bf9on,
+ &Script::o_palfadeout, // 0x04
+ &Script::o_bf8on,
+ &Script::o_bf6on,
+ &Script::o_bf7on,
+ &Script::o_setbackgroundsong, // 0x08
+ &Script::o_videofromref,
+ &Script::o_bf5on,
+ &Script::o_inputloopstart,
+ &Script::o_keyboardaction, // 0x0C
+ &Script::o_hotspot_rect,
+ &Script::o_hotspot_left,
+ &Script::o_hotspot_right,
+ &Script::o_hotspot_center, // 0x10
+ &Script::o_hotspot_center,
+ &Script::o_hotspot_current,
+ &Script::o_inputloopend,
+ &Script::o_random, // 0x14
+ &Script::o_jmp,
+ &Script::o_loadstring,
+ &Script::o_ret,
+ &Script::o_call, // 0x18
+ &Script::o_sleep,
+ &Script::o_strcmpnejmp,
+ &Script::o_xor_obfuscate,
+ &Script::o_vdxtransition, // 0x1C
+ &Script::o_swap,
+ &Script::o_nop8,
+ &Script::o_inc,
+ &Script::o_dec, // 0x20
+ &Script::o_strcmpnejmp_var,
+ &Script::o_copybgtofg,
+ &Script::o_strcmpeqjmp,
+ &Script::o_mov, // 0x24
+ &Script::o_add,
+ &Script::o_videofromstring1, // Reads a string and then does stuff: used by book in library
+ &Script::o_videofromstring2, // play vdx file from string, after setting 1 (and 2 if firstbit)
+ &Script::o_nop16, // 0x28
+ &Script::o_stopmidi,
+ &Script::o_endscript,
+ &Script::o_nop,
+ &Script::o_sethotspottop, // 0x2C
+ &Script::o_sethotspotbottom,
+ &Script::o_loadgame,
+ &Script::o_savegame,
+ &Script::o_hotspotbottom_4, // 0x30
+ &Script::o_midivolume,
+ &Script::o_jne,
+ &Script::o_loadstringvar,
+ &Script::o_chargreatjmp, // 0x34
+ &Script::o_bf7off,
+ &Script::o_charlessjmp,
+ &Script::o_copyrecttobg,
+ &Script::o_restorestkpnt, // 0x38
+ &Script::o_obscureswap,
+ &Script::o_printstring,
+ &Script::o_hotspot_slot,
+ &Script::o_checkvalidsaves, // 0x3C
+ &Script::o_resetvars,
+ &Script::o_mod,
+ &Script::o_loadscript,
+ &Script::o_setvideoorigin, // 0x40
+ &Script::o_sub,
+ &Script::o_othello,
+ &Script::o_returnscript,
+ &Script::o_sethotspotright, // 0x44
+ &Script::o_sethotspotleft,
+ &Script::o_nop,
+ &Script::o_nop,
+ &Script::o_nop8, // 0x48
+ &Script::o_nop,
+ &Script::o_nop16,
+ &Script::o_nop8,
+ &Script::o_getcd, // 0x4C
+ &Script::o_opcode4D,
+ &Script::o_nop16,
+ &Script::o_nop16,
+ &Script::o_nop16, // 0x50
+ &Script::o_nop16,
+ //&Script::o_nop8,
+ &Script::o_invalid, // Do loads with game area, maybe draw dirty areas?
+ &Script::o_hotspot_outrect,
+ &Script::o_nop, // 0x54
+ &Script::o_nop16,
+ &Script::o_stub56,
+ //&Script::o_nop32,
+ &Script::o_invalid, // completely unimplemented, plays vdx in some way
+ //&Script::o_nop, // 0x58
+ &Script::o_invalid, // 0x58 // like above, but plays from string not ref
+ &Script::o_stub59
+};
+
+} // End of Groovie namespace