aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base/plugins.cpp3
-rwxr-xr-xconfigure1
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/groovie/cursor.cpp314
-rw-r--r--engines/groovie/cursor.h96
-rw-r--r--engines/groovie/debug.cpp145
-rw-r--r--engines/groovie/debug.h63
-rw-r--r--engines/groovie/detection.cpp265
-rw-r--r--engines/groovie/font.cpp128
-rw-r--r--engines/groovie/font.h52
-rw-r--r--engines/groovie/graphics.cpp161
-rw-r--r--engines/groovie/graphics.h65
-rw-r--r--engines/groovie/groovie.cpp272
-rw-r--r--engines/groovie/groovie.h103
-rw-r--r--engines/groovie/lzss.cpp99
-rw-r--r--engines/groovie/lzss.h42
-rw-r--r--engines/groovie/module.mk24
-rw-r--r--engines/groovie/music.cpp211
-rw-r--r--engines/groovie/music.h78
-rw-r--r--engines/groovie/player.cpp98
-rw-r--r--engines/groovie/player.h68
-rw-r--r--engines/groovie/resource.cpp240
-rw-r--r--engines/groovie/resource.h71
-rw-r--r--engines/groovie/roq.cpp403
-rw-r--r--engines/groovie/roq.h69
-rw-r--r--engines/groovie/script.cpp1566
-rw-r--r--engines/groovie/script.h211
-rw-r--r--engines/groovie/vdx.cpp522
-rw-r--r--engines/groovie/vdx.h81
29 files changed, 5456 insertions, 0 deletions
diff --git a/base/plugins.cpp b/base/plugins.cpp
index cd91bed292..cea8232e59 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -108,6 +108,9 @@ public:
#if PLUGIN_ENABLED_STATIC(GOB)
LINK_PLUGIN(GOB)
#endif
+ #if PLUGIN_ENABLED_STATIC(GROOVIE)
+ LINK_PLUGIN(GROOVIE)
+ #endif
#if PLUGIN_ENABLED_STATIC(IGOR)
LINK_PLUGIN(IGOR)
#endif
diff --git a/configure b/configure
index 8a08e89138..be6219a459 100755
--- a/configure
+++ b/configure
@@ -89,6 +89,7 @@ add_engine cine "Cinematique evo 1" yes
add_engine cruise "Cinematique evo 2" no
add_engine drascula "Drascula: The Vampire Strikes Back" yes
add_engine gob "Gobli*ns" yes
+add_engine groovie "Groovie" no
add_engine igor "Igor: Objective Uikokahonia" no
add_engine kyra "Legend of Kyrandia" yes
add_engine lure "Lure of the Temptress" yes
diff --git a/engines/engines.mk b/engines/engines.mk
index 5115ac69aa..4b2780a903 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -42,6 +42,11 @@ DEFINES += -DENABLE_GOB=$(ENABLE_GOB)
MODULES += engines/gob
endif
+ifdef ENABLE_GROOVIE
+DEFINES += -DENABLE_GROOVIE=$(ENABLE_GROOVIE)
+MODULES += engines/groovie
+endif
+
ifdef ENABLE_IGOR
DEFINES += -DENABLE_IGOR=$(ENABLE_IGOR)
MODULES += engines/igor
diff --git a/engines/groovie/cursor.cpp b/engines/groovie/cursor.cpp
new file mode 100644
index 0000000000..3dd1f9bc6e
--- /dev/null
+++ b/engines/groovie/cursor.cpp
@@ -0,0 +1,314 @@
+/* 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/cursor.h"
+#include "groovie/groovie.h"
+
+namespace Groovie {
+
+// Cursor Manager
+
+CursorMan::CursorMan(OSystem *system) :
+ _syst(system), _lastTime(0), _cursor(NULL) {
+}
+
+CursorMan::~CursorMan() {
+ // Delete the cursors
+ for (uint cursor = 0; cursor < _cursors.size(); cursor++) {
+ delete _cursors[cursor];
+ }
+}
+
+uint8 CursorMan::getStyle() {
+ return _current;
+}
+
+void CursorMan::setStyle(uint8 newStyle) {
+ // Reset the animation
+ _lastFrame = 254;
+ _lastTime = 1;
+
+ // Save the new cursor
+ _current = newStyle;
+ _cursor = _cursors[newStyle];
+
+ // Show the first frame
+ _cursor->enable();
+ animate();
+}
+
+void CursorMan::animate() {
+ if (_lastTime) {
+ int newTime = _syst->getMillis();
+ if (_lastTime - newTime >= 75) {
+ _lastFrame++;
+ _lastFrame %= _cursor->getFrames();
+ _cursor->showFrame(_lastFrame);
+ _lastTime = _syst->getMillis();
+ }
+ }
+}
+
+
+// t7g Cursor
+
+class Cursor_t7g : public Cursor {
+public:
+ Cursor_t7g(OSystem *system, uint8 *img, uint8 *pal);
+
+ void enable();
+ void showFrame(uint16 frame);
+
+private:
+ OSystem *_syst;
+ byte *_img;
+ byte *_pal;
+};
+
+Cursor_t7g::Cursor_t7g(OSystem *system, uint8 *img, uint8 *pal) :
+ _syst(system), _pal(pal) {
+
+ _width = img[0];
+ _height = img[1];
+ _numFrames = img[2];
+ uint8 elinor1 = img[3];
+ uint8 elinor2 = img[4];
+
+ _img = img + 5;
+
+ debugC(1, kGroovieDebugCursor | kGroovieDebugAll, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames);
+ debugC(1, kGroovieDebugCursor | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::Cursor: elinor: 0x%02X (%d), 0x%02X (%d)", elinor1, elinor1, elinor2, elinor2);
+}
+
+void Cursor_t7g::enable() {
+ // Apply the palette
+ _syst->setCursorPalette(_pal, 0, 32);
+}
+
+void Cursor_t7g::showFrame(uint16 frame) {
+ // Set the mouse cursor
+ int offset = _width * _height * frame;
+ _syst->setMouseCursor((const byte *)_img + offset, _width, _height, _width >> 1, _height >> 1, 0);
+}
+
+
+// t7g Cursor Manager
+
+#define NUM_IMGS 9
+static const uint16 cursorDataOffsets[NUM_IMGS] = {
+0x0000, 0x182f, 0x3b6d, 0x50cc, 0x6e79, 0x825d, 0x96d7, 0xa455, 0xa776
+};
+
+#define NUM_PALS 7
+//Pals: 0xb794, 0xb7f4, 0xb854, 0xb8b4, 0xb914, 0xb974, 0xb9d4
+
+#define NUM_STYLES 11
+// pyramid is cursor 8, eyes are 9 & 10
+const uint CursorMan_t7g::_cursorImg[NUM_STYLES] = {3, 5, 4, 3, 1, 0, 2, 6, 7, 8, 8};
+const uint CursorMan_t7g::_cursorPal[NUM_STYLES] = {0, 0, 0, 0, 2, 0, 1, 3, 5, 4, 6};
+
+CursorMan_t7g::CursorMan_t7g(OSystem *system) :
+ CursorMan(system) {
+
+ // Open the cursors file
+ Common::File robgjd;
+ if (!robgjd.open("rob.gjd")) {
+ error("Groovie::Cursor: Couldn't open rob.gjd");
+ return;
+ }
+
+ // Load the images
+ for (uint imgnum = 0; imgnum < NUM_IMGS; imgnum++) {
+ robgjd.seek(cursorDataOffsets[imgnum]);
+ _images.push_back(loadImage(robgjd));
+ }
+
+ // Load the palettes
+ robgjd.seek(-0x60 * NUM_PALS, SEEK_END);
+ for (uint palnum = 0; palnum < NUM_PALS; palnum++) {
+ _palettes.push_back(loadPalette(robgjd));
+ }
+
+ // Build the cursors
+ for (uint cursor = 0; cursor < NUM_STYLES; cursor++) {
+ Cursor *s = new Cursor_t7g(_syst, _images[_cursorImg[cursor]], _palettes[_cursorPal[cursor]]);
+ _cursors.push_back(s);
+ }
+
+ robgjd.close();
+}
+
+CursorMan_t7g::~CursorMan_t7g() {
+ // Delete the images
+ for (uint img = 0; img < _images.size(); img++) {
+ delete[] _images[img];
+ }
+
+ // Delete the palettes
+ for (uint pal = 0; pal < _palettes.size(); pal++) {
+ delete[] _palettes[pal];
+ }
+}
+
+byte *CursorMan_t7g::loadImage(Common::File &file) {
+ uint16 decompbytes = 0, offset, i, length;
+ uint8 flagbyte, lengthmask = 0x0F, offsetlen, var_8;
+ byte *cursorStorage = new byte[65536];
+ uint8 *runningcursor = cursorStorage;
+
+ bool finished = false;
+ while (!(finished || file.eos())) {
+ flagbyte = file.readByte();
+ for (i = 1; i <= 8; i++) {
+ if (!file.eos()) {
+ if (flagbyte & 1) {
+ *(runningcursor++) = file.readByte();
+ decompbytes++;
+ } else {
+ var_8 = file.readByte();
+ offsetlen = file.readByte();
+ if (var_8 == 0 && offsetlen == 0) {
+ finished = true;
+ break;
+ }
+ length = (offsetlen & lengthmask) + 3;
+ offsetlen >>= 4;
+ offset = (offsetlen << 8) + var_8;
+ decompbytes += length;
+
+ for (; length > 0; length--, runningcursor++) {
+ *(runningcursor) = *(runningcursor - offset);
+ }
+ }
+ flagbyte = flagbyte >> 1;
+ }
+ }
+ }
+
+ return cursorStorage;
+}
+
+byte *CursorMan_t7g::loadPalette(Common::File &file) {
+ byte *palette = new byte[4 * 32];
+ for (uint8 colournum = 0; colournum < 32; colournum++) {
+ palette[colournum * 4 + 0] = file.readByte();
+ palette[colournum * 4 + 1] = file.readByte();
+ palette[colournum * 4 + 2] = file.readByte();
+ palette[colournum * 4 + 3] = 0;
+ }
+ return palette;
+}
+
+
+// v2 Cursor
+
+class Cursor_v2 : public Cursor {
+public:
+ Cursor_v2(Common::File &file);
+
+ void enable();
+ void showFrame(uint16 frame);
+
+private:
+ //byte *_data;
+};
+
+Cursor_v2::Cursor_v2(Common::File &file) {
+ _numFrames = file.readUint16LE();
+ _width = file.readUint16LE();
+ _height = file.readUint16LE();
+
+ debugC(1, kGroovieDebugCursor | kGroovieDebugAll, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames);
+
+ uint16 tmp16 = file.readUint16LE();
+ debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "hotspot x?: %d\n", tmp16);
+ tmp16 = file.readUint16LE();
+ debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "hotspot y?: %d\n", tmp16);
+ int loop2count = file.readUint16LE();
+ debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop2count?: %d\n", loop2count);
+ for (int l = 0; l < loop2count; l++) {
+ tmp16 = file.readUint16LE();
+ debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop2a: %d\n", tmp16);
+ tmp16 = file.readUint16LE();
+ debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop2b: %d\n", tmp16);
+ }
+
+ file.seek(0x20 * 3, SEEK_CUR);
+
+ for (int f = 0; f < _numFrames; f++) {
+ uint32 tmp32 = file.readUint32LE();
+ debugC(5, kGroovieDebugCursor | kGroovieDebugAll, "loop3: %d\n", tmp32);
+
+ //file.seek(tmp32, SEEK_CUR);
+ byte *data = new byte[tmp32];
+ file.read(data, tmp32);
+ //Common::hexdump(data, tmp32);
+ delete[] data;
+ }
+}
+
+void Cursor_v2::enable() {
+}
+
+void Cursor_v2::showFrame(uint16 frame) {
+}
+
+
+// v2 Cursor Manager
+
+CursorMan_v2::CursorMan_v2(OSystem *system) :
+ CursorMan(system) {
+
+ // Open the icons file
+ Common::File iconsFile;
+ if (!iconsFile.open("icons.ph")) {
+ error("Groovie::Cursor: Couldn't open icons.ph");
+ return;
+ }
+
+ // Verify the signature
+ uint32 tmp32 = iconsFile.readUint32LE();
+ uint16 tmp16 = iconsFile.readUint16LE();
+ if (tmp32 != 0x6e6f6369 || tmp16 != 1) {
+ error("Groovie::Cursor: icons.ph signature failed: %04X %d", tmp32, tmp16);
+ return;
+ }
+
+ // Read the number of icons
+ uint16 nicons = iconsFile.readUint16LE();
+
+ // Read the icons
+ for (int i = 0; i < nicons; i++) {
+ Cursor *s = new Cursor_v2(iconsFile);
+ _cursors.push_back(s);
+ }
+
+ iconsFile.close();
+}
+
+CursorMan_v2::~CursorMan_v2() {
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/cursor.h b/engines/groovie/cursor.h
new file mode 100644
index 0000000000..19694b2a0d
--- /dev/null
+++ b/engines/groovie/cursor.h
@@ -0,0 +1,96 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_CURSOR_H
+#define GROOVIE_CURSOR_H
+
+#include "common/system.h"
+#include "common/file.h"
+
+namespace Groovie {
+
+class Cursor {
+public:
+ virtual ~Cursor() {}
+ uint16 getFrames() { return _numFrames; }
+ virtual void enable() = 0;
+ virtual void showFrame(uint16 frame) = 0;
+
+protected:
+ uint16 _width;
+ uint16 _height;
+ uint16 _numFrames;
+};
+
+class CursorMan {
+public:
+ CursorMan(OSystem *system);
+ virtual ~CursorMan();
+
+ virtual void animate();
+ virtual void setStyle(uint8 newStyle);
+ virtual uint8 getStyle();
+
+protected:
+ OSystem *_syst;
+
+ // Animation variables
+ uint8 _lastFrame;
+ uint32 _lastTime;
+
+ // Styles
+ Common::Array<Cursor *> _cursors;
+ uint8 _current;
+ Cursor *_cursor;
+};
+
+class CursorMan_t7g : public CursorMan {
+public:
+ CursorMan_t7g(OSystem *system);
+ ~CursorMan_t7g();
+
+private:
+ // Styles data
+ static const uint _cursorImg[];
+ static const uint _cursorPal[];
+
+ // Cursors data
+ Common::Array<byte *> _images;
+ Common::Array<byte *> _palettes;
+
+ // Loading functions
+ byte *loadImage(Common::File &file);
+ byte *loadPalette(Common::File &file);
+};
+
+class CursorMan_v2 : public CursorMan {
+public:
+ CursorMan_v2(OSystem *system);
+ ~CursorMan_v2();
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_CURSOR_H
diff --git a/engines/groovie/debug.cpp b/engines/groovie/debug.cpp
new file mode 100644
index 0000000000..8968400fef
--- /dev/null
+++ b/engines/groovie/debug.cpp
@@ -0,0 +1,145 @@
+/* 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/script.h"
+#include "groovie/groovie.h"
+
+namespace Groovie {
+
+Debugger::Debugger(GroovieEngine *vm) :
+ _vm (vm), _script(&_vm->_script), _syst(_vm->_system) {
+
+ // Register the debugger comands
+ DCmd_Register("step", WRAP_METHOD(Debugger, cmd_step));
+ DCmd_Register("go", WRAP_METHOD(Debugger, cmd_go));
+ DCmd_Register("pc", WRAP_METHOD(Debugger, cmd_pc));
+ DCmd_Register("fg", WRAP_METHOD(Debugger, cmd_fg));
+ DCmd_Register("bg", WRAP_METHOD(Debugger, cmd_bg));
+ DCmd_Register("mem", WRAP_METHOD(Debugger, cmd_mem));
+ DCmd_Register("load", WRAP_METHOD(Debugger, cmd_loadgame));
+ DCmd_Register("save", WRAP_METHOD(Debugger, cmd_savegame));
+ DCmd_Register("playref", WRAP_METHOD(Debugger, cmd_playref));
+ DCmd_Register("dumppal", WRAP_METHOD(Debugger, cmd_dumppal));
+}
+
+Debugger::~Debugger() {
+ Common::clearAllSpecialDebugLevels();
+}
+
+int Debugger::getNumber(const char *arg) {
+ return strtol(arg, (char **)NULL, 0);
+}
+
+bool Debugger::cmd_step(int argc, const char **argv) {
+ _script->step();
+ return true;
+}
+
+bool Debugger::cmd_go(int argc, const char **argv) {
+ _script->step();
+ return false;
+}
+
+bool Debugger::cmd_fg(int argc, const char **argv) {
+ _vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_foreground);
+ return false;
+}
+
+bool Debugger::cmd_bg(int argc, const char **argv) {
+ _vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_background);
+ return false;
+}
+
+bool Debugger::cmd_pc(int argc, const char **argv) {
+ if (argc == 2) {
+ int val = getNumber(argv[1]);
+ _script->_currentInstruction = val;
+ }
+ DebugPrintf("pc = 0x%04X\n", _script->_currentInstruction);
+ return true;
+}
+
+bool Debugger::cmd_mem(int argc, const char **argv) {
+ if (argc >= 2) {
+ int pos = getNumber(argv[1]);
+ uint8 val;
+ if (argc >= 3) {
+ // Set
+ val = getNumber(argv[2]);
+ _script->_variables[pos] = val;
+ } else {
+ // Get
+ val = _script->_variables[pos];
+ }
+ DebugPrintf("mem[0x%04X] = 0x%02X\n", pos, val);
+ } else {
+ DebugPrintf("Syntax: mem <addr> [<val>]\n");
+ }
+ return true;
+}
+
+bool Debugger::cmd_loadgame(int argc, const char **argv) {
+ if (argc == 2) {
+ int slot = getNumber(argv[1]);
+ _script->loadgame(slot);
+ } else {
+ DebugPrintf("Syntax: load <slot>\n");
+ }
+ return true;
+}
+
+bool Debugger::cmd_savegame(int argc, const char **argv) {
+ if (argc == 2) {
+ int slot = getNumber(argv[1]);
+ _script->savegame(slot);
+ } else {
+ DebugPrintf("Syntax: save <slot>\n");
+ }
+ return true;
+}
+
+bool Debugger::cmd_playref(int argc, const char **argv) {
+ if (argc == 2) {
+ int ref = getNumber(argv[1]);
+ _script->playvideofromref(ref);
+ } else {
+ DebugPrintf("Syntax: playref <videorefnum>\n");
+ }
+ return true;
+}
+
+bool Debugger::cmd_dumppal(int argc, const char **argv) {
+ uint16 i;
+ byte palettedump[256 * 4];
+ _syst->grabPalette(palettedump, 0, 256);
+
+ for (i = 0; i < 256; i++) {
+ DebugPrintf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);
+ }
+ return true;
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/debug.h b/engines/groovie/debug.h
new file mode 100644
index 0000000000..dadba9482c
--- /dev/null
+++ b/engines/groovie/debug.h
@@ -0,0 +1,63 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_DEBUG_H
+#define GROOVIE_DEBUG_H
+
+#include "gui/debugger.h"
+#include "engines/engine.h"
+
+namespace Groovie {
+
+class Script;
+class GroovieEngine;
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger(GroovieEngine *vm);
+ ~Debugger();
+
+private:
+ GroovieEngine *_vm;
+ Script *_script;
+ OSystem *_syst;
+
+ int getNumber(const char *arg);
+
+ bool cmd_step(int argc, const char **argv);
+ bool cmd_go(int argc, const char **argv);
+ bool cmd_pc(int argc, const char **argv);
+ bool cmd_bg(int argc, const char **argv);
+ bool cmd_fg(int argc, const char **argv);
+ bool cmd_mem(int argc, const char **argv);
+ bool cmd_loadgame(int argc, const char **argv);
+ bool cmd_savegame(int argc, const char **argv);
+ bool cmd_playref(int argc, const char **argv);
+ bool cmd_dumppal(int argc, const char **argv);
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_DEBUG_H
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
new file mode 100644
index 0000000000..80023924dc
--- /dev/null
+++ b/engines/groovie/detection.cpp
@@ -0,0 +1,265 @@
+/* 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 "common/savefile.h"
+
+#include "groovie/groovie.h"
+
+namespace Groovie {
+
+//#define GROOVIE_EXPERIMENTAL
+
+static const PlainGameDescriptor groovieGames[] = {
+ // Games
+ {"t7g", "The 7th Guest"},
+
+#ifdef GROOVIE_EXPERIMENTAL
+ {"11h", "The 11th Hour: The sequel to The 7th Guest"},
+ {"clandestiny", "Clandestiny"},
+ {"unclehenry", "Uncle Henry's Playhouse"},
+ {"tlc", "Tender Loving Care"},
+
+ // Extras
+ {"making11h", "The Making of The 11th Hour"},
+ {"clantrailer", "Clandestiny Trailer"},
+#endif
+
+ // Unknown
+ {"groovie", "Groovie engine game"},
+ {0, 0}
+};
+
+static const GroovieGameDescription gameDescriptions[] = {
+
+ // The 7th Guest DOS English
+ {
+ {
+ "t7g", "",
+ AD_ENTRY1s("script.grv", "d1b8033b40aa67c076039881eccce90d", 16659),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieT7G, 0
+ },
+
+ // The 7th Guest Mac English
+ {
+ {
+ "t7g", "",
+ AD_ENTRY1s("script.grv", "6e30b54b1f3bc2262cdcf7961db2ae67", 17191),
+ Common::EN_ANY, Common::kPlatformMacintosh, Common::ADGF_NO_FLAGS
+ },
+ kGroovieT7G, 0
+ },
+
+#ifdef GROOVIE_EXPERIMENTAL
+ // The 11th Hour DOS English
+ {
+ {
+ "11h", "",
+ AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieV2, 1
+ },
+
+ // The Making of The 11th Hour DOS English
+ {
+ {
+ "making11h", "",
+ AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieV2, 2
+ },
+
+ // Clandestiny Trailer DOS English
+ {
+ {
+ "clantrailer", "",
+ AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieV2, 3
+ },
+
+ // Clandestiny DOS English
+ {
+ {
+ "clandestiny", "",
+ AD_ENTRY1s("disk.1", "f79fc1515174540fef6a34132efc4c53", 76),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieV2, 1
+ },
+
+ // Uncle Henry's Playhouse PC English
+ {
+ {
+ "unclehenry", "",
+ AD_ENTRY1s("disk.1", "0e1b1d3cecc4fc7efa62a968844d1f7a", 72),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieV2, 1
+ },
+
+ // Tender Loving Care PC English
+ {
+ {
+ "tlc", "",
+ AD_ENTRY1s("disk.1", "32a1afa68478f1f9d2b25eeea427f2e3", 84),
+ Common::EN_ANY, Common::kPlatformPC, Common::ADGF_NO_FLAGS
+ },
+ kGroovieV2, 1
+ },
+#endif
+
+ {AD_TABLE_END_MARKER, kGroovieT7G, 0}
+};
+
+static const Common::ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)gameDescriptions,
+ // Size of that superset structure
+ sizeof(GroovieGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ groovieGames,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ 0,
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0
+};
+
+
+class GroovieMetaEngine : public Common::AdvancedMetaEngine {
+public:
+ GroovieMetaEngine() : Common::AdvancedMetaEngine(detectionParams) {}
+
+ const char *getName() const {
+ return "Groovie Engine";
+ }
+
+ const char *getCopyright() const {
+ return "Groovie Engine (C) 1990-1996 Trilobyte";
+ }
+
+ bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const;
+
+ bool hasFeature(MetaEngineFeature f) const;
+ SaveStateList listSaves(const char *target) const;
+ void removeSaveState(const char *target, int slot) const;
+};
+
+bool GroovieMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *gd) const {
+ if (gd) {
+ *engine = new GroovieEngine(syst, (GroovieGameDescription *)gd);
+ }
+ return gd != 0;
+}
+
+bool GroovieMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave);
+ //(f == kSavesSupportCreationDate)
+}
+
+SaveStateList GroovieMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *sfm = g_system->getSavefileManager();
+ SaveStateList list;
+
+ // Get the list of savefiles
+ Common::String pattern = Common::String(target) + ".00?";
+ Common::StringList savefiles = sfm->listSavefiles(pattern.c_str());
+
+ // Sort the list of filenames
+ sort(savefiles.begin(), savefiles.end());
+
+ // Fill the information for the existing savegames
+ Common::StringList::iterator it = savefiles.begin();
+ while (it != savefiles.end()) {
+ int slot = it->lastChar() - '0';
+ if (slot >= 0 && slot <= 9) {
+ Common::InSaveFile *file = sfm->openForLoading(it->c_str());
+
+ // Read the savegame description
+ Common::String description;
+ unsigned char c = 1;
+ for (int i = 0; (c != 0) && (i < 15); i++) {
+ c = file->readByte();
+ switch (c) {
+ case 0:
+ break;
+ case 16: // @
+ c = ' ';
+ break;
+ case 244: // $
+ c = 0;
+ break;
+ default:
+ c += 0x30;
+ }
+ if (c != 0) {
+ description += c;
+ }
+ }
+ delete file;
+
+ list.push_back(SaveStateDescriptor(slot, description));
+ }
+ it++;
+ }
+
+ return list;
+}
+
+void GroovieMetaEngine::removeSaveState(const char *target, int slot) const {
+ if (slot < 0 || slot > 9) {
+ // Invalid slot, do nothing
+ return;
+ }
+
+ char extension[6];
+ snprintf(extension, sizeof(extension), ".00%01d", slot);
+
+ Common::String filename = target;
+ filename += extension;
+
+ g_system->getSavefileManager()->removeSavefile(filename.c_str());
+}
+
+} // End of namespace Groovie
+
+#if PLUGIN_ENABLED_DYNAMIC(GROOVIE)
+ REGISTER_PLUGIN_DYNAMIC(GROOVIE, PLUGIN_TYPE_ENGINE, Groovie::GroovieMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(GROOVIE, PLUGIN_TYPE_ENGINE, Groovie::GroovieMetaEngine);
+#endif
diff --git a/engines/groovie/font.cpp b/engines/groovie/font.cpp
new file mode 100644
index 0000000000..cabb7563a1
--- /dev/null
+++ b/engines/groovie/font.cpp
@@ -0,0 +1,128 @@
+/* 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 "common/file.h"
+#include "graphics/surface.h"
+
+#include "groovie/font.h"
+
+namespace Groovie {
+
+Font::Font(OSystem *syst) :
+ _syst(syst), _sphinxfnt(NULL) {
+
+ Common::File fontfile;
+ if (!fontfile.open("sphinx.fnt")) {
+ error("Groovie::Font: Couldn't open sphinx.fnt");
+ }
+ uint16 fontfilesize = fontfile.size();
+ _sphinxfnt = fontfile.readStream(fontfilesize);
+ fontfile.close();
+}
+
+Font::~Font() {
+ delete _sphinxfnt;
+}
+
+void Font::printstring(char *messagein) {
+ uint16 totalwidth = 0, currxoffset, i;
+
+ char message[15];
+ memset(message, 0, 15);
+
+ // Clear the top bar
+ Common::Rect topbar(640, 80);
+ Graphics::Surface *gamescreen;
+ gamescreen = _syst->lockScreen();
+ gamescreen->fillRect(topbar, 0);
+ _syst->unlockScreen();
+
+ for (i = 0; i < 14; i++) {
+ char chartocopy = messagein[i];
+ if (chartocopy <= 0x00 || chartocopy == 0x24) {
+ break;
+ }
+ message[i] = chartocopy;
+ }
+ Common::rtrim(message);
+ for (i = 0; i < strlen(message); i++) {
+ totalwidth += letterwidth(message[i]);
+ }
+ currxoffset = (640 - totalwidth) / 2;
+ char *currpos = message;
+ while (*(currpos) != 0) {
+ currxoffset += printletter(*(currpos++), currxoffset);
+ }
+}
+
+uint16 Font::letteroffset(char letter) {
+ uint16 offset;
+ offset = letter;
+ _sphinxfnt->seek(offset);
+ offset = _sphinxfnt->readByte() * 2 + 128;
+ _sphinxfnt->seek(offset);
+ offset = _sphinxfnt->readUint16LE();
+ return offset;
+}
+
+uint8 Font::letterwidth(char letter) {
+ uint16 offset = letteroffset(letter);
+ _sphinxfnt->seek(offset);
+ return _sphinxfnt->readByte();
+}
+
+uint8 Font::letterheight(char letter) {
+ uint16 offset, width, julia, data, counter = 0;
+ offset = letteroffset(letter);
+ _sphinxfnt->seek(offset);
+ width = _sphinxfnt->readByte();
+ julia = _sphinxfnt->readByte();
+ data = _sphinxfnt->readByte();
+ while (data != 0xFF) {
+ data = _sphinxfnt->readByte();
+ counter++;
+ }
+ if (counter % width != 0) assert("font file corrupt");
+ return counter / width;
+}
+
+
+uint8 Font::printletter(char letter, uint16 xoffset) {
+ uint16 offset, width, height, julia;
+ offset = letteroffset(letter);
+ height = letterheight(letter);
+ _sphinxfnt->seek(offset);
+ width = _sphinxfnt->readByte();
+ julia = _sphinxfnt->readByte();
+
+ byte *data = new byte[width * height];
+ _sphinxfnt->read(data, width * height);
+ _syst->copyRectToScreen(data, width, xoffset, 16, width, height);
+ delete data;
+
+ return width;
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/font.h b/engines/groovie/font.h
new file mode 100644
index 0000000000..1d32f1a50c
--- /dev/null
+++ b/engines/groovie/font.h
@@ -0,0 +1,52 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_FONT_H
+#define GROOVIE_FONT_H
+
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace Groovie {
+
+class Font {
+public:
+ Font(OSystem *syst);
+ ~Font();
+ void printstring(char *messagein);
+
+private:
+ OSystem *_syst;
+ Common::MemoryReadStream *_sphinxfnt;
+
+ uint16 letteroffset(char letter);
+ uint8 letterwidth(char letter);
+ uint8 letterheight(char letter);
+ uint8 printletter(char letter, uint16 xoffset);
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_FONT_H
diff --git a/engines/groovie/graphics.cpp b/engines/groovie/graphics.cpp
new file mode 100644
index 0000000000..a7ad88a443
--- /dev/null
+++ b/engines/groovie/graphics.cpp
@@ -0,0 +1,161 @@
+/* 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/groovie.h"
+#include "groovie/graphics.h"
+
+namespace Groovie {
+
+GraphicsMan::GraphicsMan(GroovieEngine *vm) :
+ _vm(vm), _changed(false), _fading(0) {
+ // Create the game surfaces
+ _foreground.create(640, 320, 1);
+ _background.create(640, 320, 1);
+}
+
+GraphicsMan::~GraphicsMan() {
+ // Free the game surfaces
+ _foreground.free();
+ _background.free();
+}
+
+void GraphicsMan::update() {
+ if (_fading) {
+ // Set the start time
+ uint32 time = _vm->_system->getMillis() - _fadeStartTime;
+
+ // Scale the time
+ int step = time / 4;
+ if (step > 256) {
+ step = 256;
+ }
+
+ if (_fading == 1) {
+ // Apply the fade in
+ applyFading(step);
+ } else if (_fading == 2) {
+ // Apply the fade out
+ applyFading(256 - step);
+
+ // Clear the buffer when ending the fade out
+ if (step == 256)
+ _foreground.fillRect(Common::Rect::Rect(640, 320), 0);
+ }
+
+ // Check for the end
+ if (step == 256) {
+ _fading = 0;
+ }
+ }
+
+ // Update the screen if needed and reset the status
+ if (_changed) {
+ _vm->_system->updateScreen();
+ _changed = false;
+ }
+}
+
+void GraphicsMan::change() {
+ _changed = true;
+}
+
+void GraphicsMan::mergeFgAndBg() {
+ uint32 i;
+ byte *countf, *countb;
+
+ countf = (byte *)_foreground.getBasePtr(0, 0);
+ countb = (byte *)_background.getBasePtr(0, 0);
+ for (i = 640 * 320; i; i--) {
+ if (255 == *(countf)) {
+ *(countf) = *(countb);
+ }
+ countf++;
+ countb++;
+ }
+}
+
+
+void GraphicsMan::updateScreen(Graphics::Surface *source) {
+ _vm->_system->copyRectToScreen((byte *)source->getBasePtr(0, 0), 640, 0, 80, 640, 320);
+ change();
+}
+
+bool GraphicsMan::isFading() {
+ return _fading;
+}
+
+void GraphicsMan::fadeIn(byte *pal) {
+ // Set the start time
+ _fadeStartTime = _vm->_system->getMillis();
+
+ // Copy the target palette
+ for (int i = 0; i < 256; i++) {
+ _paletteFull[(i * 4) + 0] = pal[(i * 3) + 0];
+ _paletteFull[(i * 4) + 1] = pal[(i * 3) + 1];
+ _paletteFull[(i * 4) + 2] = pal[(i * 3) + 2];
+ }
+
+ // Apply a black palette right now
+ applyFading(0);
+
+ // Set the current fading
+ _fading = 1;
+}
+
+void GraphicsMan::fadeOut() {
+ // Set the start time
+ _fadeStartTime = _vm->_system->getMillis();
+
+ // Get the current palette
+ _vm->_system->grabPalette(_paletteFull, 0, 256);
+
+ // Set the current fading
+ _fading = 2;
+}
+
+void GraphicsMan::applyFading(int step) {
+ // Calculate the fade factor for the given step
+ int factorR = 256 - (256 - step) * 1;
+ int factorGB = 256 - (256 - step) * 2;
+
+ if (factorR <= 0) factorR = 0;
+ if (factorGB <= 0) factorGB = 0;
+
+ // Calculate the new palette
+ byte newpal[256 * 4];
+ for (int i = 0; i < 256; i++) {
+ newpal[(i * 4) + 0] = (_paletteFull[(i * 4) + 0] * factorR) / 256;
+ newpal[(i * 4) + 1] = (_paletteFull[(i * 4) + 1] * factorGB) / 256;
+ newpal[(i * 4) + 2] = (_paletteFull[(i * 4) + 2] * factorGB) / 256;
+ }
+
+ // Set the screen palette
+ _vm->_system->setPalette(newpal, 0, 256);
+
+ // Request a screen update
+ change();
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/graphics.h b/engines/groovie/graphics.h
new file mode 100644
index 0000000000..ea3261c85f
--- /dev/null
+++ b/engines/groovie/graphics.h
@@ -0,0 +1,65 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_GRAPHICS_H
+#define GROOVIE_GRAPHICS_H
+
+namespace Groovie {
+
+class GroovieEngine;
+
+class GraphicsMan {
+public:
+ GraphicsMan(GroovieEngine *vm);
+ ~GraphicsMan();
+
+ // Buffers
+ void update();
+ void change();
+ void mergeFgAndBg();
+ void updateScreen(Graphics::Surface *source);
+ Graphics::Surface _foreground; // The main surface that most things are drawn to
+ Graphics::Surface _background; // Used occasionally, mostly (only?) in puzzles
+
+ // Palette fading
+ bool isFading();
+ void fadeIn(byte *pal);
+ void fadeOut();
+
+private:
+ GroovieEngine *_vm;
+
+ bool _changed;
+
+ // Palette fading
+ void applyFading(int step);
+ int _fading;
+ byte _paletteFull[256 * 4];
+ uint32 _fadeStartTime;
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_GRAPHICS_H
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
new file mode 100644
index 0000000000..d49579c0a2
--- /dev/null
+++ b/engines/groovie/groovie.cpp
@@ -0,0 +1,272 @@
+/* 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 "common/config-manager.h"
+#include "common/events.h"
+#include "sound/mixer.h"
+
+#include "groovie/groovie.h"
+#include "groovie/music.h"
+#include "groovie/roq.h"
+#include "groovie/vdx.h"
+
+namespace Groovie {
+
+GroovieEngine::GroovieEngine(OSystem *syst, GroovieGameDescription *gd) :
+ Engine(syst), _gameDescription(gd), _debugger(NULL), _script(this),
+ _resMan(NULL), _cursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
+ _graphicsMan(NULL), _waitingForInput(false) {
+
+ // Adding the default directories
+ Common::File::addDefaultDirectory(_gameDataDir.getChild("groovie"));
+ Common::File::addDefaultDirectory(_gameDataDir.getChild("media"));
+ Common::File::addDefaultDirectory(_gameDataDir.getChild("system"));
+
+ // Initialize the custom debug levels
+ Common::addSpecialDebugLevel(kGroovieDebugAll, "All", "Debug everything");
+ Common::addSpecialDebugLevel(kGroovieDebugVideo, "Video", "Debug video and audio playback");
+ Common::addSpecialDebugLevel(kGroovieDebugResource, "Resource", "Debug resouce management");
+ Common::addSpecialDebugLevel(kGroovieDebugScript, "Script", "Debug the scripts");
+ Common::addSpecialDebugLevel(kGroovieDebugUnknown, "Unknown", "Report values of unknown data in files");
+ Common::addSpecialDebugLevel(kGroovieDebugHotspots, "Hotspots", "Show the hotspots");
+ Common::addSpecialDebugLevel(kGroovieDebugCursor, "Cursor", "Debug cursor decompression / switching");
+}
+
+GroovieEngine::~GroovieEngine() {
+ // Delete the remaining objects
+ delete _debugger;
+ delete _resMan;
+ delete _cursorMan;
+ delete _videoPlayer;
+ delete _musicPlayer;
+ delete _graphicsMan;
+}
+
+Common::Error GroovieEngine::init() {
+ // Initialize the graphics
+ _system->beginGFXTransaction();
+ initCommonGFX(true);
+ _system->initSize(640, 480);
+ _system->endGFXTransaction();
+
+ // Create debugger. It requires GFX to be initialized
+ _debugger = new Debugger(this);
+ _script.setDebugger(_debugger);
+
+ // Create the graphics manager
+ _graphicsMan = new GraphicsMan(this);
+
+ // Create the resource and cursor managers and the video player
+ switch (_gameDescription->version) {
+ case kGroovieT7G:
+ _resMan = new ResMan_t7g();
+ _cursorMan = new CursorMan_t7g(_system);
+ _videoPlayer = new VDXPlayer(this);
+ break;
+ case kGroovieV2:
+ _resMan = new ResMan_v2();
+ _cursorMan = new CursorMan_v2(_system);
+ _videoPlayer = new ROQPlayer(this);
+ break;
+ }
+
+ // Create the music player
+ _musicPlayer = new MusicPlayer(this);
+
+ // Load volume levels
+ syncSoundSettings();
+
+ // Get the name of the main script
+ Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName;
+ if (_gameDescription->version == kGroovieT7G) {
+ // Run The 7th Guest's demo if requested
+ if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode")) {
+ filename = Common::String("demo.grv");
+ }
+ } else if (_gameDescription->version == kGroovieV2) {
+ // Open the disk index
+ Common::File disk;
+ if (!disk.open(filename)) {
+ error("Couldn't open %s", filename.c_str());
+ return Common::kNoGameDataFoundError;
+ }
+
+ // Search the entry
+ bool found = false;
+ int index = 0;
+ while (!found && !disk.eos()) {
+ Common::String line = disk.readLine();
+ if (line.hasPrefix("title: ")) {
+ // A new entry
+ index++;
+ } else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) {
+ // It's the boot of the entry were looking for,
+ // get the script filename
+ filename = line.c_str() + 6;
+ found = true;
+ }
+ }
+
+ // Couldn't find the entry
+ if (!found) {
+ error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str());
+ return Common::kUnknownError;
+ }
+ }
+
+ // Check the script file extension
+ if (!filename.hasSuffix(".grv")) {
+ error("%s isn't a valid script filename", filename.c_str());
+ return Common::kUnknownError;
+ }
+
+ // Load the script
+ if (!_script.loadScript(filename)) {
+ error("Couldn't load the script file %s", filename.c_str());
+ return Common::kUnknownError;
+ }
+
+ // Should I load a saved game?
+ if (ConfMan.hasKey("save_slot")) {
+ // Get the requested slot
+ int slot = ConfMan.getInt("save_slot");
+ _script.directGameLoad(slot);
+ }
+
+ return Common::kNoError;
+}
+
+Common::Error GroovieEngine::go() {
+ // Check that the game files and the audio tracks aren't together run from
+ // the same cd
+
+ checkCD();
+
+ // Initialize the CD
+ int cd_num = ConfMan.getInt("cdrom");
+ if (cd_num >= 0)
+ _system->openCD(cd_num);
+
+ while (!shouldQuit()) {
+ // Show the debugger if required
+ if (_debugger->isAttached()) {
+ _debugger->onFrame();
+ }
+
+ // If there's still a script error after debugging, end the execution
+ if (_script.haveError()) {
+ quitGame();
+ break;
+ }
+
+ // Handle input
+ Common::Event ev;
+ while (_eventMan->pollEvent(ev)) {
+ switch (ev.type) {
+ case Common::EVENT_KEYDOWN:
+ // CTRL-D: Attach the debugger
+ if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
+ _debugger->attach();
+
+ // Send the event to the scripts
+ _script.setKbdChar(ev.kbd.ascii);
+
+ // Continue the script execution to handle the key
+ _waitingForInput = false;
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ // Continue the script execution, the mouse
+ // pointer may fall inside a hotspot now
+ _waitingForInput = false;
+ break;
+
+ case Common::EVENT_LBUTTONDOWN:
+ // Send the event to the scripts
+ _script.setMouseClick();
+
+ // Continue the script execution to handle
+ // the click
+ _waitingForInput = false;
+ break;
+
+ case Common::EVENT_QUIT:
+ quitGame();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (_waitingForInput) {
+ // Still waiting for input, just update the mouse and wait a bit more
+ _cursorMan->animate();
+ _system->updateScreen();
+ _system->delayMillis(50);
+ } else if (_graphicsMan->isFading()) {
+ // We're waiting for a fading to end, let the CPU rest
+ // for a while and continue
+ _system->delayMillis(30);
+ } else {
+ // Everything's fine, execute another script step
+ _script.step();
+ }
+
+ // Update the screen if required
+ _graphicsMan->update();
+ }
+
+ return Common::kNoError;
+}
+
+bool GroovieEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime);
+}
+
+void GroovieEngine::syncSoundSettings() {
+ _musicPlayer->setUserVolume(ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("speech_volume"));
+}
+
+bool GroovieEngine::canLoadGameStateCurrently() {
+ // TODO: verify the engine has been initialized
+ return true;
+}
+
+Common::Error GroovieEngine::loadGameState(int slot) {
+ _script.directGameLoad(slot);
+
+ // TODO: Use specific error codes
+ return Common::kNoError;
+}
+
+void GroovieEngine::waitForInput() {
+ _waitingForInput = true;
+}
+
+} // End of namespace Groovie
diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h
new file mode 100644
index 0000000000..ef5c9a3bda
--- /dev/null
+++ b/engines/groovie/groovie.h
@@ -0,0 +1,103 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_H
+#define GROOVIE_H
+
+#include "common/advancedDetector.h"
+#include "engines/engine.h"
+#include "graphics/surface.h"
+
+#include "groovie/cursor.h"
+#include "groovie/debug.h"
+#include "groovie/graphics.h"
+#include "groovie/player.h"
+#include "groovie/resource.h"
+#include "groovie/script.h"
+
+namespace Groovie {
+
+class MusicPlayer;
+
+enum kDebugLevels {
+ kGroovieDebugAll = 1 << 0,
+ kGroovieDebugVideo = 1 << 1,
+ kGroovieDebugResource = 1 << 2,
+ kGroovieDebugScript = 1 << 3,
+ kGroovieDebugUnknown = 1 << 4,
+ kGroovieDebugHotspots = 1 << 5,
+ kGroovieDebugCursor = 1 << 6,
+ kGroovieDebugMIDI = 1 << 7
+ // the current limitation is 32 debug levels (1 << 31 is the last one)
+};
+
+enum kEngineVersion {
+ kGroovieT7G,
+ kGroovieV2
+};
+
+struct GroovieGameDescription {
+ Common::ADGameDescription desc;
+
+ kEngineVersion version; // Version of the engine
+ int indexEntry; // The index of the entry in disk.1 for V2 games
+};
+
+class GroovieEngine : public Engine {
+public:
+ GroovieEngine(OSystem *syst, GroovieGameDescription *gd);
+ ~GroovieEngine();
+
+protected:
+ Common::Error init();
+ Common::Error go();
+
+public:
+ bool hasFeature(EngineFeature f) const;
+
+ bool canLoadGameStateCurrently();
+ Common::Error loadGameState(int slot);
+ void syncSoundSettings();
+
+ Debugger *getDebugger() { return _debugger; }
+
+ void waitForInput();
+
+ Script _script;
+ ResMan *_resMan;
+ CursorMan *_cursorMan;
+ VideoPlayer *_videoPlayer;
+ MusicPlayer *_musicPlayer;
+ GraphicsMan *_graphicsMan;
+
+private:
+ GroovieGameDescription *_gameDescription;
+ Debugger *_debugger;
+ bool _waitingForInput;
+};
+
+} // End of namespace Groovie
+
+#endif // GROOVIE_H
diff --git a/engines/groovie/lzss.cpp b/engines/groovie/lzss.cpp
new file mode 100644
index 0000000000..750625d70d
--- /dev/null
+++ b/engines/groovie/lzss.cpp
@@ -0,0 +1,99 @@
+/* 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/lzss.h"
+
+#define OUT_BUFF_SIZE 131072
+#define COMP_THRESH 3 // Compression not attempted if string to be compressed is less than 3 long
+
+LzssReadStream::LzssReadStream(Common::ReadStream *indata, uint8 lengthmask, uint8 lengthbits) {
+ /*
+ TODO: Nasty hack. Make a buffer bigger than I'll ever need... probably.
+ What should *really* happen is I should define a whole new type of stream
+ that gets lzss decompressed on the fly
+ */
+ _outLzssBufData = (uint8 *)malloc(OUT_BUFF_SIZE);
+ _size = decodeLZSS(indata, lengthmask, lengthbits);
+ _pos = 0;
+}
+
+LzssReadStream::~LzssReadStream() {
+ free(_outLzssBufData);
+}
+
+uint32 LzssReadStream::decodeLZSS(Common::ReadStream *in, uint8 lengthmask, uint8 lengthbits) {
+ uint32 N = 1 << (16 - lengthbits); /* History buffer size */
+ byte *histbuff = new byte[N]; /* History buffer */
+ memset(histbuff, 0, N);
+ uint32 outstreampos = 0;
+ uint32 bufpos = 0;
+
+ while (!in->eos()) {
+ byte flagbyte = in->readByte();
+ for (uint32 i = 1; i <= 8; i++) {
+ if (!in->eos()) {
+ if ((flagbyte & 1) == 0) {
+ uint32 offsetlen = in->readUint16LE();
+ if (offsetlen == 0) {
+ break;
+ }
+ uint32 length = (offsetlen & lengthmask) + COMP_THRESH;
+ uint32 offset = (bufpos - (offsetlen >> lengthbits)) & (N - 1);
+ for (uint32 j = 0; j < length; j++) {
+ byte tempa = histbuff[(offset + j) & (N - 1)];
+ _outLzssBufData[outstreampos++] = tempa;
+ histbuff[bufpos] = tempa;
+ bufpos = (bufpos + 1) & (N - 1);
+ }
+ } else {
+ byte tempa = in->readByte();
+ if (in->eos()) {
+ break;
+ }
+ _outLzssBufData[outstreampos++] = tempa;
+ histbuff[bufpos] = tempa;
+ bufpos = (bufpos + 1) & (N - 1);
+ }
+ flagbyte = flagbyte >> 1;
+ }
+ }
+ }
+ delete[] histbuff;
+ return outstreampos;
+}
+
+bool LzssReadStream::eos() const {
+ return _pos >= _size;
+}
+
+uint32 LzssReadStream::read(void *buf, uint32 size) {
+ if (size > _size - _pos)
+ size = _size - _pos;
+
+ memcpy(buf, &_outLzssBufData[_pos], size);
+ _pos += size;
+
+ return size;
+}
diff --git a/engines/groovie/lzss.h b/engines/groovie/lzss.h
new file mode 100644
index 0000000000..2aa0816252
--- /dev/null
+++ b/engines/groovie/lzss.h
@@ -0,0 +1,42 @@
+/* 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 "common/stream.h"
+
+class LzssReadStream : public Common::ReadStream {
+private:
+ uint8 *_outLzssBufData;
+ uint32 _size;
+ uint32 _pos;
+
+ uint32 decodeLZSS(Common::ReadStream *in, uint8 lengthmask, uint8 lengthbits);
+
+public:
+ LzssReadStream(Common::ReadStream *indata, uint8 lengthmask, uint8 lengthbits);
+ ~LzssReadStream();
+
+ bool eos() const;
+ uint32 read(void *buf, uint32 size);
+};
diff --git a/engines/groovie/module.mk b/engines/groovie/module.mk
new file mode 100644
index 0000000000..1779c9786e
--- /dev/null
+++ b/engines/groovie/module.mk
@@ -0,0 +1,24 @@
+MODULE := engines/groovie
+
+MODULE_OBJS := \
+ cursor.o \
+ debug.o \
+ detection.o \
+ font.o \
+ graphics.o \
+ groovie.o \
+ lzss.o \
+ music.o \
+ player.o \
+ resource.o \
+ roq.o \
+ script.o \
+ vdx.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_GROOVIE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
new file mode 100644
index 0000000000..1a7f6a4207
--- /dev/null
+++ b/engines/groovie/music.cpp
@@ -0,0 +1,211 @@
+/* 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/music.h"
+#include "groovie/resource.h"
+
+namespace Groovie {
+
+MusicPlayer::MusicPlayer(GroovieEngine *vm) :
+ _vm(vm), _midiParser(NULL), _data(NULL), _driver(NULL),
+ _backgroundFileRef(0) {
+ // Create the parser
+ _midiParser = MidiParser::createParser_XMIDI();
+
+ // Create the driver
+ int driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
+ _driver = createMidi(driver);
+ _driver->open();
+
+ // Initialize the channel volumes
+ for (int i = 0; i < 0x10; i++) {
+ _chanVolumes[i] = 0x7F;
+ }
+
+ // Set the parser's driver
+ _midiParser->setMidiDriver(this);
+
+ // Set the timer rate
+ _midiParser->setTimerRate(_driver->getBaseTempo());
+}
+
+MusicPlayer::~MusicPlayer() {
+ // Unload the parser
+ unload();
+ delete _midiParser;
+
+ // Unload the MIDI Driver
+ _driver->close();
+ delete _driver;
+}
+
+void MusicPlayer::playSong(uint16 fileref) {
+ // Play the referenced file once
+ play(fileref, false);
+}
+
+void MusicPlayer::setBackgroundSong(uint16 fileref) {
+ _backgroundFileRef = fileref;
+}
+
+void MusicPlayer::setUserVolume(uint16 volume) {
+ // Save the new user volume
+ _userVolume = volume;
+ if (_userVolume > 0x100) _userVolume = 0x100;
+
+ // Apply it to all the channels
+ for (int i = 0; i < 0x10; i++) {
+ updateChanVolume(i);
+ }
+ //FIXME: AdlibPercussionChannel::controlChange() is empty
+ //(can't set the volume for the percusion channel)
+}
+
+void MusicPlayer::setGameVolume(uint16 volume, uint16 time) {
+ //TODO: Implement volume fading
+ debugC(5, kGroovieDebugMIDI | kGroovieDebugAll, "setting game volume: %d, %d\n", volume, time);
+
+ // Save the new game volume
+ _gameVolume = volume;
+ if (_gameVolume > 100) _gameVolume = 100;
+
+ // Apply it to all the channels
+ for (int i = 0; i < 0x10; i++) {
+ updateChanVolume(i);
+ }
+}
+
+void MusicPlayer::updateChanVolume(byte channel) {
+ // Generate a MIDI Control change message for the volume
+ uint32 b = 0x7B0;
+
+ // Specify the channel
+ b |= (channel & 0xF);
+
+ // Scale by the user and game volumes
+ uint32 val = (_chanVolumes[channel] * _userVolume * _gameVolume) / 0x100 / 100;
+ val &= 0x7F;
+
+ // Send it to the driver
+ _driver->send(b | (val << 16));
+}
+
+bool MusicPlayer::play(uint16 fileref, bool loop) {
+ // Unload the previous song
+ unload();
+
+ // Set the looping option
+ _midiParser->property(MidiParser::mpAutoLoop, loop);
+
+ // Load the new file
+ return load(fileref);
+}
+
+bool MusicPlayer::load(uint16 fileref) {
+ // Open the song resource
+ Common::SeekableReadStream *xmidiFile = _vm->_resMan->open(fileref);
+ if (!xmidiFile) {
+ error("Groovie::Music: Couldn't resource 0x%04X", fileref);
+ return false;
+ }
+
+ // Read the whole file to memory
+ int length = xmidiFile->size();
+ _data = new byte[length];
+ xmidiFile->read(_data, length);
+ delete xmidiFile;
+
+ // Start parsing the data
+ if (!_midiParser->loadMusic(_data, length)) {
+ error("Groovie::Music: Invalid XMI file");
+ return false;
+ }
+
+ // Activate the timer source
+ _driver->setTimerCallback(_midiParser, MidiParser::timerCallback);
+
+ return true;
+}
+
+void MusicPlayer::unload() {
+ // Unload the parser
+ _midiParser->unloadMusic();
+
+ // Unload the xmi file
+ delete[] _data;
+ _data = NULL;
+}
+
+int MusicPlayer::open() {
+ return 0;
+}
+
+void MusicPlayer::close() {}
+
+void MusicPlayer::send(uint32 b) {
+ if ((b & 0xFFF0) == 0x07B0) { // Volume change
+ // Save the specific channel volume
+ byte chan = b & 0xF;
+ _chanVolumes[chan] = (b >> 16) & 0x7F;
+
+ // Send the updated value
+ updateChanVolume(chan);
+
+ return;
+ }
+ _driver->send(b);
+}
+
+void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
+ switch (type) {
+ case 0x2F:
+ // End of Track, play the background song
+ if (_backgroundFileRef) {
+ play(_backgroundFileRef, true);
+ }
+ break;
+ default:
+ _driver->metaEvent(type, data, length);
+ break;
+ }
+}
+
+void MusicPlayer::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
+ _driver->setTimerCallback(timer_param, timer_proc);
+}
+
+uint32 MusicPlayer::getBaseTempo(void) {
+ return _driver->getBaseTempo();
+}
+
+MidiChannel *MusicPlayer::allocateChannel() {
+ return _driver->allocateChannel();
+}
+
+MidiChannel *MusicPlayer::getPercussionChannel() {
+ return _driver->getPercussionChannel();
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
new file mode 100644
index 0000000000..6c8e6490b5
--- /dev/null
+++ b/engines/groovie/music.h
@@ -0,0 +1,78 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_MUSIC_H
+#define GROOVIE_MUSIC_H
+
+#include "groovie/groovie.h"
+
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+
+namespace Groovie {
+
+class MusicPlayer : public MidiDriver {
+public:
+ MusicPlayer(GroovieEngine *vm);
+ ~MusicPlayer();
+ void playSong(uint16 fileref);
+ void setBackgroundSong(uint16 fileref);
+
+ // Volume
+ void setUserVolume(uint16 volume);
+ void setGameVolume(uint16 volume, uint16 time);
+private:
+ uint16 _userVolume;
+ uint16 _gameVolume;
+ byte _chanVolumes[0x10];
+ void updateChanVolume(byte channel);
+
+public:
+ // MidiDriver interface
+ int open();
+ void close();
+ void send(uint32 b);
+ void metaEvent(byte type, byte *data, uint16 length);
+ void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
+ uint32 getBaseTempo(void);
+ MidiChannel *allocateChannel();
+ MidiChannel *getPercussionChannel();
+
+private:
+ GroovieEngine *_vm;
+ byte *_data;
+ MidiParser *_midiParser;
+ MidiDriver *_driver;
+
+ uint16 _backgroundFileRef;
+
+ bool play(uint16 fileref, bool loop);
+ bool load(uint16 fileref);
+ void unload();
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_MUSIC_H
diff --git a/engines/groovie/player.cpp b/engines/groovie/player.cpp
new file mode 100644
index 0000000000..5bac190701
--- /dev/null
+++ b/engines/groovie/player.cpp
@@ -0,0 +1,98 @@
+/* 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/groovie.h"
+#include "groovie/player.h"
+
+namespace Groovie {
+
+VideoPlayer::VideoPlayer(GroovieEngine *vm) :
+ _vm(vm), _syst(vm->_system), _file(NULL), _audioStream(NULL) {
+}
+
+bool VideoPlayer::load(Common::SeekableReadStream *file, uint16 flags) {
+ _file = file;
+ _flags = flags;
+ _audioStream = NULL;
+
+ uint16 fps = loadInternal();
+
+ if (fps != 0) {
+ _millisBetweenFrames = 1000 / fps;
+ _begunPlaying = false;
+ return true;
+ } else {
+ _file = NULL;
+ return false;
+ }
+}
+
+bool VideoPlayer::playFrame() {
+ bool end = true;
+
+ // Process the next frame while the file is open
+ if (_file) {
+ end = playFrameInternal();
+ }
+
+ // The file has been completely processed
+ if (end) {
+ _file = NULL;
+
+ // Wait for pending audio
+ if (_audioStream) {
+ if (_audioStream->endOfData()) {
+ // Mark the audio stream as finished (no more data will be appended)
+ _audioStream->finish();
+ } else {
+ // Don't end if there's still audio playing
+ end = false;
+ }
+ }
+ }
+
+ return end;
+}
+
+void VideoPlayer::waitFrame() {
+ uint32 currTime = _syst->getMillis();
+ if (!_begunPlaying) {
+ _begunPlaying = true;
+ _lastFrameTime = currTime;
+ } else {
+ uint32 millisDiff = currTime - _lastFrameTime;
+ if (millisDiff < _millisBetweenFrames) {
+ debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Delaying %d (currTime=%d, _lastFrameTime=%d, millisDiff=%d, _millisBetweenFrame=%d)",
+ _millisBetweenFrames - millisDiff, currTime, _lastFrameTime, millisDiff, _millisBetweenFrames);
+ _syst->delayMillis(_millisBetweenFrames - millisDiff);
+ currTime = _syst->getMillis();
+ debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Finished delay at %d", currTime);
+ }
+ debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::Player: Frame displayed at %d (%f FPS)", currTime, 1000.0 / (currTime - _lastFrameTime));
+ _lastFrameTime = currTime;
+ }
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/player.h b/engines/groovie/player.h
new file mode 100644
index 0000000000..cf2746137e
--- /dev/null
+++ b/engines/groovie/player.h
@@ -0,0 +1,68 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_PLAYER_H
+#define GROOVIE_PLAYER_H
+
+#include "common/system.h"
+#include "sound/audiostream.h"
+
+namespace Groovie {
+
+class GroovieEngine;
+
+class VideoPlayer {
+public:
+ VideoPlayer(GroovieEngine *vm);
+ virtual ~VideoPlayer() {}
+
+ bool load(Common::SeekableReadStream *file, uint16 flags);
+ bool playFrame();
+ virtual void setOrigin(int16 x, int16 y) {};
+
+protected:
+ // To be implemented by subclasses
+ virtual uint16 loadInternal() = 0;
+ virtual bool playFrameInternal() = 0;
+
+ GroovieEngine *_vm;
+ OSystem *_syst;
+ Common::SeekableReadStream *_file;
+ uint16 _flags;
+ Audio::AppendableAudioStream *_audioStream;
+
+private:
+ // Synchronization stuff
+ bool _begunPlaying;
+ uint16 _millisBetweenFrames;
+ uint32 _lastFrameTime;
+
+protected:
+ void waitFrame();
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_PLAYER_H
diff --git a/engines/groovie/resource.cpp b/engines/groovie/resource.cpp
new file mode 100644
index 0000000000..b4ae3ea90c
--- /dev/null
+++ b/engines/groovie/resource.cpp
@@ -0,0 +1,240 @@
+/* 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/groovie.h"
+#include "groovie/resource.h"
+
+namespace Groovie {
+
+// ResMan
+
+Common::SeekableReadStream *ResMan::open(uint16 fileRef) {
+ // Get the information about the resource
+ ResInfo resInfo;
+ if (!getResInfo(fileRef, resInfo)) {
+ return NULL;
+ }
+
+ // Do we know the name of the required GJD?
+ if (resInfo.gjd >= _gjds.size()) {
+ error("Groovie::Resource: Unknown GJD %d", resInfo.gjd);
+ return NULL;
+ }
+
+ debugC(1, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Opening resource 0x%04X (%s, %d, %d)", fileRef, _gjds[resInfo.gjd].c_str(), resInfo.offset, resInfo.size);
+
+ // Does it exist?
+ if (!Common::File::exists(_gjds[resInfo.gjd])) {
+ error("Groovie::Resource: %s not found", _gjds[resInfo.gjd].c_str());
+ return NULL;
+ }
+
+ // Open the pack file
+ Common::File *gjdFile = new Common::File();
+ if (!gjdFile->open(_gjds[resInfo.gjd].c_str())) {
+ delete gjdFile;
+ error("Groovie::Resource: Couldn't open %s", _gjds[resInfo.gjd].c_str());
+ return NULL;
+ }
+
+ // Save the used gjd file (except xmi and gamwav)
+ if (resInfo.gjd < 19) {
+ _lastGjd = resInfo.gjd;
+ }
+
+ // Returning the resource substream
+ return new Common::SeekableSubReadStream(gjdFile, resInfo.offset, resInfo.offset + resInfo.size, true);
+}
+
+
+// ResMan_t7g
+
+static const char t7g_gjds[][0x15] = {"at", "b", "ch", "d", "dr", "fh", "ga", "hdisk", "htbd", "intro", "jhek", "k", "la", "li", "mb", "mc", "mu", "n", "p", "xmi", "gamwav"};
+
+ResMan_t7g::ResMan_t7g() {
+ for (int i = 0; i < 0x15; i++) {
+ // Prepare the filename
+ Common::String filename = t7g_gjds[i];
+ filename += ".gjd";
+
+ // Append it to the list of GJD files
+ _gjds.push_back(filename);
+ }
+}
+
+uint16 ResMan_t7g::getRef(Common::String name, Common::String scriptname) {
+ // Get the name of the RL file
+ Common::String rlFileName(t7g_gjds[_lastGjd]);
+ rlFileName += ".rl";
+
+ // Open the RL file
+ Common::File rlFile;
+ if (!rlFile.open(rlFileName)) {
+ error("Groovie::Resource: Couldn't open %s", rlFileName.c_str());
+ return false;
+ }
+
+ uint16 resNum;
+ bool found = false;
+ for (resNum = 0; !found && !rlFile.ioFailed(); resNum++) {
+ // Read the resource name
+ char readname[12];
+ rlFile.read(readname, 12);
+
+ // Test whether it's the resource we're searching
+ Common::String resname(readname, 12);
+ if (resname.hasPrefix(name.c_str())) {
+ debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource %12s matches %s", readname, name.c_str());
+ found = true;
+ }
+
+ // Skip the rest of resource information
+ rlFile.read(readname, 8);
+ }
+
+ // Close the RL file
+ rlFile.close();
+
+ // Verify we really found the resource
+ if (!found) {
+ error("Groovie::Resource: Couldn't find resource %s in %s", name.c_str(), rlFileName.c_str());
+ return (uint16)-1;
+ }
+
+ return (_lastGjd << 10) | (resNum - 1);
+}
+
+bool ResMan_t7g::getResInfo(uint16 fileRef, ResInfo &resInfo) {
+ // Calculate the GJD and the resource number
+ resInfo.gjd = fileRef >> 10;
+ uint16 resNum = fileRef & 0x3FF;
+
+ // Get the name of the RL file
+ Common::String rlFileName(t7g_gjds[resInfo.gjd]);
+ rlFileName += ".rl";
+
+ // Open the RL file
+ Common::File rlFile;
+ if (!rlFile.open(rlFileName)) {
+ error("Groovie::Resource: Couldn't open %s", rlFileName.c_str());
+ return false;
+ }
+
+ // Seek to the position of the desired resource
+ rlFile.seek(resNum * 20);
+ if (rlFile.eos()) {
+ rlFile.close();
+ error("Groovie::Resource: Invalid resource number: 0x%04X (%s)", resNum, rlFileName.c_str());
+ return false;
+ }
+
+ // Read the resource name (just for debugging purposes)
+ char resname[12];
+ rlFile.read(resname, 12);
+ debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource name: %12s", resname);
+
+ // Read the resource information
+ resInfo.offset = rlFile.readUint32LE();
+ resInfo.size = rlFile.readUint32LE();
+
+ // Close the resource RL file
+ rlFile.close();
+
+ return true;
+}
+
+
+// ResMan_v2
+
+ResMan_v2::ResMan_v2() {
+ Common::File indexfile;
+
+ // Open the GJD index file
+ if (!indexfile.open("gjd.gjd")) {
+ error("Groovie::Resource: Couldn't open gjd.gjd");
+ return;
+ }
+
+ Common::String line = indexfile.readLine();
+ while (!indexfile.eos() && !line.empty()) {
+ // Get the name before the space
+ Common::String filename;
+ for (const char *cur = line.c_str(); *cur != ' '; cur++) {
+ filename += *cur;
+ }
+
+ // Append it to the list of GJD files
+ if (!filename.empty()) {
+ _gjds.push_back(filename);
+ }
+
+ // Read the next line
+ line = indexfile.readLine();
+ }
+
+ // Close the GJD index file
+ indexfile.close();
+}
+
+uint16 ResMan_v2::getRef(Common::String name, Common::String scriptname) {
+ return 0;
+}
+
+bool ResMan_v2::getResInfo(uint16 fileRef, ResInfo &resInfo) {
+ // Open the RL file
+ Common::File rlFile;
+ if (!rlFile.open("dir.rl")) {
+ error("Groovie::Resource: Couldn't open dir.rl");
+ return false;
+ }
+
+ // Seek to the position of the desired resource
+ rlFile.seek(fileRef * 32);
+ if (rlFile.eos()) {
+ rlFile.close();
+ error("Groovie::Resource: Invalid resource number: 0x%04X", fileRef);
+ return false;
+ }
+
+ // Read the resource information
+ rlFile.readUint32LE(); // Unknown
+ resInfo.offset = rlFile.readUint32LE();
+ resInfo.size = rlFile.readUint32LE();
+ resInfo.gjd = rlFile.readUint16LE();
+
+ // Read the resource name (just for debugging purposes)
+ char resname[12];
+ rlFile.read(resname, 12);
+ debugC(2, kGroovieDebugResource | kGroovieDebugAll, "Groovie::Resource: Resource name: %12s", resname);
+
+ // 6 padding bytes? (it looks like they're always 0)
+
+ // Close the resource RL file
+ rlFile.close();
+
+ return true;
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/resource.h b/engines/groovie/resource.h
new file mode 100644
index 0000000000..09032a9c50
--- /dev/null
+++ b/engines/groovie/resource.h
@@ -0,0 +1,71 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_RESOURCE_H
+#define GROOVIE_RESOURCE_H
+
+namespace Groovie {
+
+struct ResInfo {
+ uint16 gjd;
+ uint32 offset;
+ uint32 size;
+};
+
+class ResMan {
+public:
+ virtual ~ResMan() {};
+
+ Common::SeekableReadStream *open(uint16 fileRef);
+ virtual uint16 getRef(Common::String name, Common::String scriptname = "") = 0;
+
+protected:
+ Common::Array<Common::String> _gjds;
+ virtual bool getResInfo(uint16 fileRef, ResInfo &resInfo) = 0;
+
+ uint16 _lastGjd;
+};
+
+class ResMan_t7g : public ResMan {
+public:
+ ResMan_t7g();
+ ~ResMan_t7g() {};
+
+ uint16 getRef(Common::String name, Common::String scriptname);
+ bool getResInfo(uint16 fileRef, ResInfo &resInfo);
+};
+
+class ResMan_v2 : public ResMan {
+public:
+ ResMan_v2();
+ ~ResMan_v2() {};
+
+ uint16 getRef(Common::String name, Common::String scriptname);
+ bool getResInfo(uint16 fileRef, ResInfo &resInfo);
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_RESOURCE_H
diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
new file mode 100644
index 0000000000..589251da31
--- /dev/null
+++ b/engines/groovie/roq.cpp
@@ -0,0 +1,403 @@
+/* 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/groovie.h"
+#include "groovie/roq.h"
+
+#include "sound/mixer.h"
+
+namespace Groovie {
+
+ROQPlayer::ROQPlayer(GroovieEngine *vm) :
+ VideoPlayer(vm) {
+}
+
+ROQPlayer::~ROQPlayer() {
+}
+
+uint16 ROQPlayer::loadInternal() {
+ // Begin reading the file
+ debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Loading video");
+
+ // Read the file header
+ ROQBlockHeader blockHeader;
+ if (!readBlockHeader(blockHeader)) {
+ return 0;
+ }
+ if (blockHeader.type != 0x1084 || blockHeader.size != 0 || blockHeader.param != 0) {
+ return 0;
+ }
+
+ // Hardcoded FPS
+ return 25;
+}
+
+bool ROQPlayer::playFrameInternal() {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Playing frame");
+
+ // Process the needed blocks until the next video frame
+ bool endframe = false;
+ while (!endframe && !_file->eos()) {
+ endframe = processBlock();
+ }
+
+ // Wait until the current frame can be shown
+ waitFrame();
+
+ // Update the screen
+ _syst->updateScreen();
+
+ // Return whether the video has ended
+ return _file->eos();
+}
+
+bool ROQPlayer::readBlockHeader(ROQBlockHeader &blockHeader) {
+ if (_file->eos()) {
+ return false;
+ } else {
+ blockHeader.type = _file->readUint16LE();
+ blockHeader.size = _file->readUint32LE();
+ blockHeader.param = _file->readUint16LE();
+
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block type = 0x%02X", blockHeader.type);
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block size = 0x%08X", blockHeader.size);
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block param = 0x%04X", blockHeader.param);
+
+ return true;
+ }
+}
+
+bool ROQPlayer::processBlock() {
+ // Read the header of the block
+ ROQBlockHeader blockHeader;
+ if (!readBlockHeader(blockHeader)) {
+ return true;
+ }
+
+ bool ok = true;
+ bool endframe = false;
+ switch (blockHeader.type) {
+ case 0x1001: // Video info
+ ok = processBlockInfo(blockHeader);
+ break;
+ case 0x1002: // Quad codebook definition
+ ok = processBlockQuadCodebook(blockHeader);
+ break;
+ case 0x1011: // Quad vector quantised video frame
+ ok = processBlockQuadVector(blockHeader);
+ endframe = true;
+ break;
+ case 0x1012: // Still image (JPEG)
+ ok = processBlockStill(blockHeader);
+ endframe = true;
+ break;
+ case 0x1013: // Hang
+ //warning("Groovie::ROQ: Hang block (skipped)");
+ break;
+ case 0x1020: // Mono sound samples
+ ok = processBlockSoundMono(blockHeader);
+ break;
+ case 0x1021: // Stereo sound samples
+ ok = processBlockSoundStereo(blockHeader);
+ break;
+ case 0x1030: // Audio container
+ ok = processBlockAudioContainer(blockHeader);
+ break;
+ default:
+ error("Groovie::ROQ: Unknown block type: 0x%04X", blockHeader.type);
+ ok = false;
+ }
+
+ // End the frame when the graphics have been modified or when there's an error
+ return endframe || !ok;
+}
+
+bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing info block");
+
+ // Verify the block header
+ if (blockHeader.type != 0x1001 || blockHeader.size != 8 || blockHeader.param != 0) {
+ return false;
+ }
+
+ uint16 tmp;
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "w = %d\n", tmp);
+ if (tmp != 640) {
+ return false;
+ }
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "h = %d\n", tmp);
+ if (tmp != 320) {
+ return false;
+ }
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "unk1 = %d\n", tmp);
+ if (tmp != 8) {
+ return false;
+ }
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "unk2 = %d\n", tmp);
+ if (tmp != 4) {
+ return false;
+ }
+ return true;
+}
+
+bool ROQPlayer::processBlockQuadCodebook(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad codebook block");
+
+ // Get the number of 2x2 pixel blocks
+ _num2blocks = blockHeader.param >> 8;
+ if (_num2blocks == 0) {
+ _num2blocks = 256;
+ }
+
+ // Get the number of 4x4 pixel blocks
+ _num4blocks = blockHeader.param & 0xFF;
+ if (_num4blocks == 0 && (blockHeader.size > (uint32)_num2blocks * 6)) {
+ _num4blocks = 256;
+ }
+
+ _file->skip(_num2blocks * 6);
+ _file->skip(_num4blocks * 4);
+
+ return true;
+}
+
+bool ROQPlayer::processBlockQuadVector(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector block");
+ _file->skip(blockHeader.size);
+ return true;
+
+ // Get the mean motion vectors
+ //byte Mx = blockHeader.param >> 8;
+ //byte My = blockHeader.param & 0xFF;
+
+ int32 ends =_file->pos() + blockHeader.size;
+ int numblocks = (640 / 8) * (320 / 8);
+ for (int j = 0; j < numblocks && ends > _file->pos(); j++) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "doing block %d/%d\n", j, numblocks);
+ uint16 codingType = _file->readUint16LE();
+ for (int i = 0; i < 8; i++) {
+ switch (codingType >> 14) {
+ case 0: // MOT: Skip block
+ //printf("coding type 0\n");
+ break;
+ case 1: { // FCC: Copy an existing block
+ //printf("coding type 1\n");
+ byte argument;
+ argument = _file->readByte();
+ //byte Dx = Mx + (argument >> 4);
+ //byte Dy = My + (argument & 0x0F);
+ // Dx = X + 8 - (argument >> 4) - Mx
+ // Dy = Y + 8 - (argument & 0x0F) - My
+ break;
+ }
+ case 2: { // SLD: Quad vector quantisation
+ //printf("coding type 2\n");
+ byte argument = _file->readByte();
+ if (argument > _num4blocks) {
+ //error("invalid 4x4 block %d of %d", argument, _num4blocks);
+ }
+ // Upsample the 4x4 pixel block
+ break;
+ }
+ case 3: // CCC:
+ //printf("coding type 3:\n");
+ processBlockQuadVectorSub(blockHeader);
+ break;
+ }
+ codingType <<= 2;
+ }
+ }
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Should have ended at %d, and has ended at %d\n", ends, _file->pos());
+ return true;
+}
+
+bool ROQPlayer::processBlockQuadVectorSub(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block");
+
+ // Get the mean motion vectors
+ //byte Mx = blockHeader.param >> 8;
+ //byte My = blockHeader.param & 0xFF;
+
+ uint16 codingType = _file->readUint16LE();
+ for (int i = 0; i < 4; i++) {
+ switch (codingType >> 14) {
+ case 0: // MOT: Skip block
+ //printf("coding type 0\n");
+ break;
+ case 1: { // FCC: Copy an existing block
+ //printf("coding type 1\n");
+ byte argument;
+ argument = _file->readByte();
+ //byte Dx = Mx + (argument >> 4);
+ //byte Dy = My + (argument & 0x0F);
+ // Dx = X + 8 - (argument >> 4) - Mx
+ // Dy = Y + 8 - (argument & 0x0F) - My
+ break;
+ }
+ case 2: { // SLD: Quad vector quantisation
+ //printf("coding type 2\n");
+ byte argument = _file->readByte();
+ if (argument > _num2blocks) {
+ //error("invalid 2x2 block: %d of %d", argument, _num2blocks);
+ }
+ break;
+ }
+ case 3:
+ //printf("coding type 3\n");
+ _file->readByte();
+ _file->readByte();
+ _file->readByte();
+ _file->readByte();
+ break;
+ }
+ codingType <<= 2;
+ }
+ return true;
+}
+
+bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block");
+ //Common::ReadStream *jpegData = new Common::SubReadStream(_file, blockHeader.size);
+ //Graphics::JPEG jpegFrame;
+ //jpegFrame.read(jpegData);
+ /*
+ Common::File save;
+ save.open("dump.jpg", Common::File::kFileWriteMode);
+ save.write(data, blockHeader.size);
+ save.close();
+ */
+ error("JPEG!");
+ return true;
+}
+
+bool ROQPlayer::processBlockSoundMono(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing mono sound block");
+
+ // Verify the block header
+ if (blockHeader.type != 0x1020) {
+ return false;
+ }
+
+ // Initialize the audio stream if needed
+ if (!_audioStream) {
+ byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE;
+#ifdef SCUMM_LITTLE_ENDIAN
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+ _audioStream = Audio::makeAppendableAudioStream(22050, flags);
+ Audio::SoundHandle sound_handle;
+ ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
+ }
+
+ // Create the audio buffer
+ int16 *buffer = new int16[blockHeader.size];
+
+ // Initialize the prediction with the block parameter
+ int16 prediction = blockHeader.param ^ 0x8000;
+
+ // Process the data
+ for (uint16 i = 0; i < blockHeader.size; i++) {
+ int16 data = _file->readByte();
+ if (data < 0x80) {
+ prediction += data * data;
+ } else {
+ data -= 0x80;
+ prediction -= data * data;
+ }
+ buffer[i] = prediction;
+ }
+
+ // Queue the read buffer
+ _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
+
+ return true;
+}
+
+bool ROQPlayer::processBlockSoundStereo(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing stereo sound block");
+
+ // Verify the block header
+ if (blockHeader.type != 0x1021) {
+ return false;
+ }
+
+ // Initialize the audio stream if needed
+ if (!_audioStream) {
+ byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_STEREO;
+#ifdef SCUMM_LITTLE_ENDIAN
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+ _audioStream = Audio::makeAppendableAudioStream(22050, flags);
+ Audio::SoundHandle sound_handle;
+ ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
+ }
+
+ // Create the audio buffer
+ int16 *buffer = new int16[blockHeader.size];
+
+ // Initialize the prediction with the block parameter
+ int16 predictionLeft = (blockHeader.param & 0xFF00) ^ 0x8000;
+ int16 predictionRight = (blockHeader.param << 8) ^ 0x8000;
+ bool left = true;
+
+ // Process the data
+ for (uint16 i = 0; i < blockHeader.size; i++) {
+ int16 data = _file->readByte();
+ if (left) {
+ if (data < 0x80) {
+ predictionLeft += data * data;
+ } else {
+ data -= 0x80;
+ predictionLeft -= data * data;
+ }
+ buffer[i] = predictionLeft;
+ } else {
+ if (data < 0x80) {
+ predictionRight += data * data;
+ } else {
+ data -= 0x80;
+ predictionRight -= data * data;
+ }
+ buffer[i] = predictionRight;
+ }
+ left = !left;
+ }
+
+ // Queue the read buffer
+ _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
+
+ return true;
+}
+
+bool ROQPlayer::processBlockAudioContainer(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing audio container block: 0x%04X", blockHeader.param);
+ return true;
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/roq.h b/engines/groovie/roq.h
new file mode 100644
index 0000000000..215974e3bd
--- /dev/null
+++ b/engines/groovie/roq.h
@@ -0,0 +1,69 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_ROQ_H
+#define GROOVIE_ROQ_H
+
+#include "groovie/player.h"
+
+namespace Groovie {
+
+class GroovieEngine;
+
+struct ROQBlockHeader {
+ uint16 type;
+ uint32 size;
+ uint16 param;
+};
+
+class ROQPlayer : public VideoPlayer {
+public:
+ ROQPlayer(GroovieEngine *vm);
+ ~ROQPlayer();
+
+protected:
+ uint16 loadInternal();
+ bool playFrameInternal();
+
+private:
+ bool readBlockHeader(ROQBlockHeader &blockHeader);
+
+ bool processBlock();
+ bool processBlockInfo(ROQBlockHeader &blockHeader);
+ bool processBlockQuadCodebook(ROQBlockHeader &blockHeader);
+ bool processBlockQuadVector(ROQBlockHeader &blockHeader);
+ bool processBlockQuadVectorSub(ROQBlockHeader &blockHeader);
+ bool processBlockStill(ROQBlockHeader &blockHeader);
+ bool processBlockSoundMono(ROQBlockHeader &blockHeader);
+ bool processBlockSoundStereo(ROQBlockHeader &blockHeader);
+ bool processBlockAudioContainer(ROQBlockHeader &blockHeader);
+
+ uint16 _num2blocks;
+ uint16 _num4blocks;
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_ROQ_H
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
diff --git a/engines/groovie/script.h b/engines/groovie/script.h
new file mode 100644
index 0000000000..3d8fda17af
--- /dev/null
+++ b/engines/groovie/script.h
@@ -0,0 +1,211 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_SCRIPT_H
+#define GROOVIE_SCRIPT_H
+
+#include "common/file.h"
+#include "common/rect.h"
+
+#include "groovie/font.h"
+
+namespace Groovie {
+
+class GroovieEngine;
+
+class Script {
+ friend class Debugger;
+
+public:
+ Script(GroovieEngine *vm);
+ ~Script();
+
+ void setDebugger(Debugger *debugger);
+
+ bool loadScript(Common::String scriptfile);
+ void directGameLoad(int slot);
+ void step();
+
+ void setMouseClick();
+ void setKbdChar(uint8 c);
+
+ bool haveError();
+
+private:
+ GroovieEngine *_vm;
+
+ Common::RandomSource _random;
+
+ bool _firstbit;
+
+ // Script filename (for debugging purposes)
+ Common::String _scriptFile;
+ Common::String _savedScriptFile;
+
+ // Code
+ byte *_code;
+ uint16 _currentInstruction;
+ byte *_savedCode;
+ uint16 _savedInstruction;
+
+ // Variables
+ byte _variables[0x400];
+ byte _savedVariables[0x180];
+
+ // Stack
+ uint16 _stack[0x20];
+ uint8 _stacktop;
+ uint8 _savedStacktop;
+
+ // Input
+ bool _mouseClicked;
+ bool _eventMouseClicked;
+ uint8 _kbdChar;
+ uint8 _eventKbdChar;
+ uint16 _inputLoopAddress;
+ int16 _inputAction;
+ uint8 _newCursorStyle;
+ uint16 _hotspotTopAction;
+ uint16 _hotspotTopCursor;
+ uint16 _hotspotBottomAction;
+ uint16 _hotspotBottomCursor;
+ uint16 _hotspotRightAction;
+ uint16 _hotspotLeftAction;
+ uint16 _hotspotCursorOldX;
+ uint16 _hotspotCursorOldY;
+
+ // Video
+ Font *_font;
+ Common::SeekableReadStream *_videoFile;
+ uint16 _videoRef;
+ uint16 _bitflags;
+
+ // Debugging
+ Debugger *_debugger;
+ Common::String _debugString;
+ void error(const char *msg);
+ bool _error;
+
+ // Helper functions
+ uint8 readScript8bits();
+ uint16 readScript16bits();
+ uint32 readScript32bits();
+ uint16 readScript8or16bits();
+ uint8 readScriptChar(bool allow7C, bool limitVal, bool limitVar);
+ uint8 readScriptVar();
+ uint16 getVideoRefString();
+
+ bool hotspot(Common::Rect rect, uint16 addr, uint8 cursor);
+
+ void loadgame(uint slot);
+ void savegame(uint slot);
+ bool playvideofromref(uint16 fileref);
+
+ // Opcodes
+ typedef void (Script::*OpcodeFunc)();
+ static OpcodeFunc _opcodes[];
+
+ void o_invalid();
+
+ void o_nop();
+ void o_nop8();
+ void o_nop16();
+ void o_nop32();
+ void o_nop8or16();
+
+ void o_playsong();
+ void o_bf9on();
+ void o_palfadeout();
+ void o_bf8on();
+ void o_bf6on();
+ void o_bf7on();
+ void o_setbackgroundsong();
+ void o_videofromref();
+ void o_bf5on();
+ void o_inputloopstart();
+ void o_keyboardaction();
+ void o_hotspot_rect();
+ void o_hotspot_left();
+ void o_hotspot_right();
+ void o_hotspot_center();
+ void o_hotspot_current();
+ void o_inputloopend();
+ void o_random();
+ void o_jmp();
+ void o_loadstring();
+ void o_ret();
+ void o_call();
+ void o_sleep();
+ void o_strcmpnejmp_var();
+ void o_copybgtofg();
+ void o_strcmpnejmp();
+ void o_xor_obfuscate();
+ void o_vdxtransition();
+ void o_swap();
+ void o_inc();
+ void o_dec();
+ void o_strcmpeqjmp();
+ void o_mov();
+ void o_add();
+ void o_videofromstring1();
+ void o_videofromstring2();
+ void o_stopmidi();
+ void o_endscript();
+ void o_sethotspottop();
+ void o_sethotspotbottom();
+ void o_loadgame();
+ void o_savegame();
+ void o_hotspotbottom_4();
+ void o_midivolume();
+ void o_jne();
+ void o_loadstringvar();
+ void o_chargreatjmp();
+ void o_bf7off();
+ void o_charlessjmp();
+ void o_copyrecttobg();
+ void o_restorestkpnt();
+ void o_obscureswap();
+ void o_printstring();
+ void o_hotspot_slot();
+ void o_checkvalidsaves();
+ void o_resetvars();
+ void o_mod();
+ void o_loadscript();
+ void o_setvideoorigin();
+ void o_sub();
+ void o_othello();
+ void o_returnscript();
+ void o_sethotspotright();
+ void o_sethotspotleft();
+ void o_getcd();
+ void o_opcode4D();
+ void o_hotspot_outrect();
+ void o_stub56();
+ void o_stub59();
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_SCRIPT_H
diff --git a/engines/groovie/vdx.cpp b/engines/groovie/vdx.cpp
new file mode 100644
index 0000000000..4a0198de22
--- /dev/null
+++ b/engines/groovie/vdx.cpp
@@ -0,0 +1,522 @@
+/* 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/groovie.h"
+#include "groovie/lzss.h"
+#include "groovie/vdx.h"
+
+#include "sound/mixer.h"
+
+#define TILE_SIZE 4 // Size of each tile on the image: only ever seen 4 so far
+#define VDX_IDENT 0x9267 // 37479
+
+namespace Groovie {
+
+VDXPlayer::VDXPlayer(GroovieEngine *vm) :
+ VideoPlayer(vm), _origX(0), _origY(0), _flagOnePrev(false),
+ _fg(&_vm->_graphicsMan->_foreground), _bg(&_vm->_graphicsMan->_background) {
+}
+
+VDXPlayer::~VDXPlayer() {
+ //delete _audioStream;
+}
+
+void VDXPlayer::setOrigin(int16 x, int16 y) {
+ _origX = x;
+ _origY = y;
+}
+
+uint16 VDXPlayer::loadInternal() {
+ uint32 engine_level = kGroovieDebugVideo | kGroovieDebugAll;
+ if ((gDebugLevel == 11) || (Common::getEnabledSpecialDebugLevels() & engine_level)) {
+ int8 i;
+ debugN(1, "Groovie::VDX: New VDX: bitflags are ");
+ for (i = 11; i >= 0; i--) {
+ debugN(1, "%d", _flags & (1 << i)? 1 : 0);
+ if (i % 4 == 0) {
+ debugN(1, " ");
+ }
+ }
+ debug(1, " ");
+ }
+ // Flags:
+ // - 1 Puzzle piece? Skip palette, don't redraw full screen, draw still to b/ack buffer
+ // - 2 Transparent colour is 0xFF
+ // - 5 Skip still chunks
+ // - 7
+ // - 8 Just show the first frame
+ // - 9 Start a palette fade in
+ _flagZero = ((_flags & (1 << 0)) != 0);
+ _flagOne = ((_flags & (1 << 1)) != 0);
+ _flag2Byte = (_flags & (1 << 2)) ? 0xFF : 0x00;
+ _flagThree = ((_flags & (1 << 3)) != 0);
+ _flagFour = ((_flags & (1 << 4)) != 0);
+ _flagFive = ((_flags & (1 << 5)) != 0);
+ _flagSix = ((_flags & (1 << 6)) != 0);
+ _flagSeven = ((_flags & (1 << 7)) != 0);
+ _flagEight = ((_flags & (1 << 8)) != 0);
+ _flagNine = ((_flags & (1 << 9)) != 0);
+
+ if (_flagOnePrev && !_flagOne && !_flagEight) {
+ _flagSeven = true;
+ }
+
+ // Save _flagOne for the next video
+ _flagOnePrev = _flagOne;
+
+ //_flagTransparent = _flagOne;
+ _flagFirstFrame = _flagEight;
+ //_flagSkipPalette = _flagSeven;
+ _flagSkipPalette = false;
+ //_flagSkipStill = _flagFive || _flagSeven;
+ //_flagUpdateStill = _flagNine || _flagSix;
+
+ // Begin reading the file
+ debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Playing video");
+
+ if (_file->readUint16LE() != VDX_IDENT) {
+ error("Groovie::VDX: This does not appear to be a 7th guest vxd file");
+ return 0;
+ } else {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: VDX file identified correctly");
+ }
+
+ uint16 tmp;
+
+ // Skip unknown data: 6 bytes, ref Martine
+ tmp = _file->readUint16LE();
+ debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine1 = 0x%04X", tmp);
+ tmp = _file->readUint16LE();
+ debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine2 = 0x%04X", tmp);
+ tmp = _file->readUint16LE();
+ debugC(2, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Martine3 (FPS?) = %d", tmp);
+
+ return tmp;
+}
+
+bool VDXPlayer::playFrameInternal() {
+ byte currRes = 0x80;
+ while (!_file->eos() && currRes == 0x80) {
+ currRes = _file->readByte();
+
+ // Skip unknown data: 1 byte, ref Edward
+ byte tmp = _file->readByte();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugUnknown | kGroovieDebugAll, "Groovie::VDX: Edward = 0x%04X", tmp);
+
+ uint32 compSize = _file->readUint32LE();
+ uint8 lengthmask = _file->readByte();
+ uint8 lengthbits = _file->readByte();
+
+ // Read the chunk data and decompress if needed
+ Common::ReadStream *vdxData = new Common::SubReadStream(_file, compSize);
+ if (lengthmask && lengthbits) {
+ Common::ReadStream *decompData = new LzssReadStream(vdxData, lengthmask, lengthbits);
+ delete vdxData;
+ vdxData = decompData;
+ }
+
+ // Use the current chunk
+ switch (currRes) {
+ case 0x00:
+ debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Replay frame");
+ break;
+ case 0x20:
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Still frame");
+ getStill(vdxData);
+ break;
+ case 0x25:
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Animation frame");
+ getDelta(vdxData);
+ break;
+ case 0x80:
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Sound resource");
+ chunkSound(vdxData);
+ break;
+ default:
+ error("Groovie::VDX: Invalid resource type: %d", currRes);
+ }
+ delete vdxData;
+ }
+
+ // Wait until the current frame can be shown
+ waitFrame();
+
+ // TODO: Move it to a better place
+ // Update the screen
+ if (currRes == 0x25) {
+ //if (_flagSeven) {
+ //_vm->_graphicsMan->mergeFgAndBg();
+ //}
+ _vm->_graphicsMan->updateScreen(_bg);
+ }
+
+ // Report the end of the video if we reached the end of the file or if we
+ // just wanted to play one frame.
+ return _file->eos() || _flagFirstFrame;
+}
+
+static const uint16 vdxBlockMapLookup[] = {
+0xc800, 0xec80, 0xfec8, 0xffec, 0xfffe, 0x3100, 0x7310, 0xf731, 0xff73, 0xfff7, 0x6c80, 0x36c8, 0x136c, 0x6310, 0xc631, 0x8c63,
+0xf000, 0xff00, 0xfff0, 0x1111, 0x3333, 0x7777, 0x6666, 0xcccc, 0x0ff0, 0x00ff, 0xffcc, 0x0076, 0xff33, 0x0ee6, 0xccff, 0x6770,
+0x33ff, 0x6ee0, 0x4800, 0x2480, 0x1248, 0x0024, 0x0012, 0x2100, 0x4210, 0x8421, 0x0042, 0x0084, 0xf888, 0x0044, 0x0032, 0x111f,
+0x22e0, 0x4c00, 0x888f, 0x4470, 0x2300, 0xf111, 0x0e22, 0x00c4, 0xf33f, 0xfccf, 0xff99, 0x99ff, 0x4444, 0x2222, 0xccee, 0x7733,
+0x00f8, 0x00f1, 0x00bb, 0x0cdd, 0x0f0f, 0x0f88, 0x13f1, 0x19b3, 0x1f80, 0x226f, 0x27ec, 0x3077, 0x3267, 0x37e4, 0x38e3, 0x3f90,
+0x44cf, 0x4cd9, 0x4c99, 0x5555, 0x603f, 0x6077, 0x6237, 0x64c9, 0x64cd, 0x6cd9, 0x70ef, 0x0f00, 0x00f0, 0x0000, 0x4444, 0x2222
+};
+
+void VDXPlayer::getDelta(Common::ReadStream *in) {
+ uint16 j, k, l;
+ uint32 offset;
+ uint8 currOpCode, param1, param2, param3;
+
+ // Get the size of the local palette
+ j = in->readUint16LE();
+
+ // Load the palette if it isn't empty
+ if (j) {
+ uint16 palBitField[16];
+ int flag = 1, palIndex;
+
+ // Load the bit field
+ for (l = 0; l < 16; l++) {
+ palBitField[l] = in->readUint16LE();
+ }
+
+ // Load the actual palette
+ for (l = 0; l < 16; l++) {
+ flag = 1 << 15;
+ for (j = 0; j < 16; j++) {
+ palIndex = (l * 16) + j;
+
+ if (flag & palBitField[l]) {
+ for (k = 0; k < 3; k++) {
+ _palBuf[(palIndex * 3) + k] = in->readByte();
+ }
+ }
+ flag = flag >> 1;
+ }
+ }
+
+ // Apply the palette
+ if (!_flagSix && !_flagSeven) {
+ setPalette(_palBuf);
+ }
+ }
+ currOpCode = in->readByte();
+
+ /* j now becomes the current block line we're dealing with */
+ j = 0;
+ offset = 0;
+ while (!in->eos()) {
+ byte colours[16];
+ if (currOpCode < 0x60) {
+ param1 = in->readByte();
+ param2 = in->readByte();
+ expandColourMap(colours, vdxBlockMapLookup[currOpCode], param1, param2);
+ decodeBlockDelta(offset, colours, 640);
+ offset += TILE_SIZE;
+ } else if (currOpCode > 0x7f) {
+ param1 = in->readByte();
+ param2 = in->readByte();
+ param3 = in->readByte();
+ expandColourMap(colours, (param1 << 8) + currOpCode, param2, param3);
+ decodeBlockDelta(offset, colours, 640);
+ offset += TILE_SIZE;
+ } else switch (currOpCode) {
+ case 0x60: /* Fill tile with the 16 colours given as parameters */
+ for (l = 0; l < 16; l++) {
+ colours[l] = in->readByte();
+ }
+ decodeBlockDelta(offset, colours, 640);
+ offset += TILE_SIZE;
+ break;
+ case 0x61: /* Skip to the end of this line, next block is start of next */
+ /* Note this is used at the end of EVERY line */
+ j++;
+ offset = j * TILE_SIZE * 640;
+ break;
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6a:
+ case 0x6b: /* Skip next param1 blocks (within line) */
+ offset += (currOpCode - 0x62) * TILE_SIZE;
+ break;
+ case 0x6c:
+ case 0x6d:
+ case 0x6e:
+ case 0x6f:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75: /* Next param1 blocks are filled with colour param2 */
+ param1 = currOpCode - 0x6b;
+ param2 = in->readByte();
+ for (l = 0; l < 16; l++) {
+ colours[l] = param2;
+ }
+ for (k = 0; k < param1; k++) {
+ decodeBlockDelta(offset, colours, 640);
+ offset += TILE_SIZE;
+ }
+ break;
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7a:
+ case 0x7b:
+ case 0x7c:
+ case 0x7d:
+ case 0x7e:
+ case 0x7f: /* Next bytes contain colours to fill the next param1 blocks in the current line*/
+ param1 = currOpCode - 0x75;
+ for (k = 0; k < param1; k++) {
+ param2 = in->readByte();
+ for (l = 0; l < 16; l++) {
+ colours[l] = param2;
+ }
+ decodeBlockDelta(offset, colours, 640);
+ offset += TILE_SIZE;
+ }
+ break;
+ default:
+ error("Groovie::VDX: Broken somehow");
+ }
+ currOpCode = in->readByte();
+ }
+}
+
+void VDXPlayer::getStill(Common::ReadStream *in) {
+ uint16 numXTiles = in->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: numXTiles=%d", numXTiles);
+ uint16 numYTiles = in->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: numYTiles=%d", numYTiles);
+
+ // It's skipped in the original:
+ uint16 colourDepth = in->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: colourDepth=%d", colourDepth);
+
+ uint16 imageWidth = TILE_SIZE * numXTiles;
+
+ uint8 mask = 0;
+ byte *buf;
+ if (_flagOne) {
+ // Paint to the foreground
+ buf = (byte *)_fg->getBasePtr(0, 0);
+ if (_flag2Byte) {
+ mask = 0xff;
+ } else {
+ mask = 0;
+ }
+
+ // TODO: Verify this is the right procedure. Couldn't find it on the
+ // disassembly, but it's required to work properly
+ _flagFirstFrame = true;
+ } else {
+ // Paint to the background
+ buf = (byte *)_bg->getBasePtr(0, 0);
+ }
+
+ // Read the palette
+ in->read(_palBuf, 3 * 256);
+
+ if (_flagSeven) {
+ _flagFive = true;
+ }
+
+ // Skip the frame when flag 5 is set, unless flag 1 is set
+ if (!_flagFive || _flagOne) {
+
+ byte colours[16];
+ for (uint16 j = 0; j < numYTiles; j++) {
+ for (uint16 i = 0; i < numXTiles; i++) { /* Tile number */
+ uint8 colour1 = in->readByte();
+ uint8 colour0 = in->readByte();
+ uint16 colourMap = in->readUint16LE();
+ expandColourMap(colours, colourMap, colour1, colour0);
+ decodeBlockStill(buf + j * TILE_SIZE * imageWidth + i * TILE_SIZE, colours, 640, mask);
+ }
+ }
+
+ // Apply the palette
+ if (_flagNine) {
+ // Flag 9 starts a fade in
+ fadeIn(_palBuf);
+ } else {
+ if (!_flagOne && !_flagSeven) {
+ // Actually apply the palette
+ setPalette(_palBuf);
+ }
+ }
+
+ if (!_flagOne) {
+ _vm->_graphicsMan->updateScreen(_bg);
+ }
+ /*
+ if (_flagSix) {
+ if (_flagOne) {
+ _vm->_graphicsMan->updateScreen(_fg);
+ } else {
+ _vm->_graphicsMan->updateScreen(_bg);
+ }
+ _flagSix = 0;
+ }
+ */
+ } else {
+ // Skip the remaining data
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Skipping still frame");
+ while (!in->eos()) {
+ in->readByte();
+ }
+ }
+}
+
+void VDXPlayer::expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0) {
+ int flag = 1 << 15;
+ for (int i = 0; i < 16; i++) {
+ // Set the corresponding colour
+ out[i] = (colourMap & flag) ? colour1 : colour0;
+
+ // Update the flag to test the next colour
+ flag >>= 1;
+ }
+}
+
+void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask) {
+ for (int y = 0; y < TILE_SIZE; y++) {
+ for (int x = 0; x < TILE_SIZE; x++) {
+ if (_flagOne) {
+ // 0xff pixels don't modify the buffer
+ if (*colours != 0xff) {
+ // Write the colour
+ *buf = *colours | mask;
+ // Note: if the mask is 0, it paints the image
+ // else, it paints the image's mask using 0xff
+ }
+ } else {
+ *buf = *colours;
+ }
+
+ // Point to the next colour
+ colours++;
+
+ // Point to the next pixel
+ buf++;
+ }
+
+ // Point to the start of the next line
+ buf += imageWidth - TILE_SIZE;
+ }
+}
+
+void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth) {
+ byte *fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset;
+ //byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset;
+
+ byte *dest;
+ // TODO: Verify just the else block is required
+ //if (_flagOne) {
+ // Paint to the foreground
+ //dest = (byte *)_fg->getBasePtr(0, 0) + offset;
+ //} else {
+ dest = (byte *)_bg->getBasePtr(0, 0) + offset;
+ //}
+
+ int32 off = _origX + _origY * imageWidth;
+ for (int y = 0; y < TILE_SIZE; y++) {
+ for (int x = 0; x < TILE_SIZE; x++) {
+ if (_flagSeven) {
+ if (fgBuf[off] != 0xff) {
+ if (*colours == 0xff) {
+ dest[off] = fgBuf[off];
+ } else {
+ dest[off] = *colours;
+ }
+ }
+ } else {
+ // Paint directly
+ dest[off] = *colours;
+ }
+ colours++;
+ off++;
+ }
+
+ // Prepare the offset of the next line
+ off += imageWidth - TILE_SIZE;
+ }
+}
+
+void VDXPlayer::chunkSound(Common::ReadStream *in) {
+ if (!_audioStream) {
+ _audioStream = Audio::makeAppendableAudioStream(22050, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE);
+ Audio::SoundHandle sound_handle;
+ ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
+ }
+
+ byte *data = new byte[60000];
+ int chunksize = in->read(data, 60000);
+ _audioStream->queueBuffer(data, chunksize);
+}
+
+void VDXPlayer::fadeIn(uint8 *targetpal) {
+ // Don't do anything if we're asked to skip palette changes
+ if (_flagSkipPalette)
+ return;
+
+ // TODO: Is it required? If so, move to an appropiate place
+ // Copy the foreground to the background
+ memcpy((byte *)_vm->_graphicsMan->_foreground.getBasePtr(0, 0), (byte *)_vm->_graphicsMan->_background.getBasePtr(0, 0), 640 * 320);
+
+ // Start a fadein
+ _vm->_graphicsMan->fadeIn(targetpal);
+
+ // Show the background
+ _vm->_graphicsMan->updateScreen(_bg);
+}
+
+void VDXPlayer::setPalette(uint8 *palette) {
+ if (_flagSkipPalette)
+ return;
+
+ uint8 palBuf[4 * 256];
+ debugC(7, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::VDX: Setting palette");
+ for (int i = 0; i < 256; i++) {
+ palBuf[(i * 4) + 0] = palette[(i * 3) + 0];
+ palBuf[(i * 4) + 1] = palette[(i * 3) + 1];
+ palBuf[(i * 4) + 2] = palette[(i * 3) + 2];
+ palBuf[(i * 4) + 3] = 0;
+ }
+ _syst->setPalette(palBuf, 0, 256);
+}
+
+} // End of Groovie namespace
diff --git a/engines/groovie/vdx.h b/engines/groovie/vdx.h
new file mode 100644
index 0000000000..226cc63843
--- /dev/null
+++ b/engines/groovie/vdx.h
@@ -0,0 +1,81 @@
+/* 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$
+ *
+ */
+
+#ifndef GROOVIE_VDX_H
+#define GROOVIE_VDX_H
+
+#include "groovie/player.h"
+
+namespace Groovie {
+
+class VDXPlayer : public VideoPlayer {
+public:
+ VDXPlayer(GroovieEngine *vm);
+ ~VDXPlayer();
+ void setOrigin(int16 x, int16 y);
+
+protected:
+ uint16 loadInternal();
+ bool playFrameInternal();
+
+private:
+ Graphics::Surface *_fg, *_bg;
+ uint8 _palBuf[3 * 256];
+
+ // Origin
+ int16 _origX, _origY;
+
+ // Video flags
+ bool _flagZero;
+ bool _flagOne;
+ bool _flagOnePrev;
+ byte _flag2Byte;
+ bool _flagThree;
+ bool _flagFour;
+ bool _flagFive;
+ bool _flagSix;
+ bool _flagSeven;
+ bool _flagEight;
+ bool _flagNine;
+
+ bool _flagSkipStill;
+ bool _flagSkipPalette;
+ bool _flagFirstFrame;
+ bool _flagTransparent;
+ bool _flagUpdateStill;
+
+ void getStill(Common::ReadStream *in);
+ void getDelta(Common::ReadStream *in);
+ void expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0);
+ void decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask);
+ void decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth);
+ void chunkSound(Common::ReadStream *in);
+ void setPalette(uint8 *palette);
+ void fadeIn(uint8 *palette);
+};
+
+} // End of Groovie namespace
+
+#endif // GROOVIE_VDX_H