aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2016-09-03 10:41:31 +0200
committerGitHub2016-09-03 10:41:31 +0200
commit9d4d4f6803252383b4488638092f004e6cc7a214 (patch)
treeef491b2d3620b91989e5f6c43292b2b5cb87b058
parente93b52416f110e15bd67367d9cfed8fea3851a2c (diff)
parent31e1e02ad6f589d927c361026722cfaf7ac74d94 (diff)
downloadscummvm-rg350-9d4d4f6803252383b4488638092f004e6cc7a214.tar.gz
scummvm-rg350-9d4d4f6803252383b4488638092f004e6cc7a214.tar.bz2
scummvm-rg350-9d4d4f6803252383b4488638092f004e6cc7a214.zip
Merge pull request #807 from blorente/macventure-clean
MACVENTURE: Add MacVenture engine.
-rw-r--r--Makefile.common3
-rw-r--r--devtools/create_macventure/AltBox_act.bmpbin0 -> 822 bytes
-rw-r--r--devtools/create_macventure/AltBox_inac.bmpbin0 -> 822 bytes
-rw-r--r--devtools/create_macventure/InvWindow_act.bmpbin0 -> 16686 bytes
-rw-r--r--devtools/create_macventure/InvWindow_inac.bmpbin0 -> 8414 bytes
-rw-r--r--devtools/create_macventure/NoGrowDoc_act.bmpbin0 -> 8622 bytes
-rw-r--r--devtools/create_macventure/NoGrowDoc_inac.bmpbin0 -> 8622 bytes
-rw-r--r--devtools/create_macventure/PlainDBox_act.bmpbin0 -> 6966 bytes
-rw-r--r--devtools/create_macventure/PlainDBox_inac.bmpbin0 -> 6966 bytes
-rw-r--r--devtools/create_macventure/RDoc4_act.bmpbin0 -> 8694 bytes
-rw-r--r--devtools/create_macventure/RDoc4_inac.bmpbin0 -> 8694 bytes
-rw-r--r--devtools/create_macventure/ZoomDoc_act.bmpbin0 -> 11302 bytes
-rw-r--r--devtools/create_macventure/ZoomDoc_inac.bmpbin0 -> 8622 bytes
-rwxr-xr-xdevtools/create_macventure/create_macventure.sh8
-rw-r--r--engines/macventure/configure.engine3
-rw-r--r--engines/macventure/container.cpp187
-rw-r--r--engines/macventure/container.h81
-rw-r--r--engines/macventure/controls.cpp77
-rw-r--r--engines/macventure/controls.h106
-rw-r--r--engines/macventure/cursor.cpp128
-rw-r--r--engines/macventure/datafiles.cpp88
-rw-r--r--engines/macventure/debug.h38
-rw-r--r--engines/macventure/detection.cpp179
-rw-r--r--engines/macventure/dialog.cpp253
-rw-r--r--engines/macventure/dialog.h123
-rw-r--r--engines/macventure/gui.cpp1464
-rw-r--r--engines/macventure/gui.h369
-rw-r--r--engines/macventure/hufflists.h57
-rw-r--r--engines/macventure/image.cpp553
-rw-r--r--engines/macventure/image.h108
-rw-r--r--engines/macventure/macventure.cpp1185
-rw-r--r--engines/macventure/macventure.h369
-rw-r--r--engines/macventure/module.mk31
-rw-r--r--engines/macventure/prebuilt_dialogs.cpp76
-rw-r--r--engines/macventure/prebuilt_dialogs.h87
-rw-r--r--engines/macventure/saveload.cpp197
-rw-r--r--engines/macventure/script.cpp1251
-rw-r--r--engines/macventure/script.h285
-rw-r--r--engines/macventure/sound.cpp282
-rw-r--r--engines/macventure/sound.h95
-rw-r--r--engines/macventure/stringtable.h109
-rw-r--r--engines/macventure/text.cpp201
-rw-r--r--engines/macventure/text.h64
-rw-r--r--engines/macventure/windows.cpp61
-rw-r--r--engines/macventure/windows.h99
-rw-r--r--engines/macventure/world.cpp350
-rw-r--r--engines/macventure/world.h139
47 files changed, 8706 insertions, 0 deletions
diff --git a/Makefile.common b/Makefile.common
index df24d397de..762b9051d0 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -285,6 +285,9 @@ endif
ifdef ENABLE_WINTERMUTE
DIST_FILES_ENGINEDATA+=wintermute.zip
endif
+ifdef ENABLE_MACVENTURE
+DIST_FILES_ENGINEDATA+=macventure.dat
+endif
DIST_FILES_ENGINEDATA:=$(addprefix $(srcdir)/dists/engine-data/,$(DIST_FILES_ENGINEDATA))
# pred.dic is currently only used for the AGI engine
diff --git a/devtools/create_macventure/AltBox_act.bmp b/devtools/create_macventure/AltBox_act.bmp
new file mode 100644
index 0000000000..aa9fa7e841
--- /dev/null
+++ b/devtools/create_macventure/AltBox_act.bmp
Binary files differ
diff --git a/devtools/create_macventure/AltBox_inac.bmp b/devtools/create_macventure/AltBox_inac.bmp
new file mode 100644
index 0000000000..aa9fa7e841
--- /dev/null
+++ b/devtools/create_macventure/AltBox_inac.bmp
Binary files differ
diff --git a/devtools/create_macventure/InvWindow_act.bmp b/devtools/create_macventure/InvWindow_act.bmp
new file mode 100644
index 0000000000..a38b0a9834
--- /dev/null
+++ b/devtools/create_macventure/InvWindow_act.bmp
Binary files differ
diff --git a/devtools/create_macventure/InvWindow_inac.bmp b/devtools/create_macventure/InvWindow_inac.bmp
new file mode 100644
index 0000000000..f3f2cfaa8b
--- /dev/null
+++ b/devtools/create_macventure/InvWindow_inac.bmp
Binary files differ
diff --git a/devtools/create_macventure/NoGrowDoc_act.bmp b/devtools/create_macventure/NoGrowDoc_act.bmp
new file mode 100644
index 0000000000..3570e798d9
--- /dev/null
+++ b/devtools/create_macventure/NoGrowDoc_act.bmp
Binary files differ
diff --git a/devtools/create_macventure/NoGrowDoc_inac.bmp b/devtools/create_macventure/NoGrowDoc_inac.bmp
new file mode 100644
index 0000000000..f3f97652e5
--- /dev/null
+++ b/devtools/create_macventure/NoGrowDoc_inac.bmp
Binary files differ
diff --git a/devtools/create_macventure/PlainDBox_act.bmp b/devtools/create_macventure/PlainDBox_act.bmp
new file mode 100644
index 0000000000..6521080588
--- /dev/null
+++ b/devtools/create_macventure/PlainDBox_act.bmp
Binary files differ
diff --git a/devtools/create_macventure/PlainDBox_inac.bmp b/devtools/create_macventure/PlainDBox_inac.bmp
new file mode 100644
index 0000000000..6521080588
--- /dev/null
+++ b/devtools/create_macventure/PlainDBox_inac.bmp
Binary files differ
diff --git a/devtools/create_macventure/RDoc4_act.bmp b/devtools/create_macventure/RDoc4_act.bmp
new file mode 100644
index 0000000000..ab78661359
--- /dev/null
+++ b/devtools/create_macventure/RDoc4_act.bmp
Binary files differ
diff --git a/devtools/create_macventure/RDoc4_inac.bmp b/devtools/create_macventure/RDoc4_inac.bmp
new file mode 100644
index 0000000000..ab78661359
--- /dev/null
+++ b/devtools/create_macventure/RDoc4_inac.bmp
Binary files differ
diff --git a/devtools/create_macventure/ZoomDoc_act.bmp b/devtools/create_macventure/ZoomDoc_act.bmp
new file mode 100644
index 0000000000..b2ccc9c602
--- /dev/null
+++ b/devtools/create_macventure/ZoomDoc_act.bmp
Binary files differ
diff --git a/devtools/create_macventure/ZoomDoc_inac.bmp b/devtools/create_macventure/ZoomDoc_inac.bmp
new file mode 100644
index 0000000000..963949b392
--- /dev/null
+++ b/devtools/create_macventure/ZoomDoc_inac.bmp
Binary files differ
diff --git a/devtools/create_macventure/create_macventure.sh b/devtools/create_macventure/create_macventure.sh
new file mode 100755
index 0000000000..1408179039
--- /dev/null
+++ b/devtools/create_macventure/create_macventure.sh
@@ -0,0 +1,8 @@
+printf "Creating border file...\n"
+
+zip -r macventure.zip *.bmp
+mv macventure.zip macventure.dat
+
+echo done
+
+ls -l macventure.dat
diff --git a/engines/macventure/configure.engine b/engines/macventure/configure.engine
new file mode 100644
index 0000000000..dc7cf7912c
--- /dev/null
+++ b/engines/macventure/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine macventure "MacVenture" no
diff --git a/engines/macventure/container.cpp b/engines/macventure/container.cpp
new file mode 100644
index 0000000000..ff98d6961f
--- /dev/null
+++ b/engines/macventure/container.cpp
@@ -0,0 +1,187 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/container.h"
+
+namespace MacVenture {
+
+Container::Container(Common::String filename) {
+ _filename = filename;
+
+ if (!_file.open(_filename)) {
+ error("CONTAINER: Could not open %s", _filename.c_str());
+ }
+
+ _res = _file.readStream(_file.size());
+ _header = _res->readUint32BE();
+ _simplified = false;
+
+ for (uint i = 0; i < 16; ++i) {
+ _huff.push_back(0);
+ }
+
+ for (uint i = 0; i < 16; ++i) {
+ _lens.push_back(0);
+ }
+
+ if (!(_header & 0x80000000)) {
+ // Is simplified container
+ _simplified = true;
+ int dataLen = _res->size() - sizeof(_header);
+ _lenObjs = _header;
+ _numObjs = dataLen / _lenObjs;
+ } else {
+ _header &= 0x7fffffff;
+ _res->seek(_header, SEEK_SET);
+ _numObjs = _res->readUint16BE();
+
+ for (uint i = 0; i < 15; ++i) {
+ _huff[i] = _res->readUint16BE();
+ }
+
+ for (uint i = 0; i < 16; ++i) {
+ _lens[i] = _res->readByte();
+ }
+
+ // Read groups
+ uint numGroups = _numObjs / 64;
+ if ((_numObjs % 64) > 0) {
+ numGroups++;
+ }
+
+ for (uint i = 0; i < numGroups; ++i) {
+ ItemGroup group;
+
+ // Place myself in the correct position to read group
+ _res->seek(_header + (i * 6) + 0x30, SEEK_SET);
+ byte b1, b2, b3;
+ b1 = _res->readByte();
+ b2 = _res->readByte();
+ b3 = _res->readByte();
+ group.bitOffset = (b1 << 16) + (b2 << 8) + (b3 << 0);
+
+ b1 = _res->readByte();
+ b2 = _res->readByte();
+ b3 = _res->readByte();
+ group.offset = (b1 << 16) + (b2 << 8) + (b3 << 0);
+
+ // Place the bit reader in the correct position
+ // group.bitOffset indicates the offset from the start of the subHeader
+ _res->seek(_header + (group.bitOffset >> 3), SEEK_SET);
+ uint32 bits = group.bitOffset & 7;
+
+ for (uint j = 0; j < 64; ++j) {
+ uint32 length = 0;
+ uint32 mask = 0;
+ mask = _res->readUint32BE();
+ mask >>= (16 - bits);
+ mask &= 0xFFFF;
+ debugC(4, kMVDebugContainer, "Load mask of object &%d:%d is %x", i, j, mask);
+ _res->seek(-4, SEEK_CUR);
+ // Look in the Huffman table
+ int x = 0;
+ for (x = 0; x < 16; x++) {
+ if (_huff[x] > mask) {
+ break;
+ }
+ }
+
+ // I will opt to copy the code from webventure,
+ // But according to the docs, this call should suffice:
+ // length = bitStream.getBits(_lens[x]);
+ // The problem is that _lens[] usually contains values larger
+ // Than 32, so we have to read them with the method below
+
+ //This code below, taken from the implementation, seems to give the same results.
+
+ uint32 bitSize = _lens[x];
+ bits += bitSize & 0xF;
+ if (bits & 0x10) {
+ bits &= 0xF;
+ _res->seek(2, SEEK_CUR);
+ }
+ bitSize >>= 4;
+ if (bitSize) {
+ length = _res->readUint32BE();
+ _res->seek(-4, SEEK_CUR);
+ bitSize--;
+ if (bitSize == 0) {
+ length = 0;
+ } else {
+ length >>= (32 - bitSize) - bits;
+ }
+ length &= (1 << bitSize) - 1;
+ length |= 1 << bitSize;
+ bits += bitSize;
+ if (bits & 0x10) {
+ bits &= 0xF;
+ _res->seek(2, SEEK_CUR);
+ }
+ }
+ group.lengths[j] = length;
+ debugC(4, kMVDebugContainer, "Load legth of object %d:%d is %d", i, j, length);
+ }
+ _groups.push_back(group);
+ }
+ }
+}
+
+Container::~Container() {
+
+ if (_file.isOpen())
+ _file.close();
+
+ if (_res)
+ delete _res;
+}
+
+uint32 Container::getItemByteSize(uint32 id) {
+ if (_simplified) {
+ return _lenObjs;
+ } else {
+ uint32 groupID = (id >> 6);
+ uint32 objectIndex = id & 0x3f; // Index within the group
+ return _groups[groupID].lengths[objectIndex];
+ }
+}
+
+Common::SeekableReadStream *Container::getItem(uint32 id) {
+ if (_simplified) {
+ _res->seek((id * _lenObjs) + sizeof(_header), SEEK_SET);
+ } else {
+ uint32 groupID = (id >> 6);
+ uint32 objectIndex = id & 0x3f; // Index within the group
+
+ uint32 offset = 0;
+ for (uint i = 0; i < objectIndex; i++) {
+ offset += _groups[groupID].lengths[i];
+ }
+ _res->seek(_groups[groupID].offset + offset + sizeof(_header), SEEK_SET);
+ }
+
+ // HACK Should Limit the size of the stream returned
+ Common::SeekableReadStream *res = _res->readStream(_res->size() - _res->pos() + 1);
+ return res;
+}
+
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/container.h b/engines/macventure/container.h
new file mode 100644
index 0000000000..e630bdf1be
--- /dev/null
+++ b/engines/macventure/container.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.
+*
+*/
+
+#ifndef MACVENTURE_CONTAINER_H
+#define MACVENTURE_CONTAINER_H
+
+#include "macventure/macventure.h"
+
+#include "common/file.h"
+#include "common/fs.h"
+#include "common/bitstream.h"
+
+namespace MacVenture {
+
+struct ItemGroup {
+ uint32 bitOffset; //It's really uint24
+ uint32 offset; //It's really uint24
+ uint32 lengths[64];
+};
+
+typedef uint32 ContainerHeader;
+
+class Container {
+
+public:
+ Container(Common::String filename);
+ ~Container();
+
+public:
+ /**
+ * Must be called before retrieving an object.
+ */
+ uint32 getItemByteSize(uint32 id);
+
+ /**
+ * getItemByteSize should be called before this one
+ */
+ Common::SeekableReadStream *getItem(uint32 id);
+
+protected:
+
+ bool _simplified;
+
+ uint _lenObjs; // In the case of simple container, lenght of an object
+ uint _numObjs;
+
+ ContainerHeader _header;
+
+ Common::Array<uint16> _huff; // huffman masks
+ Common::Array<uint8> _lens; // huffman lengths
+ Common::Array<ItemGroup> _groups;
+
+ Common::String _filename;
+ Common::File _file;
+ Common::SeekableReadStream *_res;
+
+};
+
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/controls.cpp b/engines/macventure/controls.cpp
new file mode 100644
index 0000000000..078ea88dce
--- /dev/null
+++ b/engines/macventure/controls.cpp
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/gui.h"
+
+namespace MacVenture {
+CommandButton::CommandButton() {
+ _gui = NULL;
+}
+
+CommandButton::CommandButton(ControlData data, Gui *g) {
+ _data = data;
+ _gui = g;
+ _selected = false;
+}
+
+void CommandButton::draw(Graphics::ManagedSurface &surface) const {
+
+ uint colorFill = _selected ? kColorBlack : kColorWhite;
+ uint colorText = _selected ? kColorWhite : kColorBlack;
+
+ surface.fillRect(_data.bounds, colorFill);
+ surface.frameRect(_data.bounds, kColorBlack);
+
+ if (_data.titleLength > 0) {
+ const Graphics::Font &font = _gui->getCurrentFont();
+ Common::String title(_data.title);
+ font.drawString(
+ &surface,
+ title,
+ _data.bounds.left,
+ _data.bounds.top,
+ _data.bounds.right - _data.bounds.left,
+ colorText,
+ Graphics::kTextAlignCenter);
+ }
+}
+
+bool CommandButton::isInsideBounds(const Common::Point point) const {
+ return _data.bounds.contains(point);
+}
+
+const ControlData &CommandButton::getData() const {
+ return _data;
+}
+
+void CommandButton::select() {
+ _selected = true;
+}
+
+void CommandButton::unselect() {
+ _selected = false;
+}
+
+bool CommandButton::isSelected() {
+ return _selected;
+}
+}
diff --git a/engines/macventure/controls.h b/engines/macventure/controls.h
new file mode 100644
index 0000000000..81b5278396
--- /dev/null
+++ b/engines/macventure/controls.h
@@ -0,0 +1,106 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_CONTROLS_H
+#define MACVENTURE_CONTROLS_H
+
+namespace MacVenture {
+
+class Gui;
+
+enum ControlType { // HACK, should correspond exactly with the types of controls (sliders etc)
+ kControlExitBox = 0,
+ kControlExamine = 1,
+ kControlOpen = 2,
+ kControlClose = 3,
+ kControlSpeak = 4,
+ kControlOperate = 5,
+ kControlGo = 6,
+ kControlHit = 7,
+ kControlConsume = 8,
+ kControlClickToContinue = 9
+};
+
+enum ControlAction {
+ kNoCommand = 0,
+ kStartOrResume = 1,
+ kClose = 2,
+ kTick = 3,
+ kActivateObject = 4,
+ kMoveObject = 5,
+ kConsume = 6,
+ kExamine = 7,
+ kGo = 8,
+ kHit = 9,
+ kOpen = 10,
+ kOperate = 11,
+ kSpeak = 12,
+ kBabble = 13,
+ kTargetName = 14,
+ kDebugObject = 15,
+ kClickToContinue = 16
+};
+
+struct ControlData {
+ Common::Rect bounds;
+ uint16 scrollValue;
+ uint8 visible;
+ uint16 scrollMax;
+ uint16 scrollMin;
+ uint16 cdef;
+ ControlAction refcon;
+ ControlType type;
+ uint8 titleLength;
+ Common::String title;
+ uint16 border;
+};
+
+class CommandButton {
+
+enum {
+ kCommandsLeftPadding = 0,
+ kCommandsTopPadding = 0
+};
+
+public:
+
+ CommandButton();
+
+ CommandButton(ControlData data, Gui *g);
+ ~CommandButton() {}
+
+ void draw(Graphics::ManagedSurface &surface) const;
+ bool isInsideBounds(const Common::Point point) const;
+ const ControlData &getData() const;
+ void select();
+ void unselect();
+ bool isSelected();
+
+private:
+ bool _selected;
+ ControlData _data;
+ Gui *_gui;
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/cursor.cpp b/engines/macventure/cursor.cpp
new file mode 100644
index 0000000000..655615a523
--- /dev/null
+++ b/engines/macventure/cursor.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.
+*
+*/
+
+#include "macventure/gui.h"
+
+namespace MacVenture {
+
+static void cursorTimerHandler(void *refCon);
+
+static const ClickState _transitionTable[kCursorStateCount][kCursorInputCount] = {
+ /* Button down, Button Up, Tick */
+ /* Idle */ {kCursorSCStart, kCursorIdle, kCursorIdle },
+ /* SC Start */ {kCursorSCStart, kCursorDCStart, kCursorSCDrag},
+ /* SC Do */ {kCursorSCDrag, kCursorIdle, kCursorSCDrag},
+ /* DC Start */ {kCursorDCDo, kCursorDCStart, kCursorSCSink},
+ /* DC Do */ {kCursorDCDo, kCursorIdle, kCursorDCDo },
+ /* SC Sink */ {kCursorIdle, kCursorIdle, kCursorIdle }
+};
+
+Cursor::Cursor(Gui *gui) {
+ _gui = gui;
+ _state = kCursorIdle;
+}
+Cursor::~Cursor() {}
+
+void Cursor::tick() {
+ changeState(kTickCol);
+}
+
+bool Cursor::processEvent(const Common::Event &event) {
+ if (event.type == Common::EVENT_MOUSEMOVE) {
+ _pos = event.mouse;
+ return true;
+ }
+ if (event.type == Common::EVENT_LBUTTONDOWN) {
+ changeState(kButtonDownCol);
+ return true;
+ }
+ if (event.type == Common::EVENT_LBUTTONUP) {
+ changeState(kButtonUpCol);
+ return true;
+ }
+
+ return false;
+}
+
+Common::Point Cursor::getPos() {
+ return _pos;
+}
+
+bool Cursor::canSelectDraggable() {
+ return _state == kCursorSCDrag;
+}
+
+void Cursor::changeState(CursorInput input) {
+ debugC(3, kMVDebugGUI, "Change cursor state: [%d] -> [%d]", _state, _transitionTable[_state][input]);
+ if (_state != _transitionTable[_state][input]) {
+ executeStateOut();
+ _state = _transitionTable[_state][input];
+ executeStateIn();
+ }
+}
+
+void Cursor::executeStateIn() {
+ switch (_state) {
+ case kCursorSCStart:
+ g_system->getTimerManager()->installTimerProc(&cursorTimerHandler, 300000, this, "macVentureCursor");
+ _gui->selectForDrag(_pos);
+ break;
+ case kCursorDCStart:
+ g_system->getTimerManager()->installTimerProc(&cursorTimerHandler, 300000, this, "macVentureCursor");
+ break;
+ case kCursorSCSink:
+ _gui->handleSingleClick();
+ changeState(kTickCol);
+ break;
+ default:
+ break;
+ }
+}
+
+void Cursor::executeStateOut() {
+ switch (_state) {
+ case kCursorIdle:
+ break;
+ case kCursorSCStart:
+ g_system->getTimerManager()->removeTimerProc(&cursorTimerHandler);
+ break;
+ case kCursorSCDrag:
+ _gui->handleSingleClick();
+ break;
+ case kCursorDCStart:
+ g_system->getTimerManager()->removeTimerProc(&cursorTimerHandler);
+ break;
+ case kCursorDCDo:
+ _gui->handleDoubleClick();
+ break;
+ default:
+ break;
+ }
+}
+
+static void cursorTimerHandler(void *refCon) {
+ Cursor *cursor = (Cursor *)refCon;
+ cursor->tick();
+}
+
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/datafiles.cpp b/engines/macventure/datafiles.cpp
new file mode 100644
index 0000000000..2c17b66209
--- /dev/null
+++ b/engines/macventure/datafiles.cpp
@@ -0,0 +1,88 @@
+/* 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.
+ *
+ */
+
+#ifndef MACVENTURE_DATAFILES_H
+#define MACVENTURE_DATAFILES_H
+
+#include "macventure/macventure.h"
+#include "macventure/windows.h"
+
+#include "common/unzip.h"
+
+namespace MacVenture {
+
+#define MACVENTURE_DATA_BUNDLE Common::String("macventure.dat")
+
+struct BorderName {
+ MVWindowType type;
+ const char *name;
+};
+
+static const BorderName g_borderNames[] = {
+ {kDocument, "Document"},
+ {kDBox, "DBox"},
+ {kPlainDBox, "PlainDBox"},
+ {kAltBox, "AltBox"},
+ {kNoGrowDoc, "NoGrowDoc"},
+ {kMovableDBox, "MovableDBox"},
+ {kZoomDoc, "ZoomDoc"},
+ {kZoomNoGrow, "ZoomNoGrow"},
+ {kInvWindow, "InvWindow"},
+ {kRDoc16, "RDoc16"},
+ {kRDoc4, "RDoc4"},
+ {kRDoc6, "RDoc6"},
+ {kRDoc10, "RDoc10"},
+ {kNoType, "No type"}
+};
+
+Common::String windowTypeName(MVWindowType windowType) {
+ int i = 0;
+ while (g_borderNames[i].type != kNoType) {
+ i++;
+ if (g_borderNames[i].type == windowType) {
+ return g_borderNames[i].name;
+ }
+ }
+ return "";
+}
+
+void MacVentureEngine::loadDataBundle() {
+ _dataBundle = Common::makeZipArchive(MACVENTURE_DATA_BUNDLE);
+ if (!_dataBundle) {
+ error("ENGINE: Couldn't load data bundle '%s'.", MACVENTURE_DATA_BUNDLE.c_str());
+ }
+}
+
+Common::SeekableReadStream *MacVentureEngine::getBorderFile(MVWindowType windowType, bool isActive) {
+ Common::String filename = windowTypeName(windowType);
+ filename += (isActive ? "_act.bmp" : "_inac.bmp");
+ if (!_dataBundle->hasFile(filename)) {
+ warning("Missing border file '%s' in data bundle", filename.c_str());
+ return NULL;
+ }
+
+ return _dataBundle->createReadStreamForMember(filename);
+}
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/debug.h b/engines/macventure/debug.h
new file mode 100644
index 0000000000..c223d01fe4
--- /dev/null
+++ b/engines/macventure/debug.h
@@ -0,0 +1,38 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_DEBUG_H
+#define MACVENTURE_DEBUG_H
+
+namespace MacVenture {
+enum MacVentureDebugChannel {
+ kMVDebugMain = 1 << 0,
+ kMVDebugGUI = 1 << 1,
+ kMVDebugImage = 1 << 2,
+ kMVDebugText = 1 << 3,
+ kMVDebugScript = 1 << 4,
+ kMVDebugSound = 1 << 5,
+ kMVDebugContainer = 1 << 6
+};
+} // End namespace MacVenture
+
+#endif
diff --git a/engines/macventure/detection.cpp b/engines/macventure/detection.cpp
new file mode 100644
index 0000000000..10676d5b25
--- /dev/null
+++ b/engines/macventure/detection.cpp
@@ -0,0 +1,179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+
+#include "macventure/macventure.h"
+
+namespace MacVenture {
+
+#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_MACRESFORK)
+
+#define BASEGAME(n, v, f, md5, s) {n, v, AD_ENTRY1s(f, md5, s), Common::EN_ANY, Common::kPlatformMacintosh, ADGF_DEFAULT, GUIO0()}
+
+static const ADGameDescription gameDescriptions[] = {
+ BASEGAME("shadowgate", "Zojoi Rerelease", "Shadowgate.bin", "ebbfbcbf93938bd2900cb0c0213b19ad", 68974), // Zojoi Rerelease
+ BASEGAME("deja_vu", "Zojoi Rerelease", "Deja Vu.bin", "5e9f5a8e3c8eb29ed02b34ae5937354f", 69034), // Zojoi Rerelease
+ BASEGAME("deja_vu2", "Zojoi Rerelease", "Lost in Las Vegas.bin", "8f8e1d8d41f577ee0fbc03847969af0d", 66520), // Zojoi Rerelease
+ AD_TABLE_END_MARKER
+};
+
+const char *MacVentureEngine::getGameFileName() const {
+ return _gameDescription->filesDescriptions[0].fileName;
+}
+} // End of namespace MacVenture
+
+static const PlainGameDescriptor macventureGames[] = {
+ { "shadowgate", "Shadowgate" },
+ { "deja_vu", "Deja Vu"},
+ { "deja_vu2", "Deja Vu II"},
+ { 0, 0 }
+};
+
+namespace MacVenture {
+
+SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot);
+
+class MacVentureMetaEngine : public AdvancedMetaEngine {
+public:
+ MacVentureMetaEngine() : AdvancedMetaEngine(MacVenture::gameDescriptions, sizeof(ADGameDescription), macventureGames) {
+ _guiOptions = GUIO0();
+ _md5Bytes = 5000000; // TODO: Upper limit, adjust it once all games are added
+ }
+
+ virtual const char *getName() const override {
+ return "MacVenture";
+ }
+ virtual const char *getOriginalCopyright() const override {
+ return "(C) ICOM Simulations";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool MacVentureMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime);
+}
+
+bool MacVentureEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+SaveStateList MacVentureMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += ".###";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ int slotNum = atoi(file->c_str() + file->size() - 3);
+ SaveStateDescriptor desc;
+ // Do not allow save slot 0 (used for auto-saving) to be deleted or
+ // overwritten.
+ desc.setDeletableFlag(slotNum != 0);
+ desc.setWriteProtectedFlag(slotNum == 0);
+
+ if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(*file);
+ if (in) {
+ desc = loadMetaData(in, slotNum);
+ if (desc.getSaveSlot() != slotNum) {
+ // invalid
+ delete in;
+ continue;
+ }
+ saveList.push_back(desc);
+ delete in;
+ }
+ }
+ }
+
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+ return saveList;
+}
+
+int MacVentureMetaEngine::getMaximumSaveSlot() const { return 999; }
+
+bool MacVentureMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *game) const {
+ if (game) {
+ *engine = new MacVenture::MacVentureEngine(syst, game);
+ }
+ return game != 0;
+}
+
+void MacVentureMetaEngine::removeSaveState(const char *target, int slot) const {
+ g_system->getSavefileManager()->removeSavefile(Common::String::format("%s.%03d", target, slot));
+}
+
+
+SaveStateDescriptor MacVentureMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ SaveStateDescriptor desc;
+ Common::String saveFileName;
+ Common::String pattern = target;
+ pattern += ".###";
+ Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ int slotNum = atoi(file->c_str() + file->size() - 3);
+ if (slotNum == slot) {
+ saveFileName = *file;
+ }
+ }
+
+ Common::InSaveFile *in = saveFileMan->openForLoading(saveFileName);
+ if (in) {
+ desc = loadMetaData(in, slot);
+ delete in;
+ return desc;
+ }
+ return SaveStateDescriptor(-1, "");
+}
+
+} // End of namespace MacVenture
+
+#if PLUGIN_ENABLED_DYNAMIC(MACVENTURE)
+ REGISTER_PLUGIN_DYNAMIC(MACVENTURE, PLUGIN_TYPE_ENGINE, MacVenture::MacVentureMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(MACVENTURE, PLUGIN_TYPE_ENGINE, MacVenture::MacVentureMetaEngine);
+#endif
diff --git a/engines/macventure/dialog.cpp b/engines/macventure/dialog.cpp
new file mode 100644
index 0000000000..7827bddffd
--- /dev/null
+++ b/engines/macventure/dialog.cpp
@@ -0,0 +1,253 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/system.h"
+
+#include "macventure/dialog.h"
+namespace MacVenture {
+
+
+Dialog::Dialog(Gui *gui, Common::Point pos, uint width, uint height) :
+ _gui(gui), _bounds(Common::Rect(pos.x, pos.y, pos.x + width, pos.y + height)) {}
+
+Dialog::Dialog(Gui *gui, PrebuiltDialogs prebuilt) {
+ _gui = gui;
+ const PrebuiltDialog &dialog = g_prebuiltDialogs[prebuilt];
+ calculateBoundsFromPrebuilt(dialog.bounds);
+ for (int i = 0; dialog.elements[i].type != kDEEnd; i++) {
+ addPrebuiltElement(dialog.elements[i]);
+ }
+}
+
+Dialog::~Dialog() {
+ for (Common::Array<DialogElement*>::iterator it = _elements.begin(); it != _elements.end(); it++) {
+ delete *it;
+ }
+}
+
+void Dialog::handleDialogAction(DialogElement *trigger, DialogAction action) {
+ switch(action) {
+ case kDACloseDialog:
+ _gui->closeDialog();
+ break;
+ case kDASubmit:
+ _gui->setTextInput(_userInput);
+ _gui->closeDialog();
+ break;
+ case kDASaveAs:
+ _gui->saveGame();
+ _gui->closeDialog();
+ break;
+ case kDALoadGame:
+ _gui->loadGame();
+ _gui->closeDialog();
+ break;
+ case kDANewGame:
+ _gui->newGame();
+ _gui->closeDialog();
+ break;
+ case kDAQuit:
+ _gui->quitGame();
+ _gui->closeDialog();
+ break;
+ default:
+ break;
+ }
+}
+
+const Graphics::Font &Dialog::getFont() {
+ return _gui->getCurrentFont();
+}
+
+bool Dialog::processEvent(Common::Event event) {
+ for (Common::Array<DialogElement*>::iterator it = _elements.begin(); it != _elements.end(); it++) {
+ if ((*it)->processEvent(this, event)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Dialog::addButton(Common::String title, MacVenture::DialogAction action, Common::Point position, uint width, uint height) {
+ _elements.push_back(new DialogButton(this, title, action, position, width, height));
+}
+
+void Dialog::addText(Common::String content, Common::Point position) {
+ _elements.push_back(new DialogPlainText(this, content, position));
+}
+
+void Dialog::addTextInput(Common::Point position, int width, int height) {
+ _elements.push_back(new DialogTextInput(this, position, width, height));
+}
+
+void Dialog::draw() {
+ Graphics::ManagedSurface compose;
+ // Compose the surface
+ compose.create(_bounds.width(), _bounds.height());
+ Common::Rect base(0, 0, _bounds.width(), _bounds.height());
+ compose.fillRect(base, kColorWhite);
+ compose.frameRect(base, kColorBlack);
+ for (Common::Array<DialogElement*>::iterator it = _elements.begin(); it != _elements.end(); it++) {
+ (*it)->draw(this, compose);
+ }
+
+ g_system->copyRectToScreen(compose.getPixels(), compose.pitch,
+ _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
+ }
+
+void Dialog::localize(Common::Point &point) {
+ point.x -= _bounds.left;
+ point.y -= _bounds.top;
+}
+
+void Dialog::setUserInput(Common::String content) {
+ _userInput = content;
+}
+
+void Dialog::addPrebuiltElement(const MacVenture::PrebuiltDialogElement &element) {
+ Common::Point position(element.left, element.top);
+ switch(element.type) {
+ case kDEButton:
+ addButton(element.title, element.action, position, element.width, element.height);
+ break;
+ case kDEPlainText:
+ addText(element.title, position);
+ break;
+ case kDETextInput:
+ addTextInput(position, element.width, element.height);
+ break;
+ default:
+ break;
+ }
+}
+
+// Dialog Element
+
+DialogElement::DialogElement(Dialog *dialog, Common::String title, DialogAction action, Common::Point position, uint width, uint height) :
+ _text(title), _action(action) {
+ if (width == 0) {
+ width = dialog->getFont().getStringWidth(title);
+ }
+ if (height == 0) {
+ height = dialog->getFont().getFontHeight();
+ }
+ _bounds = Common::Rect(position.x, position.y, position.x + width, position.y + height);
+}
+
+bool DialogElement::processEvent(MacVenture::Dialog *dialog, Common::Event event) {
+ return doProcessEvent(dialog, event);
+}
+
+void DialogElement::draw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target) {
+ doDraw(dialog, target);
+}
+
+const Common::String &DialogElement::getText() {
+ return doGetText();
+}
+
+const Common::String &DialogElement::doGetText() {
+ return _text;
+}
+
+// CONCRETE DIALOG ELEMENTS
+
+DialogButton::DialogButton(Dialog *dialog, Common::String title, DialogAction action, Common::Point position, uint width, uint height):
+ DialogElement(dialog, title, action, position, width, height) {}
+
+bool DialogButton::doProcessEvent(MacVenture::Dialog *dialog, Common::Event event) {
+ Common::Point mouse = event.mouse;
+ if (event.type == Common::EVENT_LBUTTONDOWN) {
+ dialog->localize(mouse);
+ if (_bounds.contains(mouse)) {
+ debugC(2, kMVDebugGUI, "Click! Button: %s", _text.c_str());
+ dialog->handleDialogAction(this, _action);
+ return true;
+ }
+ }
+ return false;
+}
+
+void DialogButton::doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target) {
+ target.fillRect(_bounds, kColorWhite);
+ target.frameRect(_bounds, kColorBlack);
+ // Draw title
+ dialog->getFont().drawString(
+ &target, _text, _bounds.left, _bounds.top, _bounds.width(), kColorBlack, Graphics::kTextAlignCenter);
+}
+
+DialogPlainText::DialogPlainText(Dialog *dialog, Common::String content, Common::Point position) :
+ DialogElement(dialog, content, kDANone, position, 0, 0) { }
+
+DialogPlainText::~DialogPlainText() {}
+
+bool DialogPlainText::doProcessEvent(MacVenture::Dialog *dialog, Common::Event event) {
+ return false;
+}
+
+void DialogPlainText::doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target) {
+ // Draw contents
+ dialog->getFont().drawString(
+ &target, _text, _bounds.left, _bounds.top, _bounds.width(), kColorBlack, Graphics::kTextAlignCenter);
+
+}
+
+DialogTextInput::DialogTextInput(Dialog *dialog, Common::Point position, uint width, uint height) :
+ DialogElement(dialog, "", kDANone, position, width, height) {}
+DialogTextInput::~DialogTextInput() {}
+
+bool DialogTextInput::doProcessEvent(Dialog *dialog, Common::Event event) {
+ if (event.type == Common::EVENT_KEYDOWN) {
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_BACKSPACE:
+ if (!_text.empty()) {
+ _text.deleteLastChar();
+ dialog->setUserInput(_text);
+ return true;
+ }
+ break;
+ default:
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) {
+ _text += (char)event.kbd.ascii;
+ dialog->setUserInput(_text);
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+void DialogTextInput::doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target) {
+ target.fillRect(_bounds, kColorWhite);
+ target.frameRect(_bounds, kColorBlack);
+ dialog->getFont().drawString(&target, _text, _bounds.left, _bounds.top, _bounds.width(), kColorBlack);
+}
+
+void Dialog::calculateBoundsFromPrebuilt(const PrebuiltDialogBounds &bounds) {
+ _bounds = Common::Rect(
+ bounds.left,
+ bounds.top,
+ bounds.right,
+ bounds.bottom);
+}
+} // End of namespace MacVenture
diff --git a/engines/macventure/dialog.h b/engines/macventure/dialog.h
new file mode 100644
index 0000000000..67331d7eb3
--- /dev/null
+++ b/engines/macventure/dialog.h
@@ -0,0 +1,123 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_DIALOG_H
+#define MACVENTURE_DIALOG_H
+
+#include "graphics/macgui/macwindowmanager.h"
+
+#include "macventure/macventure.h"
+#include "macventure/prebuilt_dialogs.h"
+
+namespace MacVenture {
+
+using namespace Graphics::MacGUIConstants;
+class Gui;
+class DialogElement;
+
+class Dialog {
+public:
+ Dialog(Gui *gui, Common::Point pos, uint width, uint height);
+ Dialog(Gui *gui, PrebuiltDialogs prebuilt);
+
+ ~Dialog();
+
+ bool processEvent(Common::Event event);
+ void draw();
+ void localize(Common::Point &point);
+ void handleDialogAction(DialogElement *trigger, DialogAction action);
+
+ const Graphics::Font &getFont();
+
+ void addButton(Common::String title, DialogAction action, Common::Point position, uint width = 0, uint height = 0);
+ void addText(Common::String content, Common::Point position);
+ void addTextInput(Common::Point position, int width, int height);
+
+ void setUserInput(Common::String content);
+
+private:
+ void addPrebuiltElement(const PrebuiltDialogElement &element);
+
+ void calculateBoundsFromPrebuilt(const PrebuiltDialogBounds &bounds);
+
+private:
+ Gui *_gui;
+
+ Common::String _userInput;
+ Common::Array<DialogElement*> _elements;
+ Common::Rect _bounds;
+};
+
+class DialogElement {
+public:
+ DialogElement(Dialog *dialog, Common::String title, DialogAction action, Common::Point position, uint width = 0, uint height = 0);
+ virtual ~DialogElement() {}
+
+ bool processEvent(Dialog *dialog, Common::Event event);
+ void draw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target);
+ const Common::String &getText();
+
+private:
+ virtual bool doProcessEvent(Dialog *dialog, Common::Event event) = 0;
+ virtual void doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target) = 0;
+ virtual const Common::String &doGetText();
+
+protected:
+ Common::String _text;
+ Common::Rect _bounds;
+ DialogAction _action;
+};
+
+// Dialog elements
+class DialogButton : public DialogElement {
+public:
+ DialogButton(Dialog *dialog, Common::String title, DialogAction action, Common::Point position, uint width = 0, uint height = 0);
+ ~DialogButton() {}
+
+private:
+ bool doProcessEvent(Dialog *dialog, Common::Event event);
+ void doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target);
+};
+
+class DialogPlainText : public DialogElement {
+public:
+ DialogPlainText(Dialog *dialog, Common::String content, Common::Point position);
+ ~DialogPlainText();
+
+private:
+ bool doProcessEvent(Dialog *dialog, Common::Event event);
+ void doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target);
+};
+
+class DialogTextInput : public DialogElement {
+public:
+ DialogTextInput(Dialog *dialog, Common::Point position, uint width, uint height);
+ ~DialogTextInput();
+
+private:
+ bool doProcessEvent(Dialog *dialog, Common::Event event);
+ void doDraw(MacVenture::Dialog *dialog, Graphics::ManagedSurface &target);
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/gui.cpp b/engines/macventure/gui.cpp
new file mode 100644
index 0000000000..dff41b3c23
--- /dev/null
+++ b/engines/macventure/gui.cpp
@@ -0,0 +1,1464 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/file.h"
+#include "common/system.h"
+#include "common/debug-channels.h"
+#include "common/debug.h"
+#include "image/bmp.h"
+
+#include "macventure/gui.h"
+#include "macventure/dialog.h"
+
+namespace MacVenture {
+
+enum MenuAction;
+
+enum {
+ kCursorWidth = 2,
+ kCursorHeight = 2
+};
+
+enum {
+ kExitButtonWidth = 10,
+ kExitButtonHeight = 10
+};
+
+enum {
+ kMenuHighLevel = -1,
+ kMenuAbout = 0,
+ kMenuFile = 1,
+ kMenuEdit = 2,
+ kMenuSpecial = 3
+};
+
+enum {
+ kCommandNum = 8
+};
+
+enum {
+ kDragThreshold = 5
+};
+
+const bool kLoadStaticMenus = true;
+
+static const Graphics::MenuData menuSubItems[] = {
+ { kMenuHighLevel, "File", 0, 0, false },
+ { kMenuHighLevel, "Edit", 0, 0, false },
+ { kMenuHighLevel, "Special", 0, 0, false },
+ { kMenuHighLevel, "Font", 0, 0, false },
+ { kMenuHighLevel, "FontSize", 0, 0, false },
+
+ //{ kMenuAbout, "About", kMenuActionAbout, 0, true},
+
+ { kMenuFile, "New", kMenuActionNew, 0, true },
+ { kMenuFile, NULL, 0, 0, false },
+ { kMenuFile, "Open...", kMenuActionOpen, 0, true },
+ { kMenuFile, "Save", kMenuActionSave, 0, true },
+ { kMenuFile, "Save as...", kMenuActionSaveAs, 0, true },
+ { kMenuFile, NULL, 0, 0, false },
+ { kMenuFile, "Quit", kMenuActionQuit, 0, true },
+
+ { kMenuEdit, "Undo", kMenuActionUndo, 'Z', false },
+ { kMenuEdit, NULL, 0, 0, false },
+ { kMenuEdit, "Cut", kMenuActionCut, 'K', false },
+ { kMenuEdit, "Copy", kMenuActionCopy, 'C', false },
+ { kMenuEdit, "Paste", kMenuActionPaste, 'V', false },
+ { kMenuEdit, "Clear", kMenuActionClear, 'B', false },
+
+ { kMenuSpecial, "Clean Up", kMenuActionCleanUp, 0, false },
+ { kMenuSpecial, "Mess Up", kMenuActionMessUp, 0, false },
+
+ { 0, NULL, 0, 0, false }
+};
+
+
+bool commandsWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+bool mainGameWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+bool outConsoleWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+bool selfWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+bool exitsWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+bool diplomaWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+bool inventoryWindowCallback(Graphics::WindowClick, Common::Event &event, void *gui);
+
+void menuCommandsCallback(int action, Common::String &text, void *data);
+
+Gui::Gui(MacVentureEngine *engine, Common::MacResManager *resman) {
+ _engine = engine;
+ _resourceManager = resman;
+ _windowData = NULL;
+ _controlData = NULL;
+ _draggedObj.id = 0;
+ _draggedObj.pos = Common::Point(0, 0);
+ _dialog = NULL;
+
+ _cursor = new Cursor(this);
+
+ _consoleText = new ConsoleText(this);
+ _graphics = NULL;
+
+ initGUI();
+}
+
+Gui::~Gui() {
+
+ if (_windowData)
+ delete _windowData;
+
+ if (_controlData)
+ delete _controlData;
+
+ if (_exitsData)
+ delete _exitsData;
+
+ if (_cursor)
+ delete _cursor;
+
+ if (_consoleText)
+ delete _consoleText;
+
+ if (_dialog)
+ delete _dialog;
+
+ clearAssets();
+
+ if (_graphics)
+ delete _graphics;
+}
+
+void Gui::initGUI() {
+ _screen.create(kScreenWidth, kScreenHeight, Graphics::PixelFormat::createFormatCLUT8());
+ _wm.setScreen(&_screen);
+
+ // Menu
+ _menu = _wm.addMenu();
+ if (!loadMenus())
+ error("GUI: Could not load menus");
+ _menu->setCommandsCallback(menuCommandsCallback, this);
+ _menu->calcDimensions();
+
+ loadGraphics();
+
+ if (!loadWindows())
+ error("GUI: Could not load windows");
+
+ initWindows();
+
+ assignObjReferences();
+
+ if (!loadControls())
+ error("GUI: Could not load controls");
+
+ draw();
+
+}
+
+void Gui::reloadInternals() {
+ clearAssets();
+ loadGraphics();
+}
+
+void Gui::draw() {
+ // Will be performance-improved after the milestone
+ _wm.setFullRefresh(true);
+
+ drawWindows();
+
+ _wm.draw();
+
+ drawDraggedObject();
+ drawDialog();
+ // TODO: When window titles with custom borders are in MacGui, this should be used.
+ //drawWindowTitle(kMainGameWindow, _mainGameWindow->getSurface());
+}
+
+void Gui::drawMenu() {
+ _menu->draw(&_screen);
+}
+
+void Gui::drawTitle() {
+ warning("drawTitle hasn't been tested yet");
+}
+
+void Gui::clearControls() {
+ if (!_controlData)
+ return;
+
+ Common::Array<CommandButton>::iterator it = _controlData->begin();
+ for (; it != _controlData->end(); ++it) {
+ it->unselect();
+ }
+}
+
+void Gui::initWindows() {
+ // Game Controls Window
+ _controlsWindow = _wm.addWindow(false, false, false);
+ _controlsWindow->setDimensions(getWindowData(kCommandsWindow).bounds);
+ _controlsWindow->setActive(false);
+ _controlsWindow->setCallback(commandsWindowCallback, this);
+ loadBorders(_controlsWindow, findWindowData(kCommandsWindow).type);
+
+ // Main Game Window
+ _mainGameWindow = _wm.addWindow(false, false, false);
+ _mainGameWindow->setDimensions(getWindowData(kMainGameWindow).bounds);
+ _mainGameWindow->setActive(false);
+ _mainGameWindow->setCallback(mainGameWindowCallback, this);
+ loadBorders(_mainGameWindow, findWindowData(kMainGameWindow).type);
+
+ // In-game Output Console
+ _outConsoleWindow = _wm.addWindow(true, true, false);
+ // HACK We have to hand-create the dimensions, otherwise they don't fit
+ const WindowData &wd = getWindowData(kOutConsoleWindow);
+ Common::Rect dimensions = wd.bounds;
+ dimensions.setWidth(dimensions.width() - borderBounds(wd.type).rightOffset);
+ _outConsoleWindow->setDimensions(dimensions);
+ _outConsoleWindow->setActive(false);
+ _outConsoleWindow->setCallback(outConsoleWindowCallback, this);
+ loadBorders(_outConsoleWindow, findWindowData(kOutConsoleWindow).type);
+
+ // Self Window
+ _selfWindow = _wm.addWindow(false, true, false);
+ _selfWindow->setDimensions(getWindowData(kSelfWindow).bounds);
+ _selfWindow->setActive(false);
+ _selfWindow->setCallback(selfWindowCallback, this);
+ loadBorders(_selfWindow, findWindowData(kSelfWindow).type);
+
+ // Exits Window
+ _exitsWindow = _wm.addWindow(false, false, false);
+ _exitsWindow->setDimensions(getWindowData(kExitsWindow).bounds);
+ _exitsWindow->setActive(false);
+ _exitsWindow->setCallback(exitsWindowCallback, this);
+ loadBorders(_exitsWindow, findWindowData(kExitsWindow).type);
+}
+
+const WindowData &Gui::getWindowData(WindowReference reference) {
+ return findWindowData(reference);
+}
+
+const Graphics::Font &Gui::getCurrentFont() {
+ return *_wm.getFont("Chicago-12", Graphics::FontManager::kBigGUIFont);
+}
+
+void Gui::bringToFront(WindowReference winID) {
+ findWindow(winID)->setActive(true);
+}
+
+void Gui::setWindowTitle(WindowReference winID, Common::String string) {
+ findWindowData(winID).title = string;
+ findWindowData(winID).titleLength = string.size();
+}
+
+void Gui::updateWindowInfo(WindowReference ref, ObjID objID, const Common::Array<ObjID> &children) {
+ if (ref == kNoWindow) {
+ return;
+ }
+ WindowData &data = findWindowData(ref);
+ data.children.clear();
+ data.objRef = objID;
+ uint32 originx = 0x7fff;
+ uint32 originy = 0x7fff;
+ for (uint i = 0; i < children.size(); i++) {
+ if (children[i] != 1) {
+ ObjID child = children[i];
+ if (ref != kMainGameWindow) {
+ Common::Point childPos = _engine->getObjPosition(child);
+ originx = originx > (uint)childPos.x ? (uint)childPos.x : originx;
+ originy = originy > (uint)childPos.y ? (uint)childPos.y : originy;
+ }
+ data.children.push_back(DrawableObject(child, kBlitBIC));
+ }
+ }
+ if (originx != 0x7fff) {
+ data.bounds.left = originx;
+ }
+ if (originy != 0x7fff) {
+ data.bounds.top = originy;
+ }
+ if (ref != kMainGameWindow) {
+ data.updateScroll = true;
+ }
+}
+
+void Gui::addChild(WindowReference target, ObjID child) {
+ findWindowData(target).children.push_back(DrawableObject(child, kBlitBIC));
+}
+
+void Gui::removeChild(WindowReference target, ObjID child) {
+ WindowData &data = findWindowData(target);
+ uint index = 0;
+ for (;index < data.children.size(); index++) {
+ if (data.children[index].obj == child) {
+ break;
+ }
+ }
+
+ if (index < data.children.size())
+ data.children.remove_at(index);
+}
+
+void Gui::assignObjReferences() {
+ findWindowData(kSelfWindow).objRef = 0;
+}
+
+WindowReference Gui::createInventoryWindow(ObjID objRef) {
+ Graphics::MacWindow *newWindow = _wm.addWindow(true, true, true);
+ WindowData newData;
+ GlobalSettings settings = _engine->getGlobalSettings();
+ newData.refcon = (WindowReference)ABS(_inventoryWindows.size() + kInventoryStart); // This is a HACK
+
+ if (_windowData->back().refcon < 0x80) { // There is already another inventory window
+ newData.bounds = _windowData->back().bounds; // Inventory windows are always last
+ newData.bounds.translate(newData.bounds.left + settings._invOffsetX, newData.bounds.top + settings._invOffsetY);
+ } else {
+ BorderBounds bbs = borderBounds(kInvWindow);
+ newData.bounds = Common::Rect(
+ settings._invLeft - bbs.leftOffset,
+ settings._invTop - bbs.topOffset,
+ settings._invLeft + settings._invWidth,
+ settings._invTop + settings._invHeight);
+ }
+ newData.type = kInvWindow;
+ newData.hasCloseBox = true;
+ newData.visible = true;
+ newData.objRef = objRef;
+ _windowData->push_back(newData);
+
+ newWindow->setDimensions(newData.bounds);
+ newWindow->setCallback(inventoryWindowCallback, this);
+ newWindow->setCloseable(true);
+ loadBorders(newWindow, newData.type);
+ _inventoryWindows.push_back(newWindow);
+
+ debugC(1, kMVDebugGUI, "Create new inventory window. Reference: %d", newData.refcon);
+ return newData.refcon;
+}
+
+void Gui::loadBorders(Graphics::MacWindow *target, MVWindowType type) {
+ loadBorder(target, type, false);
+ loadBorder(target, type, true);
+}
+
+void Gui::loadBorder(Graphics::MacWindow *target, MVWindowType type, bool active) {
+
+ Common::SeekableReadStream *stream = _engine->getBorderFile(type, active);
+
+ if (stream) {
+ BorderBounds bbs = borderBounds(type);
+ target->loadBorder(*stream, active, bbs.leftOffset, bbs.rightOffset, bbs.topOffset, bbs.bottomOffset);
+
+ delete stream;
+ }
+}
+
+void Gui::loadGraphics() {
+ if (_graphics)
+ delete _graphics;
+ _graphics = new Container(_engine->getFilePath(kGraphicPathID));
+}
+
+void Gui::clearAssets() {
+ Common::HashMap<ObjID, ImageAsset*>::const_iterator it = _assets.begin();
+ for (; it != _assets.end(); it++) {
+ delete it->_value;
+ }
+ _assets.clear();
+}
+
+bool Gui::loadMenus() {
+
+ if (kLoadStaticMenus) {
+ // We assume that, if there are static menus, we don't need dynamic ones
+ _menu->addStaticMenus(menuSubItems);
+ return true;
+ }
+
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+ Common::MacResIDArray::const_iterator iter;
+
+ if ((resArray = _resourceManager->getResIDArray(MKTAG('M', 'E', 'N', 'U'))).size() == 0)
+ return false;
+
+ _menu->addMenuSubItem(0, "Abb", kMenuActionAbout, 0, 'A', true);
+
+ int i = 1;
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = _resourceManager->getResource(MKTAG('M', 'E', 'N', 'U'), *iter);
+ uint16 key;
+ uint16 style;
+ uint8 titleLength;
+ char *title;
+
+ /* Skip menuID, width, height, resourceID, placeholder */
+ for (int skip = 0; skip < 5; skip++) {
+ res->readUint16BE();
+ }
+ titleLength = res->readByte();
+ title = new char[titleLength + 1];
+ res->read(title, titleLength);
+ title[titleLength] = '\0';
+
+ if (titleLength > 1) {
+ _menu->addMenuItem(title);
+
+ // Read submenu items
+ while ((titleLength = res->readByte())) {
+ title = new char[titleLength + 1];
+ res->read(title, titleLength);
+ title[titleLength] = '\0';
+ // Skip icon
+ res->readUint16BE();
+ // Read key
+ key = res->readUint16BE();
+ // Skip mark
+ res->readUint16BE();
+ // Read style
+ style = res->readUint16BE();
+ _menu->addMenuSubItem(i, title, 0, style, key, false);
+ }
+ }
+
+ i++;
+ delete res;
+ }
+
+ return true;
+}
+
+bool Gui::loadWindows() {
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+ Common::MacResIDArray::const_iterator iter;
+
+ _windowData = new Common::List<WindowData>();
+
+ if ((resArray = _resourceManager->getResIDArray(MKTAG('W', 'I', 'N', 'D'))).size() == 0)
+ return false;
+
+ uint32 id = kCommandsWindow;
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = _resourceManager->getResource(MKTAG('W', 'I', 'N', 'D'), *iter);
+ WindowData data;
+ uint16 top, left, bottom, right;
+ top = res->readUint16BE();
+ left = res->readUint16BE();
+ bottom = res->readUint16BE();
+ right = res->readUint16BE();
+ data.type = (MVWindowType)res->readUint16BE();
+ BorderBounds bbs = borderBounds(data.type);
+ data.bounds = Common::Rect(
+ left - bbs.leftOffset,
+ top - bbs.topOffset,
+ right + bbs.rightOffset,
+ bottom + bbs.bottomOffset);
+
+ data.visible = res->readUint16BE();
+ data.hasCloseBox = res->readUint16BE();
+ data.refcon = (WindowReference)id; id++;
+ res->readUint32BE(); // Skip the true id. For some reason it's reading 0
+ data.titleLength = res->readByte();
+ if (data.titleLength) {
+ char *newTitle = new char[data.titleLength + 1];
+ res->read(newTitle, data.titleLength);
+ newTitle[data.titleLength] = '\0';
+ data.title = Common::String(newTitle);
+ delete[] newTitle;
+ }
+ data.scrollPos = Common::Point(0, 0);
+
+ debugC(1, kMVDebugGUI, "Window loaded: %s", data.title.c_str());
+
+ _windowData->push_back(data);
+
+ delete res;
+ }
+
+ return true;
+}
+
+bool Gui::loadControls() {
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+ Common::MacResIDArray::const_iterator iter;
+
+ _controlData = new Common::Array<CommandButton>();
+ _exitsData = new Common::Array<CommandButton>();
+
+ if ((resArray = _resourceManager->getResIDArray(MKTAG('C', 'N', 'T', 'L'))).size() == 0)
+ return false;
+
+ uint32 id = kControlExitBox;
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = _resourceManager->getResource(MKTAG('C', 'N', 'T', 'L'), *iter);
+ ControlData data;
+ uint16 top, left, bottom, right;
+ top = res->readUint16BE();
+ left = res->readUint16BE();
+ bottom = res->readUint16BE();
+ right = res->readUint16BE();
+ data.scrollValue = res->readUint16BE();
+ data.visible = res->readByte();
+ res->readByte(); // Unused
+ data.scrollMax = res->readUint16BE();
+ data.scrollMin = res->readUint16BE();
+ data.cdef = res->readUint16BE();
+ data.refcon = (ControlAction)res->readUint32BE();
+ data.type = (ControlType)id; id++;
+ data.titleLength = res->readByte();
+ if (data.titleLength) {
+ char *title = new char[data.titleLength + 1];
+ res->read(title, data.titleLength);
+ title[data.titleLength] = '\0';
+ data.title = Common::String(title);
+ delete[] title;
+ }
+ if (data.type != kControlExitBox) {
+ BorderBounds bbs = borderBounds(getWindowData(kCommandsWindow).type);
+ // We just want to move the button, not change it's size
+ data.bounds = Common::Rect(left + bbs.leftOffset, top + bbs.topOffset, right + bbs.leftOffset, bottom + bbs.topOffset);
+ } else {
+ data.bounds = Common::Rect(left, top, right, bottom);
+ }
+
+
+ _controlData->push_back(CommandButton(data, this));
+
+ delete res;
+ }
+
+ return true;
+}
+
+void Gui::drawWindows() {
+
+ drawCommandsWindow();
+ drawMainGameWindow();
+ drawSelfWindow();
+ drawInventories();
+ drawExitsWindow();
+ drawConsoleWindow();
+
+}
+
+void Gui::drawCommandsWindow() {
+ if (_engine->needsClickToContinue()) {
+ Graphics::ManagedSurface *srf = _controlsWindow->getSurface();
+ WindowData data = getWindowData(kCommandsWindow);
+ srf->fillRect(Common::Rect(0, 0, srf->w, srf->h), kColorWhite);
+ getCurrentFont().drawString(
+ srf,
+ _engine->getCommandsPausedString(),
+ 0,
+ (srf->h / 2) - getCurrentFont().getFontHeight(),
+ data.bounds.right - data.bounds.left,
+ kColorBlack,
+ Graphics::kTextAlignCenter);
+ } else {
+ Common::Array<CommandButton>::const_iterator it = _controlData->begin();
+ for (; it != _controlData->end(); ++it) {
+ CommandButton button = *it;
+ if (button.getData().type != kControlExitBox)
+ button.draw(*_controlsWindow->getSurface());
+ }
+ }
+}
+
+void Gui::drawMainGameWindow() {
+ const WindowData &data = getWindowData(kMainGameWindow);
+ BorderBounds border = borderBounds(data.type);
+ ObjID objRef = data.objRef;
+
+ _mainGameWindow->setDirty(true);
+
+ if (data.objRef > 0 && data.objRef < 2000) {
+ ensureAssetLoaded(objRef);
+
+ _assets[objRef]->blitInto(
+ _mainGameWindow->getSurface(),
+ border.leftOffset,
+ border.topOffset,
+ kBlitDirect);
+ }
+
+ drawObjectsInWindow(data, _mainGameWindow->getSurface());
+
+ if (DebugMan.isDebugChannelEnabled(kMVDebugGUI)) {
+ Graphics::MacWindow *win = findWindow(data.refcon);
+ Common::Rect innerDims = win->getInnerDimensions();
+ int x = win->getDimensions().left;
+ int y = win->getDimensions().top;
+ innerDims.translate(-x, -y);
+ win->getSurface()->frameRect(innerDims, kColorGreen);
+ }
+
+ findWindow(kMainGameWindow)->setDirty(true);
+}
+
+void Gui::drawSelfWindow() {
+ drawObjectsInWindow(getWindowData(kSelfWindow), _selfWindow->getSurface());
+ if (_engine->isObjSelected(1)) {
+ invertWindowColors(kSelfWindow);
+ }
+ findWindow(kSelfWindow)->setDirty(true);
+}
+
+void Gui::drawInventories() {
+
+ Graphics::ManagedSurface *srf;
+ for (uint i = 0; i < _inventoryWindows.size(); i++) {
+ const WindowData &data = getWindowData((WindowReference)(kInventoryStart + i));
+ Graphics::MacWindow *win = findWindow(data.refcon);
+ srf = win->getSurface();
+ srf->clear(kColorGreen);
+ srf->fillRect(srf->getBounds(), kColorWhite);
+ drawObjectsInWindow(data, srf);
+
+ if (DebugMan.isDebugChannelEnabled(kMVDebugGUI)) {
+ Common::Rect innerDims = win->getInnerDimensions();
+ int x = win->getDimensions().left;
+ int y = win->getDimensions().top;
+ innerDims.translate(-x, -y);
+ srf->frameRect(innerDims, kColorGreen);
+ }
+
+ findWindow(data.refcon)->setDirty(true);
+ }
+
+}
+
+void Gui::drawExitsWindow() {
+
+ Graphics::ManagedSurface *srf = _exitsWindow->getSurface();
+ BorderBounds border = borderBounds(getWindowData(kExitsWindow).type);
+
+ srf->fillRect(Common::Rect(
+ border.leftOffset,
+ border.topOffset,
+ srf->w + border.rightOffset,
+ srf->h + border.bottomOffset), kColorWhite);
+
+ Common::Array<CommandButton>::const_iterator it = _exitsData->begin();
+ for (; it != _exitsData->end(); ++it) {
+ CommandButton button = *it;
+ button.draw(*_exitsWindow->getSurface());
+ }
+
+ findWindow(kExitsWindow)->setDirty(true);
+}
+
+void Gui::drawConsoleWindow() {
+
+ Graphics::ManagedSurface *srf = _outConsoleWindow->getSurface();
+ BorderBounds bounds = borderBounds(getWindowData(kOutConsoleWindow).type);
+ _consoleText->renderInto(srf, bounds, kConsoleLeftOffset);
+}
+
+void Gui::drawObjectsInWindow(const WindowData &targetData, Graphics::ManagedSurface *surface) {
+ BorderBounds border = borderBounds(targetData.type);
+ Common::Point pos;
+ ObjID child;
+ BlitMode mode;
+
+ if (targetData.children.size() == 0) {
+ return;
+ }
+
+ Graphics::ManagedSurface composeSurface;
+ createInnerSurface(&composeSurface, surface, border);
+ assert(composeSurface.w <= surface->w &&
+ composeSurface.h <= surface->h);
+ composeSurface.clear(kColorGreen);
+
+ for (uint i = 0; i < targetData.children.size(); i++) {
+ child = targetData.children[i].obj;
+ mode = (BlitMode)targetData.children[i].mode;
+ pos = _engine->getObjPosition(child);
+ pos -= targetData.scrollPos;
+ ensureAssetLoaded(child);
+
+ _assets[child]->blitInto(
+ &composeSurface,
+ pos.x,
+ pos.y,
+ mode);
+
+ if (_engine->isObjVisible(child)) {
+ if (_engine->isObjSelected(child) ||
+ child == _draggedObj.id) {
+
+ _assets[child]->blitInto(
+ &composeSurface, pos.x, pos.y, kBlitOR);
+ }
+ }
+
+ if (DebugMan.isDebugChannelEnabled(kMVDebugGUI)) {
+ Common::Rect testBounds = _engine->getObjBounds(child);
+ testBounds.translate(-targetData.scrollPos.x, -targetData.scrollPos.y);
+ surface->frameRect(testBounds, kColorGreen);
+ }
+ }
+ Common::Point composePosition = Common::Point(border.leftOffset, border.topOffset);
+ surface->transBlitFrom(composeSurface, composePosition, kColorGreen);
+}
+
+void Gui::drawWindowTitle(WindowReference target, Graphics::ManagedSurface *surface) {
+ // TODO: Implement when MacGui supports titles in windows with custom borders.
+}
+
+void Gui::drawDraggedObject() {
+ if (_draggedObj.id != 0 &&
+ _engine->isObjVisible(_draggedObj.id)) {
+ ensureAssetLoaded(_draggedObj.id);
+ ImageAsset *asset = _assets[_draggedObj.id];
+
+ // In case of overflow from the right/top
+ uint w = asset->getWidth() + MIN((int16)0, _draggedObj.pos.x);
+ uint h = asset->getHeight() + MIN((int16)0, _draggedObj.pos.y);
+
+ // In case of overflow from the bottom/left
+ if (_draggedObj.pos.x > 0 && _draggedObj.pos.x + w > kScreenWidth) {
+ w = kScreenWidth - _draggedObj.pos.x;
+ }
+ if (_draggedObj.pos.y > 0 && _draggedObj.pos.y + h > kScreenHeight) {
+ h = kScreenHeight - _draggedObj.pos.y;
+ }
+
+ Common::Point target = _draggedObj.pos;
+ if (target.x < 0) {
+ target.x = 0;
+ }
+ if (target.y < 0) {
+ target.y = 0;
+ }
+
+ _draggedSurface.create(w, h, _screen.format);
+ _draggedSurface.blitFrom(
+ _screen,
+ Common::Rect(
+ target.x,
+ target.y,
+ target.x + _draggedSurface.w,
+ target.y + _draggedSurface.h),
+ Common::Point(0, 0));
+ asset->blitInto(&_draggedSurface, MIN((int16)0, _draggedObj.pos.x), MIN((int16)0, _draggedObj.pos.y), kBlitBIC);
+
+ g_system->copyRectToScreen(
+ _draggedSurface.getBasePtr(0, 0),
+ _draggedSurface.pitch,
+ target.x,
+ target.y,
+ _draggedSurface.w,
+ _draggedSurface.h
+ );
+ }
+}
+
+void Gui::drawDialog() {
+ if (_dialog) {
+ _dialog->draw();
+ }
+}
+
+void Gui::updateWindow(WindowReference winID, bool containerOpen) {
+ if (winID == kNoWindow) {
+ return;
+ }
+ if (winID == kSelfWindow || containerOpen) {
+ WindowData &data = findWindowData(winID);
+ if (winID == kCommandsWindow) {
+ Common::Array<CommandButton>::iterator it = _controlData->begin();
+ for (; it != _controlData->end(); ++it) {
+ it->unselect();
+ }
+ }
+ Common::Array<DrawableObject> &children = data.children;
+ for (uint i = 0; i < children.size(); i++) {
+ uint flag = 0;
+ ObjID child = children[i].obj;
+ BlitMode mode = kBlitDirect;
+ bool off = !_engine->isObjVisible(child);
+ if (flag || !off || !_engine->isObjClickable(child)) {
+ mode = kBlitBIC;
+ if (off || flag) {
+ mode = kBlitXOR;
+ } else if (!off && _engine->isObjSelected(child)) {
+ mode = kBlitOR;
+ }
+ children[i] = DrawableObject(child, mode);
+ } else {
+ children[i] = DrawableObject(child, kBlitXOR);
+ }
+ }
+ if (winID == kMainGameWindow) {
+ drawMainGameWindow();
+ } else {
+ Graphics::MacWindow *winRef = findWindow(winID);
+ winRef->getSurface()->fillRect(data.bounds, kColorGray);
+ }
+ if (data.type == kZoomDoc && data.updateScroll) {
+ warning("Unimplemented: update scroll");
+ }
+ }
+}
+
+void Gui::clearExits() {
+ _exitsData->clear();
+}
+
+void Gui::unselectExits() {
+ Common::Array<CommandButton>::const_iterator it = _exitsData->begin();
+ for (; it != _exitsData->end(); ++it) {
+ CommandButton button = *it;
+ button.unselect();
+ }
+}
+
+void Gui::updateExit(ObjID obj) {
+ if (!_engine->isObjExit(obj)) {
+ return;
+ }
+ BorderBounds border = borderBounds(getWindowData(kExitsWindow).type);
+
+ int ctl = -1;
+ int i = 0;
+ Common::Array<CommandButton>::const_iterator it = _exitsData->begin();
+ for (;it != _exitsData->end(); it++) {
+ if (it->getData().refcon == obj)
+ ctl = i;
+ else
+ i++;
+ }
+
+ if (ctl != -1)
+ _exitsData->remove_at(ctl);
+
+ if (!_engine->isHiddenExit(obj) &&
+ _engine->getParent(obj) == _engine->getParent(1)) {
+ ControlData data;
+ data.titleLength = 0;
+ data.refcon = (ControlAction)obj; // Objects can be exits (actions)
+ Common::Point pos = _engine->getObjExitPosition(obj);
+ pos.x += border.leftOffset;
+ pos.y += border.topOffset;
+ data.bounds = Common::Rect(pos.x, pos.y, pos.x + kExitButtonWidth, pos.y + kExitButtonHeight);
+ data.visible = true;
+
+ _exitsData->push_back(CommandButton(data, this));
+ }
+}
+
+void Gui::printText(const Common::String &text) {
+ debugC(1, kMVDebugGUI, "Print Text: %s", text.c_str());
+ _consoleText->printLine(text, _outConsoleWindow->getDimensions().width());
+}
+
+void Gui::showPrebuiltDialog(PrebuiltDialogs type) {
+ closeDialog();
+ _dialog = new Dialog(this, type);
+}
+
+bool Gui::isDialogOpen() {
+ return _dialog != NULL;
+}
+
+void Gui::setTextInput(Common::String str) {
+ _engine->setTextInput(str);
+}
+
+
+void Gui::closeDialog() {
+ delete _dialog;
+ _dialog = NULL;
+}
+
+void Gui::getTextFromUser() {
+ if (_dialog) {
+ delete _dialog;
+ }
+ showPrebuiltDialog(kSpeakDialog);
+}
+
+void Gui::loadGame() {
+ _engine->scummVMSaveLoadDialog(false);
+}
+
+void Gui::saveGame() {
+ _engine->scummVMSaveLoadDialog(true);
+}
+
+void Gui::newGame() {
+ _engine->newGame();
+}
+
+void Gui::quitGame() {
+ _engine->requestQuit();
+}
+
+void Gui::createInnerSurface(Graphics::ManagedSurface *innerSurface, Graphics::ManagedSurface *outerSurface, const BorderBounds &borders) {
+ innerSurface->create(
+ outerSurface->w - borders.leftOffset - borders.rightOffset,
+ outerSurface->h - borders.topOffset - borders.bottomOffset,
+ outerSurface->format);
+}
+
+void Gui::moveDraggedObject(Common::Point target) {
+ ensureAssetLoaded(_draggedObj.id);
+ _draggedObj.pos = target + _draggedObj.mouseOffset;
+
+ // TODO FInd more elegant way of making pow2
+ _draggedObj.hasMoved = (_draggedObj.startPos.sqrDist(_draggedObj.pos) >= (kDragThreshold * kDragThreshold));
+
+ debugC(4, kMVDebugGUI, "Dragged obj position: (%d, %d), mouse offset: (%d, %d), hasMoved: %d, dist: %d, threshold: %d",
+ _draggedObj.pos.x, _draggedObj.pos.y,
+ _draggedObj.mouseOffset.x, _draggedObj.mouseOffset.y,
+ _draggedObj.hasMoved,
+ _draggedObj.startPos.sqrDist(_draggedObj.pos),
+ kDragThreshold * kDragThreshold
+ );
+
+}
+
+WindowReference Gui::findWindowAtPoint(Common::Point point) {
+ Common::List<WindowData>::iterator it;
+ Graphics::MacWindow *win;
+ for (it = _windowData->begin(); it != _windowData->end(); it++) {
+ win = findWindow(it->refcon);
+ if (win && it->refcon != kDiplomaWindow) { //HACK, diploma should be cosnidered
+ if (win->getDimensions().contains(point)) {
+ return it->refcon;
+ }
+ }
+ }
+ return kNoWindow;
+}
+
+Common::Point Gui::getGlobalScrolledSurfacePosition(WindowReference reference) {
+ const WindowData &data = getWindowData(reference);
+ BorderBounds border = borderBounds(data.type);
+ Graphics::MacWindow *win = findWindow(reference);
+ if (!win) {
+ return Common::Point(0, 0);
+ }
+ return Common::Point(
+ win->getDimensions().left + border.leftOffset - data.scrollPos.x,
+ win->getDimensions().top + border.topOffset - data.scrollPos.y);
+}
+
+WindowData &Gui::findWindowData(WindowReference reference) {
+ assert(_windowData);
+
+ Common::List<WindowData>::iterator iter = _windowData->begin();
+ while (iter->refcon != reference && iter != _windowData->end()) {
+ iter++;
+ }
+
+ if (iter->refcon == reference)
+ return *iter;
+
+ error("GUI: Could not locate the desired window data");
+}
+
+Graphics::MacWindow *Gui::findWindow(WindowReference reference) {
+ if (reference < 0x80 && reference >= kInventoryStart) { // It's an inventory window
+ return _inventoryWindows[reference - kInventoryStart];
+ }
+ switch (reference) {
+ case MacVenture::kNoWindow:
+ return NULL;
+ case MacVenture::kCommandsWindow:
+ return _controlsWindow;
+ case MacVenture::kMainGameWindow:
+ return _mainGameWindow;
+ case MacVenture::kOutConsoleWindow:
+ return _outConsoleWindow;
+ case MacVenture::kSelfWindow:
+ return _selfWindow;
+ case MacVenture::kExitsWindow:
+ return _exitsWindow;
+ case MacVenture::kDiplomaWindow:
+ return _diplomaWindow;
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+void Gui::ensureInventoryOpen(WindowReference reference, ObjID id) {
+ assert(reference < 0x80 && reference >= kInventoryStart);
+ if (reference - kInventoryStart == (int)_inventoryWindows.size()) {
+ createInventoryWindow(id);
+ }
+}
+
+WindowReference Gui::getObjWindow(ObjID objID) {
+ switch (objID) {
+ case 0xfffc: return kExitsWindow;
+ case 0xfffd: return kSelfWindow;
+ case 0xfffe: return kOutConsoleWindow;
+ case 0xffff: return kCommandsWindow;
+ }
+
+ return findObjWindow(objID);
+}
+
+WindowReference Gui::findObjWindow(ObjID objID) {
+ // This is a bit of a HACK, we take advantage of the consecutive nature of references
+ for (uint i = kCommandsWindow; i <= kDiplomaWindow; i++) {
+ const WindowData &data = getWindowData((WindowReference)i);
+ if (data.objRef == objID) {
+ return data.refcon;
+ }
+ }
+
+ for (uint i = kInventoryStart; i < _inventoryWindows.size() + kInventoryStart; i++) {
+ const WindowData &data = getWindowData((WindowReference)i);
+ if (data.objRef == objID) {
+ return data.refcon;
+ }
+ }
+
+ return kNoWindow;
+}
+
+void Gui::checkSelect(const WindowData &data, Common::Point pos, const Common::Rect &clickRect, WindowReference ref) {
+ ObjID child = 0;
+ for (Common::Array<DrawableObject>::const_iterator it = data.children.begin(); it != data.children.end(); it++) {
+ if (canBeSelected((*it).obj, clickRect, ref)) {
+ child = (*it).obj;
+ }
+ }
+ if (child != 0) {
+ selectDraggable(child, ref, pos);
+ bringToFront(ref);
+ }
+}
+
+bool Gui::canBeSelected(ObjID obj, const Common::Rect &clickRect, WindowReference ref) {
+ return (_engine->isObjClickable(obj) &&
+ isRectInsideObject(clickRect, obj));
+}
+
+bool Gui::isRectInsideObject(Common::Rect target, ObjID obj) {
+ ensureAssetLoaded(obj);
+ Common::Rect bounds = _engine->getObjBounds(obj);
+ Common::Rect intersection = bounds.findIntersectingRect(target);
+ // We translate it to the image's coord system
+ intersection = Common::Rect(
+ intersection.left - bounds.left,
+ intersection.top - bounds.top,
+ intersection.left - bounds.left + intersection.width(),
+ intersection.top - bounds.top + intersection.height());
+
+ return _assets[obj]->isRectInside(intersection);
+}
+
+void Gui::selectDraggable(ObjID child, WindowReference origin, Common::Point click) {
+ if (_engine->isObjClickable(child) && _draggedObj.id == 0) {
+ _draggedObj.hasMoved = false;
+ _draggedObj.id = child;
+ _draggedObj.startWin = origin;
+ Common::Point localizedClick = click - getGlobalScrolledSurfacePosition(origin);
+ _draggedObj.mouseOffset = _engine->getObjPosition(child) - localizedClick;
+ _draggedObj.pos = click + _draggedObj.mouseOffset;
+ _draggedObj.startPos = _draggedObj.pos;
+ }
+}
+
+void Gui::handleDragRelease(bool shiftPressed, bool isDoubleClick) {
+ if (_draggedObj.id != 0) {
+ WindowReference destinationWindow = findWindowAtPoint(_draggedObj.pos);
+ if (destinationWindow == kNoWindow) {
+ return;
+ }
+ if (_draggedObj.hasMoved) {
+ const WindowData &destinationWindowData = getWindowData(destinationWindow);
+ ObjID destObject = destinationWindowData.objRef;
+ Common::Point dropPosition = _draggedObj.pos - _draggedObj.startPos;
+ dropPosition = localizeTravelledDistance(dropPosition, _draggedObj.startWin, destinationWindow);
+ debugC(3, kMVDebugGUI, "Drop the object %d at obj %d, pos (%d, %d)", _draggedObj.id, destObject, dropPosition.x, dropPosition.y);
+
+ _engine->handleObjectDrop(_draggedObj.id, dropPosition, destObject);
+ }
+ _engine->handleObjectSelect(_draggedObj.id, destinationWindow, shiftPressed, isDoubleClick);
+ _draggedObj.id = 0;
+ _draggedObj.hasMoved = false;
+ }
+}
+
+Common::Rect Gui::calculateClickRect(Common::Point clickPos, Common::Rect windowBounds) {
+ int left = clickPos.x - windowBounds.left;
+ int top = clickPos.y - windowBounds.top;
+ return Common::Rect(left - kCursorWidth, top - kCursorHeight, left + kCursorWidth, top + kCursorHeight);
+}
+
+Common::Point Gui::localizeTravelledDistance(Common::Point point, WindowReference origin, WindowReference target) {
+ if (origin != target) {
+ // ori.local to global
+ point += getGlobalScrolledSurfacePosition(origin);
+ if (findWindow(target)) {
+ // dest.globalToLocal
+ point -= getGlobalScrolledSurfacePosition(target);
+ }
+ }
+ return point;
+}
+
+void Gui::removeInventoryWindow(WindowReference ref) {
+ _inventoryWindows.remove_at(ref - kInventoryStart);
+ bool found = false;
+ Common::List<WindowData>::iterator it;
+ for (it = _windowData->begin(); it != _windowData->end() && !found; it++) {
+ if (it->refcon == ref) {
+ _windowData->erase(it);
+ found = true;
+ }
+ }
+}
+
+
+/* HANDLERS */
+void Gui::handleMenuAction(MenuAction action) {
+ switch (action) {
+ case MacVenture::kMenuActionAbout:
+ warning("Unimplemented MacVenture Menu Action: About");
+ break;
+ case MacVenture::kMenuActionNew:
+ _engine->newGame();
+ break;
+ case MacVenture::kMenuActionOpen:
+ loadGame();
+ break;
+ case MacVenture::kMenuActionSave:
+ saveGame();
+ break;
+ case MacVenture::kMenuActionSaveAs:
+ saveGame();
+ break;
+ case MacVenture::kMenuActionQuit:
+ _engine->requestQuit();
+ break;
+ case MacVenture::kMenuActionUndo:
+ warning("Unimplemented MacVenture Menu Action: Undo");
+ break;
+ case MacVenture::kMenuActionCut:
+ warning("Unimplemented MacVenture Menu Action: Cut");
+ break;
+ case MacVenture::kMenuActionCopy:
+ warning("Unimplemented MacVenture Menu Action: Copy");
+ break;
+ case MacVenture::kMenuActionPaste:
+ warning("Unimplemented MacVenture Menu Action: Paste");
+ break;
+ case MacVenture::kMenuActionClear:
+ warning("Unimplemented MacVenture Menu Action: Clear");
+ break;
+ case MacVenture::kMenuActionCleanUp:
+ warning("Unimplemented MacVenture Menu Action: Clean Up");
+ break;
+ case MacVenture::kMenuActionMessUp:
+ warning("Unimplemented MacVenture Menu Action: Mess Up");
+ break;
+ case MacVenture::kMenuActionCommand:
+ warning("Unimplemented MacVenture Menu Action: GENERIC");
+ break;
+ default:
+ break;
+ }
+}
+
+/* CALLBACKS */
+
+bool commandsWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+ return g->processCommandEvents(click, event);
+}
+
+bool mainGameWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+ return g->processMainGameEvents(click, event);
+}
+
+bool outConsoleWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+ return g->processOutConsoleEvents(click, event);
+}
+
+bool selfWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+
+ return g->processSelfEvents(click, event);
+}
+
+bool exitsWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+
+ return g->processExitsEvents(click, event);
+}
+
+bool diplomaWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+
+ return g->processDiplomaEvents(click, event);
+}
+
+bool inventoryWindowCallback(Graphics::WindowClick click, Common::Event &event, void *gui) {
+ Gui *g = (Gui*)gui;
+
+ return g->processInventoryEvents(click, event);
+}
+
+void menuCommandsCallback(int action, Common::String &text, void *data) {
+ Gui *g = (Gui *)data;
+
+ g->handleMenuAction((MenuAction)action);
+}
+
+
+void Gui::invertWindowColors(WindowReference winID) {
+ Graphics::ManagedSurface *srf = findWindow(winID)->getSurface();
+ for (uint y = 0; y < srf->h; y++) {
+ for (uint x = 0; x < srf->w; x++) {
+ byte p = *(byte *)srf->getBasePtr(x, y);
+ *(byte *)srf->getBasePtr(x, y) =
+ (p == kColorWhite) ? kColorBlack : kColorGray;
+ }
+ }
+}
+
+bool Gui::tryCloseWindow(WindowReference winID) {
+ WindowData data = findWindowData(winID);
+ Graphics::MacWindow *win = findWindow(winID);
+ _wm.removeWindow(win);
+ if (winID < 0x80) {
+ removeInventoryWindow(winID);
+ }
+ return true;
+}
+
+Common::Point Gui::getObjMeasures(ObjID obj) {
+ ensureAssetLoaded(obj);
+ int w = _assets[obj]->getWidth();
+ int h = _assets[obj]->getHeight();
+ return Common::Point(w, h);
+}
+
+bool Gui::processEvent(Common::Event &event) {
+ bool processed = false;
+
+ processed |= _cursor->processEvent(event);
+
+ if (_dialog && _dialog->processEvent(event)) {
+ return true;
+ }
+
+ if (event.type == Common::EVENT_MOUSEMOVE) {
+ if (_draggedObj.id != 0) {
+ moveDraggedObject(event.mouse);
+ }
+ processed = true;
+ }
+
+ processed |= _wm.processEvent(event);
+ return (processed);
+}
+
+bool Gui::processCommandEvents(WindowClick click, Common::Event &event) {
+ if (event.type == Common::EVENT_LBUTTONUP) {
+ if (_engine->needsClickToContinue()) {
+ _engine->selectControl(kClickToContinue);
+ return true;
+ }
+
+ Common::Point position(
+ event.mouse.x - _controlsWindow->getDimensions().left,
+ event.mouse.y - _controlsWindow->getDimensions().top);
+
+ CommandButton data;
+ if (!_controlData)
+ return false;
+
+ Common::Array<CommandButton>::iterator it = _controlData->begin();
+ for (; it != _controlData->end(); ++it) {
+ if (it->isInsideBounds(position)) {
+ it->select();
+ data = *it;
+ } else {
+ it->unselect();
+ }
+ }
+
+ _engine->selectControl(data.getData().refcon);
+ _engine->refreshReady();
+ _engine->preparedToRun();
+ }
+ return false;
+}
+
+bool MacVenture::Gui::processMainGameEvents(WindowClick click, Common::Event &event) {
+ if (_engine->needsClickToContinue())
+ return true;
+
+ return false;
+}
+
+bool MacVenture::Gui::processOutConsoleEvents(WindowClick click, Common::Event &event) {
+ if (_engine->needsClickToContinue())
+ return true;
+
+ if (click == kBorderScrollUp && event.type == Common::EVENT_LBUTTONDOWN) {
+ _consoleText->scrollUp();
+ return true;
+ }
+ if (click == kBorderScrollDown && event.type == Common::EVENT_LBUTTONDOWN) {
+ _consoleText->scrollDown();
+ return true;
+ }
+
+ return getWindowData(kOutConsoleWindow).visible;
+}
+
+bool MacVenture::Gui::processSelfEvents(WindowClick click, Common::Event &event) {
+ if (_engine->needsClickToContinue())
+ return true;
+
+ if (event.type == Common::EVENT_LBUTTONUP) {
+ _engine->handleObjectSelect(1, kSelfWindow, false, false);
+ }
+ return true;
+}
+
+bool MacVenture::Gui::processExitsEvents(WindowClick click, Common::Event &event) {
+ if (event.type == Common::EVENT_LBUTTONUP) {
+ if (_engine->needsClickToContinue()) {
+ return true;
+ }
+
+ Common::Point position(
+ event.mouse.x - _exitsWindow->getDimensions().left,
+ event.mouse.y - _exitsWindow->getDimensions().top);
+
+ CommandButton button;
+ if (!_exitsData)
+ return false;
+
+ Common::Array<CommandButton>::iterator it = _exitsData->begin();
+ for (; it != _exitsData->end(); ++it) {
+ if (it->isInsideBounds(position)) {
+ it->select();
+ button = *it;
+ _engine->handleObjectSelect(button.getData().refcon, kExitsWindow, false, false);
+ return true;
+ } else {
+ it->unselect();
+ }
+ }
+
+ }
+ return getWindowData(kExitsWindow).visible;
+}
+
+bool MacVenture::Gui::processDiplomaEvents(WindowClick click, Common::Event &event) {
+ if (_engine->needsClickToContinue())
+ return true;
+
+ return getWindowData(kDiplomaWindow).visible;
+}
+
+bool Gui::processInventoryEvents(WindowClick click, Common::Event &event) {
+ if (event.type == Common::EVENT_LBUTTONDOWN && click == kBorderCloseButton) {
+ WindowReference ref = findWindowAtPoint(event.mouse);
+ if (ref == kNoWindow) {
+ return false;
+ }
+
+ if (click == kBorderCloseButton) {
+ removeInventoryWindow(ref);
+ return true;
+ }
+ }
+
+ if (_engine->needsClickToContinue())
+ return true;
+
+ if (event.type == Common::EVENT_LBUTTONDOWN) {
+ // Find the appropriate window
+ WindowReference ref = findWindowAtPoint(event.mouse);
+ if (ref == kNoWindow) {
+ return false;
+ }
+
+ WindowData &data = findWindowData((WindowReference) ref);
+
+ if (click == kBorderScrollUp) {
+ data.scrollPos.y = MAX(0, data.scrollPos.y - kScrollAmount);
+ }
+ if (click == kBorderScrollDown) {
+ data.scrollPos.y += kScrollAmount;
+ }
+ if (click == kBorderScrollLeft) {
+ data.scrollPos.x = MAX(0, data.scrollPos.x - kScrollAmount);
+ }
+ if (click == kBorderScrollRight) {
+ data.scrollPos.x += kScrollAmount;
+ }
+ }
+ return true;
+}
+
+void Gui::selectForDrag(Common::Point cursorPosition) {
+ WindowReference ref = findWindowAtPoint(cursorPosition);
+ if (ref == kNoWindow) {
+ return;
+ }
+
+ Graphics::MacWindow *win = findWindow(ref);
+ WindowData &data = findWindowData((WindowReference) ref);
+
+ Common::Rect clickRect = calculateClickRect(cursorPosition + data.scrollPos, win->getDimensions());
+ checkSelect(data, cursorPosition, clickRect, (WindowReference)ref);
+}
+
+void Gui::handleSingleClick() {
+ debugC(2, kMVDebugGUI, "Registered Single Click");
+ // HACK THERE HAS TO BE A MORE ELEGANT WAY
+ if (_dialog) {
+ return;
+ }
+ handleDragRelease(false, false);
+}
+
+void Gui::handleDoubleClick() {
+ debugC(2, kMVDebugGUI, "Registered Double Click");
+ if (_dialog) {
+ return;
+ }
+ handleDragRelease(false, true);
+}
+
+void Gui::ensureAssetLoaded(ObjID obj) {
+ if (!_assets.contains(obj)) {
+ _assets[obj] = new ImageAsset(obj, _graphics);
+ }
+}
+
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/gui.h b/engines/macventure/gui.h
new file mode 100644
index 0000000000..8ae4a1534d
--- /dev/null
+++ b/engines/macventure/gui.h
@@ -0,0 +1,369 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_GUI_H
+#define MACVENTURE_GUI_H
+
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/macwindow.h"
+#include "graphics/macgui/macmenu.h"
+
+#include "graphics/font.h"
+
+#include "common/timer.h"
+
+#include "macventure/macventure.h"
+#include "macventure/container.h"
+#include "macventure/image.h"
+#include "macventure/prebuilt_dialogs.h"
+#include "macventure/dialog.h"
+#include "macventure/controls.h"
+#include "macventure/windows.h"
+
+namespace MacVenture {
+
+using namespace Graphics::MacGUIConstants;
+using namespace Graphics::MacWindowConstants;
+class MacVentureEngine;
+typedef uint32 ObjID;
+
+class Cursor;
+class ConsoleText;
+class CommandButton;
+class ImageAsset;
+class Dialog;
+
+BorderBounds borderBounds(MVWindowType type);
+
+enum MenuAction {
+ kMenuActionAbout,
+ kMenuActionNew,
+ kMenuActionOpen,
+ kMenuActionSave,
+ kMenuActionSaveAs,
+ kMenuActionQuit,
+ kMenuActionUndo,
+ kMenuActionCut,
+ kMenuActionCopy,
+ kMenuActionPaste,
+ kMenuActionClear,
+ kMenuActionCleanUp,
+ kMenuActionMessUp,
+
+ kMenuActionCommand
+};
+
+struct DraggedObj {
+ ObjID id;
+ Common::Point pos;
+ Common::Point mouseOffset;
+ Common::Point startPos;
+ WindowReference startWin;
+ bool hasMoved;
+};
+
+class Gui {
+
+public:
+ Gui(MacVentureEngine *engine, Common::MacResManager *resman);
+ ~Gui();
+
+ void reloadInternals();
+
+ void draw();
+ void drawMenu();
+ void drawTitle();
+
+ void clearControls();
+ bool processEvent(Common::Event &event);
+ void handleMenuAction(MenuAction action);
+ void updateWindow(WindowReference winID, bool containerOpen);
+ void invertWindowColors(WindowReference winID);
+
+ WindowReference createInventoryWindow(ObjID objRef);
+ bool tryCloseWindow(WindowReference winID);
+
+ Common::Point getObjMeasures(ObjID obj);
+
+ WindowReference getObjWindow(ObjID objID);
+ WindowReference findObjWindow(ObjID objID);
+
+ // Event processors
+ bool processCommandEvents(WindowClick click, Common::Event &event);
+ bool processMainGameEvents(WindowClick click, Common::Event &event);
+ bool processOutConsoleEvents(WindowClick click, Common::Event &event);
+ bool processSelfEvents(WindowClick click, Common::Event &event);
+ bool processExitsEvents(WindowClick click, Common::Event &event);
+ bool processDiplomaEvents(WindowClick click, Common::Event &event);
+ bool processInventoryEvents(WindowClick click, Common::Event &event);
+
+ const WindowData& getWindowData(WindowReference reference);
+
+ const Graphics::Font& getCurrentFont();
+
+ // Clicks
+ void selectForDrag(Common::Point cursorPosition);
+ void handleSingleClick();
+ void handleDoubleClick();
+
+ // Modifiers
+ void bringToFront(WindowReference window);
+ void setWindowTitle(WindowReference winID, Common::String string);
+ void updateWindowInfo(WindowReference ref, ObjID objID, const Common::Array<ObjID> &children);
+ void ensureInventoryOpen(WindowReference reference, ObjID id);
+
+ void addChild(WindowReference target, ObjID child);
+ void removeChild(WindowReference target, ObjID child);
+
+ void clearExits();
+ void unselectExits();
+ void updateExit(ObjID id);
+
+ void printText(const Common::String &text);
+
+ //Dialog interactions
+ void showPrebuiltDialog(PrebuiltDialogs type);
+ bool isDialogOpen();
+
+ void getTextFromUser();
+ void setTextInput(Common::String str);
+ void closeDialog();
+
+ void loadGame();
+ void saveGame();
+ void newGame();
+ void quitGame();
+
+ void createInnerSurface(Graphics::ManagedSurface *innerSurface, Graphics::ManagedSurface *outerSurface, const BorderBounds &borders);
+
+
+private: // Attributes
+
+ MacVentureEngine *_engine;
+ Common::MacResManager *_resourceManager;
+
+ Graphics::ManagedSurface _screen;
+ Graphics::MacWindowManager _wm;
+
+ Common::List<WindowData> *_windowData;
+ Common::Array<CommandButton> *_controlData;
+ Common::Array<CommandButton> *_exitsData;
+
+ Graphics::MacWindow *_controlsWindow;
+ Graphics::MacWindow *_mainGameWindow;
+ Graphics::MacWindow *_outConsoleWindow;
+ Graphics::MacWindow *_selfWindow;
+ Graphics::MacWindow *_exitsWindow;
+ Graphics::MacWindow *_diplomaWindow;
+ Common::Array<Graphics::MacWindow*> _inventoryWindows;
+ Graphics::Menu *_menu;
+ Dialog *_dialog;
+
+ Container *_graphics;
+ Common::HashMap<ObjID, ImageAsset*> _assets;
+
+ Graphics::ManagedSurface _draggedSurface;
+ DraggedObj _draggedObj;
+
+ Cursor *_cursor;
+
+ ConsoleText *_consoleText;
+
+private: // Methods
+
+ // Initializers
+ void initGUI();
+ void initWindows();
+ void assignObjReferences(); // Mainly guesswork
+
+ // Loaders
+ bool loadMenus();
+ bool loadWindows();
+ bool loadControls();
+ void loadBorders(Graphics::MacWindow *target, MVWindowType type);
+ void loadBorder(Graphics::MacWindow *target, MVWindowType type, bool active);
+ void loadGraphics();
+ void clearAssets();
+
+ // Drawers
+ void drawWindows();
+ void drawCommandsWindow();
+ void drawMainGameWindow();
+ void drawSelfWindow();
+ void drawInventories();
+ void drawExitsWindow();
+ void drawConsoleWindow();
+
+ void drawDraggedObject();
+ void drawObjectsInWindow(const WindowData &targetData, Graphics::ManagedSurface *surface);
+ void drawWindowTitle(WindowReference target, Graphics::ManagedSurface *surface);
+ void drawDialog();
+
+ void moveDraggedObject(Common::Point target);
+
+ // Finders
+ WindowReference findWindowAtPoint(Common::Point point);
+ Common::Point getGlobalScrolledSurfacePosition(WindowReference reference);
+ WindowData& findWindowData(WindowReference reference);
+ Graphics::MacWindow *findWindow(WindowReference reference);
+
+ // Utils
+ void checkSelect(const WindowData &data, Common::Point pos, const Common::Rect &clickRect, WindowReference ref);
+ bool canBeSelected(ObjID obj, const Common::Rect &clickRect, WindowReference ref);
+ bool isRectInsideObject(Common::Rect target, ObjID obj);
+ void selectDraggable(ObjID child, WindowReference origin, Common::Point startPos);
+ void handleDragRelease(bool shiftPressed, bool isDoubleClick);
+ Common::Rect calculateClickRect(Common::Point clickPos, Common::Rect windowBounds);
+ Common::Point localizeTravelledDistance(Common::Point point, WindowReference origin, WindowReference target);
+ void removeInventoryWindow(WindowReference ref);
+
+ void ensureAssetLoaded(ObjID obj);
+
+};
+
+enum ClickState {
+ kCursorIdle = 0,
+ kCursorSCStart = 1,
+ kCursorSCDrag = 2,
+ kCursorDCStart = 3,
+ kCursorDCDo = 4,
+ kCursorSCSink = 5,
+ kCursorStateCount
+};
+
+enum CursorInput { // Columns for the FSM transition table
+ kButtonDownCol = 0,
+ kButtonUpCol = 1,
+ kTickCol = 2,
+ kCursorInputCount
+};
+
+class Cursor {
+
+public:
+ Cursor(Gui *gui);
+ ~Cursor();
+
+ void tick();
+ bool processEvent(const Common::Event &event);
+ Common::Point getPos();
+ bool canSelectDraggable();
+
+private:
+
+ void changeState(CursorInput input);
+ void executeStateIn();
+ void executeStateOut();
+
+
+private:
+ Gui *_gui;
+
+ Common::Point _pos;
+ ClickState _state;
+};
+
+
+
+enum {
+ kConsoleLeftOffset = 2
+};
+
+class ConsoleText {
+
+public:
+
+ ConsoleText(Gui *gui) {
+ _gui = gui;
+ _lines.push_back("");
+ updateScroll();
+ }
+
+ ~ConsoleText() {
+
+ }
+
+ void printLine(const Common::String &str, int maxW) {
+ Common::StringArray wrappedLines;
+ int textW = maxW;
+ const Graphics::Font *font = &_gui->getCurrentFont();
+
+ font->wordWrapText(str, textW, wrappedLines);
+
+ if (wrappedLines.empty()) // Sometimes we have empty lines
+ _lines.push_back("");
+
+ for (Common::StringArray::const_iterator j = wrappedLines.begin(); j != wrappedLines.end(); ++j) {
+ _lines.push_back(*j);
+ }
+
+ updateScroll();
+ }
+
+ void renderInto(Graphics::ManagedSurface *target, const BorderBounds borders, int textOffset) {
+ target->fillRect(target->getBounds(), kColorWhite);
+
+ Graphics::ManagedSurface *composeSurface = new Graphics::ManagedSurface();
+ _gui->createInnerSurface(composeSurface, target, borders);
+ composeSurface->clear(kColorGreen);
+
+ const Graphics::Font *font = &_gui->getCurrentFont();
+ uint y = target->h - font->getFontHeight();
+ for (uint i = _scrollPos; i != 0; i--) {
+ font->drawString(target, _lines[i], textOffset, y, font->getStringWidth(_lines[i]), kColorBlack);
+ y -= font->getFontHeight();
+ }
+
+ Common::Point composePosition = Common::Point(borders.leftOffset, borders.topOffset);
+ target->transBlitFrom(*composeSurface, composePosition, kColorGreen);
+ delete composeSurface;
+ }
+
+ void updateScroll() {
+ _scrollPos = _lines.size() - 1;
+ }
+
+ void scrollDown() {
+ if (_scrollPos < (int)(_lines.size() - 1)) {
+ _scrollPos++;
+ }
+ }
+
+ void scrollUp() {
+ if (_scrollPos > 0) {
+ _scrollPos--;
+ }
+ }
+
+
+private:
+
+ Gui *_gui;
+
+ Common::StringArray _lines;
+ int _scrollPos;
+
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/hufflists.h b/engines/macventure/hufflists.h
new file mode 100644
index 0000000000..1ea4f21535
--- /dev/null
+++ b/engines/macventure/hufflists.h
@@ -0,0 +1,57 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_HUFFLIST_H
+#define MACVENTURE_HUFFLIST_H
+
+namespace MacVenture {
+
+// The engine uses a <= comparison instead of ==, so I can't use Common::Huffman
+class HuffmanLists {
+public:
+ HuffmanLists() {
+ _numEntries = 0;
+ }
+ HuffmanLists(uint32 num, uint32 *lens, uint32 *masks, uint32 *symbols) {
+ _numEntries = num;
+ _lens = Common::Array<uint32>(lens, num);
+ _masks = Common::Array<uint32>(masks, num);
+ _symbols = Common::Array<uint32>(symbols, num);
+ }
+ ~HuffmanLists() {}
+
+
+ uint32 getNumEntries() const { return _numEntries; }
+ uint32 getLength(uint32 index) const { return _lens[index]; }
+ uint32 getMask(uint32 index) const { return _masks[index]; }
+ uint32 getSymbol(uint32 index) const { return _symbols[index]; }
+
+private:
+ uint32 _numEntries;
+ Common::Array<uint32> _lens;
+ Common::Array<uint32> _masks;
+ Common::Array<uint32> _symbols;
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/image.cpp b/engines/macventure/image.cpp
new file mode 100644
index 0000000000..172121af0b
--- /dev/null
+++ b/engines/macventure/image.cpp
@@ -0,0 +1,553 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/image.h"
+
+namespace MacVenture {
+
+static const PPICHuff PPIC1Huff = {
+ // Masks
+ { 0x0000,0x2000,0x4000,0x5000,0x6000,0x7000,0x8000,0x9000,0xa000,
+ 0xb000,0xc000,0xd000,0xd800,0xe000,0xe800,0xf000,0xf800 },
+ // Lens
+ { 3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5 },
+ // Symbols
+ { 0x00,0x0f,0x03,0x05,0x06,0x07,0x08,0x09,0x0a,0x0c,0xff,0x01,
+ 0x02,0x04,0x0b,0x0d,0xe }
+};
+
+static const PPICHuff PPIC2Huff = {
+ // Masks
+ { 0x0000,0x4000,0x8000,0xc000,0xc800,0xd000,0xd800,0xe000,0xe800,
+ 0xf000,0xf400,0xf600,0xf800,0xfa00,0xfc00,0xfe00,0xff00 },
+ // Lens
+ { 2,2,2,5,5,5,5,5,5,6,7,7,7,7,7,8,8 },
+ // Symbols
+ { 0xff,0x00,0x0f,0x01,0x03,0x07,0x0e,0x0c,0x08,0x06,0x02,0x04,
+ 0x09,0x0d,0x0b,0x0a,0x05 }
+};
+
+// Used to load the huffman table in PPIC3 decoding
+static const byte loadBits[] = {
+ 0x08, 0x0f, 0x02, 0xff, 0x00,
+ 0x04, 0xff, 0x01,
+ 0x07, 0x09, 0x08, 0xff, 0x03,
+ 0x04, 0xff, 0x04,
+ 0x0a, 0x07, 0x0a, 0x0b, 0x06, 0xff, 0x05,
+ 0x06, 0x06, 0x0b, 0xff, 0x07,
+ 0x03, 0xff, 0x09,
+ 0x04, 0x03, 0x0e, 0xff, 0x0c,
+ 0x02, 0xff, 0x0d,
+ 0x01, 0xff, 0x0f,
+ 0xff
+};
+
+ImageAsset::ImageAsset(ObjID original, Container *container) {
+ _id = (original * 2);
+ _mask = (original * 2) + 1;
+
+ uint imgRowBytes = 0;
+ uint imgBitWidth = 0;
+ uint imgBitHeight = 0;
+ uint maskRowBytes = 0;
+ uint maskBitWidth = 0;
+ uint maskBitHeight = 0;
+
+ _container = container;
+ decodePPIC(_id, _imgData, imgBitHeight, imgBitWidth, imgRowBytes);
+ _imgRowBytes = imgRowBytes;
+ _imgBitWidth = imgBitWidth;
+ _imgBitHeight = imgBitHeight;
+
+ if (_container->getItemByteSize(_mask)) {
+ decodePPIC(_mask, _maskData, maskBitHeight, maskBitWidth, maskRowBytes);
+ }
+ _maskRowBytes = maskRowBytes;
+ _maskBitWidth = maskBitWidth;
+ _maskBitHeight = maskBitHeight;
+}
+
+ImageAsset::~ImageAsset() {
+ debugC(3, kMVDebugImage, "~ImageAsset(%d)", _id / 2);
+}
+
+void ImageAsset::decodePPIC(ObjID id, Common::Array<byte> &data, uint &bitHeight, uint &bitWidth, uint &rowBytes) {
+ ObjID realID = id;
+ uint32 size = _container->getItemByteSize(id);
+ if (size < 2) {
+ rowBytes = 0;
+ bitHeight = 0;
+ bitWidth = 0;
+ return;
+ }
+ if (size == 2) {
+ Common::SeekableReadStream *newItemStream = _container->getItem(id);
+ realID = newItemStream->readUint16BE();
+ delete newItemStream;
+ }
+ Common::SeekableReadStream *baseStream = _container->getItem(realID);
+ Common::BitStream32BEMSB stream(baseStream);
+
+ uint8 mode = stream.getBits(3);
+ int w, h;
+ if (stream.getBit()) {
+ h = stream.getBits(10);
+ } else {
+ h = stream.getBits(6);
+ }
+
+ if (stream.getBit()) {
+ w = stream.getBits(10);
+ } else {
+ w = stream.getBits(6);
+ }
+
+ rowBytes = ((w + 0xF) >> 3) & 0xFFFE;
+ bitWidth = w;
+ bitHeight = h;
+
+ for (uint i = 0; i < rowBytes * h; i++) {
+ data.push_back(0);
+ }
+
+ switch (mode) {
+ case MacVenture::kPPIC0:
+ decodePPIC0(stream, data, bitHeight, bitWidth, rowBytes);
+ break;
+ case MacVenture::kPPIC1:
+ decodePPIC1(stream, data, bitHeight, bitWidth, rowBytes);
+ break;
+ case MacVenture::kPPIC2:
+ decodePPIC2(stream, data, bitHeight, bitWidth, rowBytes);
+ break;
+ case MacVenture::kPPIC3:
+ decodePPIC3(stream, data, bitHeight, bitWidth, rowBytes);
+ break;
+ }
+
+ delete baseStream;
+}
+
+void ImageAsset::decodePPIC0(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ uint words = bitWidth >> 4;
+ uint bytes = bitWidth & 0xF;
+ uint v = 0;
+ uint p = 0;
+ for (uint y = 0; y < bitHeight; y++) {
+ for (uint x = 0; x < words; x++) {
+ v = stream.peekBits(32);
+ stream.skip(16);
+ v >>= 16 - (stream.pos() % 8);
+ data[p] = (v >> 8) & 0xff; p++;
+ data[p] = v & 0xff; p++;
+ }
+ if (bytes) {
+ v = stream.getBits(bytes);
+ v <<= 16 - bytes;
+ data[p] = (v >> 8) & 0xff; p++;
+ data[p] = v & 0xff; p++;
+ }
+ }
+
+}
+
+void ImageAsset::decodePPIC1(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ decodeHuffGraphic(PPIC1Huff, stream, data, bitHeight, bitWidth, rowBytes);
+}
+
+void ImageAsset::decodePPIC2(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ decodeHuffGraphic(PPIC2Huff, stream, data, bitHeight, bitWidth, rowBytes);
+}
+
+void ImageAsset::decodePPIC3(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ // We need to load the huffman from the PPIC itself
+ PPICHuff huff;
+ uint16 v, bits;
+ uint16 load = 0;
+ while ((bits = loadBits[load++]) != 0xFF) {
+ v = stream.getBits(bits);
+ while ((bits = loadBits[load++]) != 0xFF) {
+ huff.symbols[loadBits[load++]] = v % bits;
+ v = (bits != 0) ? (v / bits) : 0;
+ }
+ huff.symbols[loadBits[load++]] = v;
+ }
+ huff.symbols[0x10] = 0;
+ for (uint i = 0x10; i > 0; i--) {
+ for (uint j = i; j <= 0x10; j++) {
+ if (huff.symbols[j] >= huff.symbols[i - 1]) {
+ huff.symbols[j]++;
+ }
+ }
+ }
+
+ for (int i = 0x10; i >= 0; i--) {
+ if (huff.symbols[i] == 0x10) {
+ huff.symbols[i] = 0xff;
+ break;
+ }
+ }
+
+ bits = stream.getBits(2) + 1;
+ uint16 mask = 0;
+ for (uint i = 0; i < 0xf; i++) {
+ if (i) {
+ while (!stream.getBit()) {
+ bits++;
+ }
+ }
+ huff.lens[i] = bits;
+ huff.masks[i] = mask;
+ mask += 1 << (16 - bits);
+ }
+ huff.masks[0xf] = mask;
+ while (mask&(1 << (16 - bits))) {
+ bits++;
+ }
+ huff.masks[0x10] = mask | (1 << (16 - bits));
+ huff.lens[0xf] = bits;
+ huff.lens[0x10] = bits;
+
+ decodeHuffGraphic(huff, stream, data, bitHeight, bitWidth, rowBytes);
+}
+
+void ImageAsset::decodeHuffGraphic(const PPICHuff &huff, Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ byte flags = 0;
+ _walkRepeat = 0;
+ _walkLast = 0;
+ if (bitWidth & 3) {
+ flags = stream.getBits(5);
+ } else {
+ flags = stream.getBits(4) << 1;
+ }
+
+ byte odd = 0;
+ byte blank = bitWidth & 0xf;
+ if (blank) {
+ blank >>= 2;
+ odd = blank & 1;
+ blank = 2 - (blank >> 1);
+ }
+
+ uint16 pos = 0;
+ for (uint y = 0; y < bitHeight; y++) {
+ uint16 x = 0;
+ for (; x < bitWidth >> 3; x++) {
+ byte hi = walkHuff(huff, stream) << 4;
+ data[pos++] = walkHuff(huff, stream) | hi;
+ }
+ if (odd) {
+ data[pos] = walkHuff(huff, stream) << 4;
+ }
+ pos += blank;
+ }
+
+ uint16 edge = bitWidth & 3;
+ if (edge) {
+ pos = rowBytes - blank;
+ uint16 bits = 0;
+ uint16 val = 0;
+ uint16 v;
+ for (uint y = 0; y < bitHeight; y++) {
+ if (flags & 1) {
+ if (bits < edge) {
+ v = walkHuff(huff, stream) << 4;
+ val |= v >> bits;
+ bits += 4;
+ }
+ bits -= edge;
+ v = val;
+ val <<= edge;
+ val &= 0xFF;
+ } else {
+ v = stream.getBits(edge);
+ v <<= 8 - edge;
+ }
+ if (odd)
+ v >>= 4;
+
+ data[pos] |= v & 0xff;
+ pos += rowBytes;
+ }
+ }
+ if (flags & 8) {
+ pos = 0;
+ for (uint y = 0; y < bitHeight; y++) {
+ uint16 v = 0;
+ if (flags & 2) {
+ for (uint x = 0; x < rowBytes; x++) {
+ data[pos] ^= v;
+ v = data[pos];
+ pos++;
+ }
+ } else {
+ for (uint x = 0; x < rowBytes; x++) {
+ uint16 val = data[pos] ^ v;
+ val ^= (val >> 4) & 0xf;
+ data[pos] = val;
+ pos++;
+ v = (val << 4) & 0xff;
+ }
+ }
+ }
+ }
+ if (flags & 4) {
+ uint16 delta = rowBytes * 4;
+ if (flags & 2) {
+ delta *= 2;
+ }
+ pos = 0;
+ uint q = delta;
+ for (uint i = 0; i < bitHeight * rowBytes - delta; i++) {
+ data[q] ^= data[pos];
+ q++;
+ pos++;
+ }
+ }
+}
+
+byte ImageAsset::walkHuff(const PPICHuff &huff, Common::BitStream &stream) {
+ if (_walkRepeat) {
+ _walkRepeat--;
+ _walkLast = ((_walkLast << 8) & 0xFF00) | (_walkLast >> 8);
+ return _walkLast & 0xFF;
+ }
+ uint16 dw = stream.peekBits(16);
+ uint16 i = 0;
+ for (;i < 16; i++) {
+ if (huff.masks[i + 1] > dw) {
+ break;
+ }
+ }
+ stream.skip(huff.lens[i]);
+ uint8 val = huff.symbols[i];
+ if (val == 0xFF) {
+ if (!stream.getBit()) {
+ _walkLast &= 0xFF;
+ _walkLast |= _walkLast << 8;
+ }
+ _walkRepeat = stream.getBits(3);
+ if (_walkRepeat < 3) {
+ _walkRepeat <<= 4;
+ _walkRepeat |= stream.getBits(4);
+ if (_walkRepeat < 8) {
+ _walkRepeat <<= 8;
+ _walkRepeat |= stream.getBits(8);
+ }
+ }
+ _walkRepeat -= 2;
+ _walkLast = ((_walkLast << 8) & 0xFF00) | (_walkLast >> 8);
+ return _walkLast & 0xFF;
+ } else {
+ _walkLast <<= 8;
+ _walkLast |= val;
+ _walkLast &= 0xFFFF;
+ }
+ return val;
+}
+
+void ImageAsset::blitInto(Graphics::ManagedSurface *target, int x, int y, BlitMode mode) {
+ if (mode == kBlitDirect) {
+ blitDirect(target, x, y, _imgData, _imgBitHeight, _imgBitWidth, _imgRowBytes);
+ } else if (mode < kBlitXOR) {
+ if (_container->getItemByteSize(_mask)) { // Has mask
+ switch (mode) {
+ case MacVenture::kBlitBIC:
+ blitBIC(target, x, y, _maskData, _maskBitHeight, _maskBitWidth, _maskRowBytes);
+ break;
+ case MacVenture::kBlitOR:
+ blitOR(target, x, y, _maskData, _maskBitHeight, _maskBitWidth, _maskRowBytes);
+ break;
+ default:
+ break;
+ }
+ } else if (_container->getItemByteSize(_id)) {
+ switch (mode) {
+ case MacVenture::kBlitBIC:
+ target->fillRect(Common::Rect(x, y, x + _imgBitWidth, y + _imgBitHeight), kColorWhite);
+ break;
+ case MacVenture::kBlitOR:
+ target->fillRect(Common::Rect(x, y, x + _imgBitWidth, y + _imgBitHeight), kColorBlack);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_container->getItemByteSize(_id) && mode > 0) {
+ blitXOR(target, x, y, _imgData, _imgBitHeight, _imgBitWidth, _imgRowBytes);
+ }
+ }
+}
+
+bool ImageAsset::isPointInside(Common::Point point) {
+ if (point.x >= _maskBitWidth || point.y >= _maskBitHeight) {
+ return false;
+ }
+ if (_maskData.empty()) {
+ return false;
+ }
+ // We see if the point lands on the mask.
+ uint pix = _maskData[(point.y * _maskRowBytes) + (point.x >> 3)] & (1 << (7 - (point.x & 7)));
+ return pix != 0;
+}
+
+bool ImageAsset::isRectInside(Common::Rect rect) {
+ if (_maskData.empty()) {
+ return (rect.width() > 0 && rect.height() > 0);
+ }
+
+ for (int y = rect.top; y < rect.top + rect.height(); y++) {
+ uint bmpofs = y * _maskRowBytes;
+ byte pix;
+ for (int x = rect.left; x < rect.left + rect.width(); x++) {
+ pix = _maskData[bmpofs + (x >> 3)] & (1 << (7 - (x & 7)));
+ if (pix) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+int ImageAsset::getWidth() {
+ if (_imgData.size() == 0) {
+ return 0;
+ }
+ return MAX(0, (int)_imgBitWidth);
+}
+
+int ImageAsset::getHeight() {
+ if (_imgData.size() == 0) {
+ return 0;
+ }
+ return MAX(0, (int)_imgBitHeight);
+}
+
+void ImageAsset::blitDirect(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ uint sx, sy, w, h;
+ calculateSectionToDraw(target, ox, oy, bitWidth, bitHeight, sx, sy, w, h);
+
+ for (uint y = 0; y < h; y++) {
+ uint bmpofs = (y + sy) * rowBytes;
+ byte pix = 0;
+ for (uint x = 0; x < w; x++) {
+ assert(ox + x <= target->w);
+ assert(oy + y <= target->h);
+ pix = data[bmpofs + ((x + sx) >> 3)] & (1 << (7 - ((x + sx) & 7)));
+ pix = pix ? kColorBlack : kColorWhite;
+ *((byte *)target->getBasePtr(ox + x, oy + y)) = pix;
+ }
+ }
+}
+
+void ImageAsset::blitBIC(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ uint sx, sy, w, h;
+ calculateSectionToDraw(target, ox, oy, bitWidth, bitHeight, sx, sy, w, h);
+
+ for (uint y = 0; y < h; y++) {
+ uint bmpofs = (y + sy) * rowBytes;
+ byte pix = 0;
+ for (uint x = 0; x < w; x++) {
+ assert(ox + x <= target->w);
+ assert(oy + y <= target->h);
+ pix = data[bmpofs + ((x + sx) >> 3)] & (1 << (7 - ((x + sx) & 7)));
+ if (pix) {
+ *((byte *)target->getBasePtr(ox + x, oy + y)) = kColorWhite;
+ }
+ }
+ }
+}
+
+void ImageAsset::blitOR(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ uint sx, sy, w, h;
+ calculateSectionToDraw(target, ox, oy, bitWidth, bitHeight, sx, sy, w, h);
+
+ for (uint y = 0; y < h; y++) {
+ uint bmpofs = (y + sy) * rowBytes;
+ byte pix = 0;
+ for (uint x = 0; x < w; x++) {
+ assert(ox + x <= target->w);
+ assert(oy + y <= target->h);
+ pix = data[bmpofs + ((x + sx) >> 3)] & (1 << (7 - ((x + sx) & 7)));
+ if (pix) {
+ *((byte *)target->getBasePtr(ox + x, oy + y)) = kColorBlack;
+ }
+ }
+ }
+}
+
+void ImageAsset::blitXOR(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes) {
+ uint sx, sy, w, h;
+ calculateSectionToDraw(target, ox, oy, bitWidth, bitHeight, sx, sy, w, h);
+
+ for (uint y = 0; y < h; y++) {
+ uint bmpofs = (y + sy) * rowBytes;
+ byte pix = 0;
+ for (uint x = 0; x < w; x++) {
+ pix = data[bmpofs + ((x + sx) >> 3)] & (1 << (7 - ((x + sx) & 7)));
+ if (pix) { // We need to xor
+ assert(ox + x <= target->w);
+ assert(oy + y <= target->h);
+ byte p = *((byte *)target->getBasePtr(ox + x, oy + y));
+ *((byte *)target->getBasePtr(ox + x, oy + y)) =
+ (p == kColorWhite) ? kColorBlack : kColorWhite;
+ }
+ }
+ }
+}
+
+void ImageAsset::calculateSectionToDraw(Graphics::ManagedSurface *target, int &ox, int &oy, uint bitWidth, uint bitHeight, uint &sx, uint &sy, uint &w, uint &h) {
+
+ calculateSectionInDirection(target->w, bitWidth, ox, sx, w);
+ calculateSectionInDirection(target->h, bitHeight, oy, sy, h);
+
+ assert(w <= target->w);
+ assert((int)w >= 0);
+ assert(w <= bitWidth);
+ assert(h <= target->h);
+ assert((int)h >= 0);
+ assert(h <= bitHeight);
+}
+
+void ImageAsset::calculateSectionInDirection(uint targetWhole, uint originWhole, int &originPosition, uint &startPosition, uint &blittedWhole) {
+ startPosition = 0;
+ blittedWhole = originWhole;
+ if (originPosition < 0) {
+ if (ABS(originPosition) > (int)blittedWhole) {
+ blittedWhole = 0;
+ } else {
+ blittedWhole -= -originPosition;
+ }
+ startPosition = -originPosition;
+ originPosition = 0;
+ }
+ if (originPosition + blittedWhole > targetWhole) {
+ if (originPosition > (int)targetWhole) {
+ blittedWhole = 0;
+ } else {
+ blittedWhole = targetWhole - originPosition;
+ }
+ }
+}
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/image.h b/engines/macventure/image.h
new file mode 100644
index 0000000000..6f6b9600ac
--- /dev/null
+++ b/engines/macventure/image.h
@@ -0,0 +1,108 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_IMAGE_H
+#define MACVENTURE_IMAGE_H
+
+#include "macventure/macventure.h"
+#include "macventure/container.h"
+
+namespace MacVenture {
+
+typedef uint32 ObjID;
+class Container;
+
+
+enum BlitMode {
+ kBlitDirect = 0,
+ kBlitBIC = 1,
+ kBlitOR = 2,
+ kBlitXOR = 3
+};
+
+enum GraphicsEncoding {
+ kPPIC0 = 0,
+ kPPIC1 = 1,
+ kPPIC2 = 2,
+ kPPIC3 = 3
+};
+
+struct PPICHuff {
+ uint16 masks[17];
+ uint16 lens[17];
+ uint8 symbols[17];
+};
+
+class ImageAsset {
+public:
+ ImageAsset(ObjID original, Container *container);
+ ~ImageAsset();
+
+ void blitInto(Graphics::ManagedSurface *target, int x, int y, BlitMode mode);
+
+ bool isPointInside(Common::Point point);
+ bool isRectInside(Common::Rect rect);
+
+ int getWidth();
+ int getHeight();
+
+private:
+ void decodePPIC(ObjID id, Common::Array<byte> &data, uint &bitHeight, uint &bitWidth, uint &rowBytes);
+
+ void decodePPIC0(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ void decodePPIC1(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ void decodePPIC2(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ void decodePPIC3(Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+
+ void decodeHuffGraphic(const PPICHuff &huff, Common::BitStream &stream, Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ byte walkHuff(const PPICHuff &huff, Common::BitStream &stream);
+
+ void blitDirect(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ void blitBIC(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ void blitOR(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+ void blitXOR(Graphics::ManagedSurface *target, int ox, int oy, const Common::Array<byte> &data, uint bitHeight, uint bitWidth, uint rowBytes);
+
+ void calculateSectionToDraw(Graphics::ManagedSurface *target, int &ox, int &oy, uint bitWidth, uint bitHeight, uint &sx, uint &sy, uint &w, uint &h);
+ void calculateSectionInDirection(uint targetWhole, uint originWhole, int &originPosition, uint &startPosition, uint &blittedWhole);
+
+private:
+ ObjID _id;
+ ObjID _mask;
+ Container *_container;
+
+ uint16 _walkRepeat;
+ uint16 _walkLast;
+
+ Common::Array<byte> _imgData;
+ uint16 _imgRowBytes;
+ uint16 _imgBitWidth;
+ uint16 _imgBitHeight;
+
+ Common::Array<byte> _maskData;
+ uint16 _maskRowBytes;
+ uint16 _maskBitWidth;
+ uint16 _maskBitHeight;
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/macventure.cpp b/engines/macventure/macventure.cpp
new file mode 100644
index 0000000000..826409f30b
--- /dev/null
+++ b/engines/macventure/macventure.cpp
@@ -0,0 +1,1185 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/debug-channels.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/config-manager.h"
+#include "engines/util.h"
+
+#include "macventure/macventure.h"
+
+// To move
+#include "common/file.h"
+
+namespace MacVenture {
+
+// HACK, see below
+void toASCII(Common::String &str) {
+ debugC(3, kMVDebugMain, "toASCII: %s", str.c_str());
+ Common::String::iterator it = str.begin();
+ for (; it != str.end(); it++) {
+ if (*it == '\216') {
+ str.replace(it, it + 1, "e");
+ }
+ if (*it == '\210') {
+ str.replace(it, it + 1, "a");
+ }
+ }
+}
+
+enum {
+ kMaxMenuTitleLength = 30
+};
+
+MacVentureEngine::MacVentureEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst) {
+ _gameDescription = gameDesc;
+ _rnd = new Common::RandomSource("macventure");
+
+ initDebugChannels();
+
+ _debugger = NULL;
+ _resourceManager = NULL;
+ _globalSettings = NULL;
+ _gui = NULL;
+ _world = NULL;
+ _scriptEngine = NULL;
+ _filenames = NULL;
+
+ _decodingDirectArticles = NULL;
+ _decodingNamingArticles = NULL;
+ _decodingIndirectArticles = NULL;
+ _textHuffman = NULL;
+
+ _soundManager = NULL;
+
+ _dataBundle = NULL;
+
+ debug("MacVenture::MacVentureEngine()");
+}
+
+MacVentureEngine::~MacVentureEngine() {
+ debug("MacVenture::~MacVentureEngine()");
+
+ DebugMan.clearAllDebugChannels();
+
+ if (_rnd)
+ delete _rnd;
+
+ if (_debugger)
+ delete _debugger;
+
+ if (_resourceManager)
+ delete _resourceManager;
+
+ if (_globalSettings)
+ delete _globalSettings;
+
+ if (_gui)
+ delete _gui;
+
+ if (_world)
+ delete _world;
+
+ if (_scriptEngine)
+ delete _scriptEngine;
+
+ if (_filenames)
+ delete _filenames;
+
+ if (_decodingDirectArticles)
+ delete _decodingDirectArticles;
+
+ if (_decodingNamingArticles)
+ delete _decodingNamingArticles;
+
+ if (_decodingIndirectArticles)
+ delete _decodingIndirectArticles;
+
+ if (_textHuffman)
+ delete _textHuffman;
+
+ if (_soundManager)
+ delete _soundManager;
+
+ if (_dataBundle)
+ delete _dataBundle;
+}
+
+void MacVentureEngine::initDebugChannels() {
+ DebugMan.addDebugChannel(kMVDebugMain, "main", "Engine state");
+ DebugMan.addDebugChannel(kMVDebugGUI, "gui", "Gui");
+ DebugMan.addDebugChannel(kMVDebugText, "text", "Text decoders and printers");
+ DebugMan.addDebugChannel(kMVDebugImage, "image", "Image decoders and renderers");
+ DebugMan.addDebugChannel(kMVDebugScript, "script", "Script engine");
+ DebugMan.addDebugChannel(kMVDebugSound, "sound", "Sound decoders");
+ DebugMan.addDebugChannel(kMVDebugContainer, "container", "Containers");
+}
+
+Common::Error MacVentureEngine::run() {
+ debug("MacVenture::MacVentureEngine::init()");
+ initGraphics(kScreenWidth, kScreenHeight, true);
+
+ _debugger = new Console(this);
+
+ // Additional setup.
+ debug("MacVentureEngine::init");
+
+ _resourceManager = new Common::MacResManager();
+ if (!_resourceManager->open(getGameFileName()))
+ error("ENGINE: Could not open %s as a resource fork", getGameFileName());
+
+ // Engine-wide loading
+ if (!loadGlobalSettings())
+ error("ENGINE: Could not load the engine settings");
+
+ _oldTextEncoding = !loadTextHuffman();
+
+ _filenames = new StringTable(this, _resourceManager, kFilenamesStringTableID);
+ _decodingDirectArticles = new StringTable(this, _resourceManager, kCommonArticlesStringTableID);
+ _decodingNamingArticles = new StringTable(this, _resourceManager, kNamingArticlesStringTableID);
+ _decodingIndirectArticles = new StringTable(this, _resourceManager, kIndirectArticlesStringTableID);
+
+ loadDataBundle();
+
+ // Big class instantiation
+ _gui = new Gui(this, _resourceManager);
+ _world = new World(this, _resourceManager);
+ _scriptEngine = new ScriptEngine(this, _world);
+
+ _soundManager = new SoundManager(this, _mixer);
+
+ setInitialFlags();
+
+ int directSaveSlotLoading = ConfMan.getInt("save_slot");
+ if (directSaveSlotLoading >= 0) {
+ if (loadGameState(directSaveSlotLoading).getCode() != Common::kNoError) {
+ error("ENGINE: Could not load game from slot '%d'", directSaveSlotLoading);
+ }
+ } else {
+ setNewGameState();
+ }
+ selectControl(kStartOrResume);
+
+ _gui->addChild(kSelfWindow, 1);
+ _gui->updateWindow(kSelfWindow, false);
+
+ while (_gameState != kGameStateQuitting) {
+ processEvents();
+
+ if (_gameState != kGameStateQuitting && !_gui->isDialogOpen()) {
+
+ if (_prepared) {
+ _prepared = false;
+
+ if (!_halted)
+ updateState(false);
+
+ if (_cmdReady || _halted) {
+ _halted = false;
+ if (runScriptEngine()) {
+ _halted = true;
+ _paused = true;
+ } else {
+ _paused = false;
+ updateState(true);
+ updateControls();
+ updateExits();
+ }
+ }
+
+ if (_gameState == kGameStateWinnig || _gameState == kGameStateLosing) {
+ endGame();
+ }
+ }
+ }
+ refreshScreen();
+ }
+
+ return Common::kNoError;
+}
+
+void MacVentureEngine::refreshScreen() {
+ _gui->draw();
+ g_system->updateScreen();
+ g_system->delayMillis(50);
+}
+
+void MacVentureEngine::newGame() {
+ _world->startNewGame();
+ reset();
+ setInitialFlags();
+ setNewGameState();
+}
+
+void MacVentureEngine::setInitialFlags() {
+ _paused = false;
+ _halted = false;
+ _cmdReady = false;
+ _haltedAtEnd = false;
+ _haltedInSelection = false;
+ _clickToContinue = true;
+ _gameState = kGameStateInit;
+ _destObject = 0;
+ _prepared = true;
+}
+
+void MacVentureEngine::setNewGameState() {
+ _cmdReady = true;
+ ObjID playerParent = _world->getObjAttr(1, kAttrParentObject);
+ _currentSelection.push_back(playerParent);// Push the parent of the player
+ _world->setObjAttr(playerParent, kAttrContainerOpen, 1);
+}
+
+void MacVentureEngine::reset() {
+ resetInternals();
+ resetGui();
+}
+
+void MacVentureEngine::resetInternals() {
+ _scriptEngine->reset();
+ _currentSelection.clear();
+ _objQueue.clear();
+ _textQueue.clear();
+}
+
+void MacVentureEngine::resetGui() {
+ _gui->reloadInternals();
+ _gui->updateWindowInfo(kMainGameWindow, getParent(1), _world->getChildren(getParent(1), true));
+ // HACK! should update all inventories
+ _gui->ensureInventoryOpen(kInventoryStart, 1);
+ _gui->updateWindowInfo(kInventoryStart, 1, _world->getChildren(1, true));
+ updateControls();
+ updateExits();
+ refreshScreen();
+}
+
+void MacVentureEngine::requestQuit() {
+ // TODO: Display save game dialog and such
+ _gameState = kGameStateQuitting;
+}
+
+void MacVentureEngine::requestUnpause() {
+ _paused = false;
+ _gameState = kGameStatePlaying;
+}
+
+void MacVentureEngine::selectControl(ControlAction id) {
+ debugC(2, kMVDebugMain, "Select control %x", id);
+ if (id == kClickToContinue) {
+ _clickToContinue = false;
+ _paused = true;
+ return;
+ }
+
+ _selectedControl = id;
+ refreshReady();
+}
+
+void MacVentureEngine::refreshReady() {
+ switch (getInvolvedObjects()) {
+ case 0: // No selected object
+ _cmdReady = true;
+ break;
+ case 1: // We have some selected object
+ _cmdReady = _currentSelection.size() != 0;
+ break;
+ case 2:
+ if (_destObject > 0) // We have a destination seleted
+ _cmdReady = true;
+ break;
+ }
+}
+
+void MacVentureEngine::preparedToRun() {
+ _prepared = true;
+}
+
+void MacVentureEngine::gameChanged() {
+ _gameChanged = true;
+}
+
+void MacVentureEngine::winGame() {
+ _gui->showPrebuiltDialog(kWinGameDialog);
+ _gameState = kGameStateWinnig;
+}
+
+void MacVentureEngine::loseGame() {
+ _gui->showPrebuiltDialog(kLoseGameDialog);
+ _paused = true;
+ //_gameState = kGameStateLosing;
+}
+
+void MacVentureEngine::clickToContinue() {
+ _clickToContinue = true;
+}
+
+void MacVentureEngine::enqueueObject(ObjectQueueID type, ObjID objID, ObjID target) {
+ QueuedObject obj;
+ obj.id = type;
+
+ if (type == kUpdateObject && isObjEnqueued(objID)) {
+ return;
+ }
+
+ if (type == kUpdateWindow) {
+ obj.target = target;
+ }
+
+ if (type != kHightlightExits) {
+ obj.object = objID;
+ obj.parent = _world->getObjAttr(objID, kAttrParentObject);
+ obj.x = _world->getObjAttr(objID, kAttrPosX);
+ obj.y = _world->getObjAttr(objID, kAttrPosY);
+ obj.exitx = _world->getObjAttr(objID, kAttrExitX);
+ obj.exity = _world->getObjAttr(objID, kAttrExitY);
+ obj.hidden = _world->getObjAttr(objID, kAttrHiddenExit);
+ obj.offscreen = _world->getObjAttr(objID, kAttrInvisible);
+ obj.invisible = _world->getObjAttr(objID, kAttrUnclickable);
+ }
+ _objQueue.push_back(obj);
+}
+
+void MacVentureEngine::enqueueText(TextQueueID type, ObjID target, ObjID source, ObjID text) {
+ QueuedText newText;
+ newText.id = type;
+ newText.destination = target;
+ newText.source = source;
+ newText.asset = text;
+ _textQueue.push_back(newText);
+}
+
+void MacVentureEngine::enqueueSound(SoundQueueID type, ObjID target) {
+ QueuedSound newSound;
+ newSound.id = type;
+ newSound.reference = target;
+ _soundQueue.push_back(newSound);
+}
+
+void MacVentureEngine::handleObjectSelect(ObjID objID, WindowReference win, bool shiftPressed, bool isDoubleClick) {
+ if (win == kExitsWindow) {
+ win = kMainGameWindow;
+ }
+
+ const WindowData &windata = _gui->getWindowData(win);
+
+ if (shiftPressed) {
+ // TODO: Implement shift functionality.
+ } else {
+ if (_selectedControl && _currentSelection.size() > 0 && getInvolvedObjects() > 1) {
+ if (objID == 0) {
+ selectPrimaryObject(windata.objRef);
+ } else {
+ selectPrimaryObject(objID);
+ }
+ preparedToRun();
+ } else {
+ if (objID == 0) {
+ unselectAll();
+ objID = win;
+ }
+ if (objID > 0) {
+ int currentObjectIndex = findObjectInArray(objID, _currentSelection);
+
+ if (currentObjectIndex >= 0)
+ unselectAll();
+
+ if (isDoubleClick) {
+ selectObject(objID);
+ _destObject = objID;
+ setDeltaPoint(Common::Point(0, 0));
+ if (!_cmdReady) {
+ selectControl(kActivateObject);
+ _cmdReady = true;
+ }
+ } else {
+ selectObject(objID);
+ if (getInvolvedObjects() == 1)
+ _cmdReady = true;
+ }
+ preparedToRun();
+ }
+ }
+ }
+}
+
+void MacVentureEngine::handleObjectDrop(ObjID objID, Common::Point delta, ObjID newParent) {
+ _destObject = newParent;
+ setDeltaPoint(delta);
+ selectControl(kMoveObject);
+ refreshReady();
+ preparedToRun();
+}
+
+void MacVentureEngine::setDeltaPoint(Common::Point newPos) {
+ debugC(4, kMVDebugMain, "Update delta: Old(%d, %d), New(%d, %d)",
+ _deltaPoint.x, _deltaPoint.y,
+ newPos.x, newPos.y);
+ _deltaPoint = newPos;
+}
+
+void MacVentureEngine::focusObjWin(ObjID objID) {
+ _gui->bringToFront(getObjWindow(objID));
+}
+
+void MacVentureEngine::updateWindow(WindowReference winID) {
+ _gui->updateWindow(winID, true);
+}
+
+bool MacVentureEngine::showTextEntry(ObjID text, ObjID srcObj, ObjID destObj) {
+ debugC(3, kMVDebugMain, "Showing speech dialog, asset %d from %d to %d", text, srcObj, destObj);
+ _gui->getTextFromUser();
+
+ _prepared = false;
+ warning("Show text entry: not fully tested");
+ return true;
+}
+
+void MacVentureEngine::setTextInput(Common::String content) {
+ _prepared = true;
+ _userInput = content;
+ _clickToContinue = false;
+}
+
+Common::String MacVentureEngine::getUserInput() {
+ return _userInput;
+}
+
+
+Common::String MacVentureEngine::getStartGameFileName() {
+ Common::SeekableReadStream *res;
+ res = _resourceManager->getResource(MKTAG('S', 'T', 'R', ' '), kStartGameFilenameID);
+ if (!res)
+ return "";
+
+ byte length = res->readByte();
+ char *fileName = new char[length + 1];
+ res->read(fileName, length);
+ fileName[length] = '\0';
+ Common::String result = Common::String(fileName, length);
+ // HACK, see definition of toASCII
+ toASCII(result);
+
+ delete[] fileName;
+ delete res;
+
+ return result;
+}
+
+const GlobalSettings& MacVentureEngine::getGlobalSettings() const {
+ return *_globalSettings;
+}
+
+// Private engine methods
+void MacVentureEngine::processEvents() {
+ Common::Event event;
+
+ while (_eventMan->pollEvent(event)) {
+ if (_gui->processEvent(event))
+ continue;
+
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ _gameState = kGameStateQuitting;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool MacVenture::MacVentureEngine::runScriptEngine() {
+ debugC(3, kMVDebugMain, "Running script engine");
+ if (_haltedAtEnd) {
+ _haltedAtEnd = false;
+ if (_scriptEngine->resume(false)) {
+ _haltedAtEnd = true;
+ return true;
+ }
+ return false;
+ }
+
+ if (_haltedInSelection) {
+ _haltedInSelection = false;
+ if (_scriptEngine->resume(false)) {
+ _haltedInSelection = true;
+ return true;
+ }
+ updateState(true);
+ }
+
+ while (!_currentSelection.empty()) {
+ ObjID obj = _currentSelection.front();
+ _currentSelection.remove_at(0);
+ if (isGameRunning() && _world->isObjActive(obj)) {
+ if (_scriptEngine->runControl(_selectedControl, obj, _destObject, _deltaPoint)) {
+ _haltedInSelection = true;
+ return true;
+ }
+ updateState(true);
+ }
+ }
+ if (_selectedControl == 1) {
+ _gameChanged = false;
+ } else if (isGameRunning()) {
+ if (_scriptEngine->runControl(kTick, _selectedControl, _destObject, _deltaPoint)) {
+ _haltedAtEnd = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+void MacVentureEngine::endGame() {
+ requestQuit();
+}
+
+void MacVentureEngine::updateState(bool pause) {
+ _prepared = false;
+ runObjQueue();
+ printTexts();
+ playSounds(pause);
+}
+
+void MacVentureEngine::revert() {
+ _gui->invertWindowColors(kMainGameWindow);
+ preparedToRun();
+}
+
+void MacVentureEngine::runObjQueue() {
+ while (!_objQueue.empty()) {
+ uint32 biggest = 0;
+ uint32 index = 0;
+ uint32 temp;
+ for (uint i = 0; i < _objQueue.size(); i++) {
+ temp = _objQueue[i].id;
+ if (temp > biggest) {
+ biggest = temp;
+ index = i;
+ }
+ }
+ QueuedObject obj = _objQueue[index];
+ _objQueue.remove_at(index);
+ switch (obj.id) {
+ case 0x2:
+ focusObjectWindow(obj.object);
+ break;
+ case 0x3:
+ openObject(obj.object);
+ break;
+ case 0x4:
+ closeObject(obj.object);
+ break;
+ case 0x7:
+ checkObject(obj);
+ break;
+ case 0x8:
+ reflectSwap(obj.object, obj.target);
+ break;
+ case 0xc:
+ _world->setObjAttr(_gui->getWindowData(kMainGameWindow).refcon, kAttrContainerOpen, 0);
+ _world->setObjAttr(_world->getObjAttr(1, kAttrParentObject), kAttrContainerOpen, 1);
+ break;
+ case 0xd:
+ toggleExits();
+ break;
+ case 0xe:
+ zoomObject(obj.object);
+ break;
+ }
+ }
+}
+
+void MacVentureEngine::printTexts() {
+ for (uint i = 0; i < _textQueue.size(); i++) {
+ QueuedText text = _textQueue.front();
+ _textQueue.remove_at(0);
+ switch (text.id) {
+ case kTextNumber:
+ _gui->printText(Common::String(text.asset));
+ gameChanged();
+ break;
+ case kTextNewLine:
+ _gui->printText(Common::String(""));
+ gameChanged();
+ break;
+ case kTextPlain:
+ _gui->printText(_world->getText(text.asset, text.source, text.destination));
+ gameChanged();
+ break;
+ }
+ }
+}
+
+void MacVentureEngine::playSounds(bool pause) {
+ int delay = 0;
+ while (!_soundQueue.empty()) {
+ QueuedSound item = _soundQueue.front();
+ _soundQueue.remove_at(0);
+ switch (item.id) {
+ case kSoundPlay:
+ _soundManager->playSound(item.reference);
+ break;
+ case kSoundPlayAndWait:
+ delay = _soundManager->playSound(item.reference);
+ break;
+ case kSoundWait:
+ // Empty in the original.
+ break;
+ }
+ }
+ if (pause && delay > 0) {
+ warning("Sound pausing not yet tested. Pausing for %d", delay);
+ g_system->delayMillis(delay);
+ preparedToRun();
+ }
+}
+
+void MacVentureEngine::updateControls() {
+ selectControl(kNoCommand);
+ _gui->clearControls();
+ toggleExits();
+ resetVars();
+}
+
+void MacVentureEngine::resetVars() {
+ selectControl(kNoCommand);
+ _currentSelection.clear();
+ _destObject = 0;
+ setDeltaPoint(Common::Point(0, 0));
+ _cmdReady = false;
+}
+
+void MacVentureEngine::unselectAll() {
+ while (!_currentSelection.empty()) {
+ unselectObject(_currentSelection.front());
+ }
+}
+
+void MacVentureEngine::selectObject(ObjID objID) {
+ if (!_currentSelection.empty()) {
+ if (findParentWindow(objID) != findParentWindow(_currentSelection[0])) {
+ // TODO: Needs further testing, but it doesn't seem necessary.
+ //unselectAll();
+ }
+ }
+ if (findObjectInArray(objID, _currentSelection) == -1) {
+ _currentSelection.push_back(objID);
+ highlightExit(objID);
+ }
+}
+
+void MacVentureEngine::unselectObject(ObjID objID) {
+ int idxCur = findObjectInArray(objID, _currentSelection);
+ if (idxCur != -1) {
+ _currentSelection.remove_at(idxCur);
+ highlightExit(objID);
+ }
+}
+
+
+void MacVentureEngine::updateExits() {
+ _gui->clearExits();
+ _gui->unselectExits();
+
+ Common::Array<ObjID> exits = _world->getChildren(_world->getObjAttr(1, kAttrParentObject), true);
+ for (uint i = 0; i < exits.size(); i++)
+ _gui->updateExit(exits[i]);
+
+}
+
+int MacVentureEngine::findObjectInArray(ObjID objID, const Common::Array<ObjID> &list) {
+ // Find the object in the current selection
+ bool found = false;
+ uint i = 0;
+ while (i < list.size() && !found) {
+ if (list[i] == objID) {
+ found = true;
+ } else {
+ i++;
+ }
+ }
+ // HACK, should use iterator
+ return found ? i : -1;
+}
+
+uint MacVentureEngine::getPrefixNdx(ObjID obj) {
+ return _world->getObjAttr(obj, kAttrPrefixes);
+}
+
+Common::String MacVentureEngine::getPrefixString(uint flag, ObjID obj) {
+ uint ndx = getPrefixNdx(obj);
+ ndx = ((ndx) >> flag) & 3;
+ return _decodingNamingArticles->getString(ndx);
+}
+
+Common::String MacVentureEngine::getNoun(ObjID ndx) {
+ return _decodingIndirectArticles->getString(ndx);
+}
+
+void MacVentureEngine::highlightExit(ObjID objID) {
+ // TODO: It seems unnecessary since the GUI checks whether an object
+ // is selected, which includes exits.
+ warning("STUB: highlightExit");
+}
+
+void MacVentureEngine::selectPrimaryObject(ObjID objID) {
+ if (objID == _destObject) {
+ return;
+ }
+ int idx;
+ debugC(4, kMVDebugMain, "Select primary object (%d)", objID);
+ if (_destObject > 0 &&
+ (idx = findObjectInArray(_destObject, _currentSelection)) != -1) {
+ unselectAll();
+ }
+ _destObject = objID;
+ if (findObjectInArray(_destObject, _currentSelection) == -1) {
+ selectObject(_destObject);
+ }
+
+ _cmdReady = true;
+}
+
+void MacVentureEngine::focusObjectWindow(ObjID objID) {
+ if (objID) {
+ WindowReference win = getObjWindow(objID);
+ if (win)
+ _gui->bringToFront(win);
+ }
+}
+
+void MacVentureEngine::openObject(ObjID objID) {
+ debugC(3, kMVDebugMain, "Open Object[%d] parent[%d] x[%d] y[%d]",
+ objID,
+ _world->getObjAttr(objID, kAttrParentObject),
+ _world->getObjAttr(objID, kAttrPosX),
+ _world->getObjAttr(objID, kAttrPosY));
+
+ if (getObjWindow(objID)) {
+ return;
+ }
+ if (objID == _world->getObjAttr(1, kAttrParentObject)) {
+ _gui->updateWindowInfo(kMainGameWindow, objID, _world->getChildren(objID, true));
+ _gui->updateWindow(kMainGameWindow, _world->getObjAttr(objID, kAttrContainerOpen));
+ updateExits();
+ _gui->setWindowTitle(kMainGameWindow, _world->getText(objID, objID, objID)); // it ignores source and target in the original
+ } else { // Open inventory window
+ Common::Point p(_world->getObjAttr(objID, kAttrPosX), _world->getObjAttr(objID, kAttrPosY));
+ WindowReference invID = _gui->createInventoryWindow(objID);
+ _gui->setWindowTitle(invID, _world->getText(objID, objID, objID));
+ _gui->updateWindowInfo(invID, objID, _world->getChildren(objID, true));
+ _gui->updateWindow(invID, _world->getObjAttr(objID, kAttrContainerOpen));
+ }
+}
+
+void MacVentureEngine::closeObject(ObjID objID) {
+ warning("closeObject: not fully implemented");
+ _gui->tryCloseWindow(getObjWindow(objID));
+ return;
+}
+
+void MacVentureEngine::checkObject(QueuedObject old) {
+ bool hasChanged = false;
+ debugC(3, kMVDebugMain, "Check Object[%d] parent[%d] x[%d] y[%d]",
+ old.object,
+ old.parent,
+ old.x,
+ old.y);
+ ObjID id = old.object;
+ if (id == 1) {
+ if (old.parent != _world->getObjAttr(id, kAttrParentObject)) {
+ enqueueObject(kSetToPlayerParent, id);
+ }
+ if (old.offscreen != _world->getObjAttr(id, kAttrInvisible) ||
+ old.invisible != _world->getObjAttr(id, kAttrUnclickable)) {
+ updateWindow(findParentWindow(id));
+ }
+ } else if (old.parent != _world->getObjAttr(id, kAttrParentObject) ||
+ old.x != _world->getObjAttr(id, kAttrPosX) ||
+ old.y != _world->getObjAttr(id, kAttrPosY)) {
+ WindowReference oldWin = getObjWindow(old.parent);
+ if (oldWin) {
+ _gui->removeChild(oldWin, id);
+ hasChanged = true;
+ }
+
+ WindowReference newWin = findParentWindow(id);
+ if (newWin) {
+ _gui->addChild(newWin, id);
+ hasChanged = true;
+ }
+ } else if (old.offscreen != _world->getObjAttr(id, kAttrInvisible) ||
+ old.invisible != _world->getObjAttr(id, kAttrUnclickable)) {
+ updateWindow(findParentWindow(id));
+ }
+
+ if (_world->getObjAttr(id, kAttrIsExit)) {
+ if (hasChanged ||
+ old.hidden != _world->getObjAttr(id, kAttrHiddenExit) ||
+ old.exitx != _world->getObjAttr(id, kAttrExitX) ||
+ old.exity != _world->getObjAttr(id, kAttrExitY))
+ _gui->updateExit(id);
+ }
+ WindowReference win = getObjWindow(id);
+ ObjID cur = id;
+ ObjID root = _world->getObjAttr(1, kAttrParentObject);
+ while (cur != root) {
+ if (cur == 0 || !_world->getObjAttr(cur, kAttrContainerOpen)) {
+ break;
+ }
+ cur = _world->getObjAttr(cur, kAttrParentObject);
+ }
+ if (cur == root) {
+ if (win) {
+ return;
+ }
+ enqueueObject(kOpenWindow, id); //open
+ } else {
+ if (!win) {
+ return;
+ }
+ enqueueObject(kCloseWindow, id); //close
+ }
+
+ // Update children
+ Common::Array<ObjID> children = _world->getChildren(id, true);
+ for (uint i = 0; i < children.size(); i++) {
+ enqueueObject(kUpdateObject, children[i]);
+ }
+}
+
+void MacVentureEngine::reflectSwap(ObjID fromID, ObjID toID) {
+ WindowReference from = getObjWindow(fromID);
+ WindowReference to = getObjWindow(toID);
+ WindowReference tmp = to;
+ debugC(3, kMVDebugMain, "Swap Object[%d] to Object[%d], from win[%d] to win[%d] ",
+ fromID, toID, from, to);
+
+ if (!to) {
+ tmp = from;
+ }
+ if (tmp) {
+ Common::String newTitle = _world->getText(toID, 0, 0); // Ignores src and targ in the original
+ _gui->setWindowTitle(tmp, newTitle);
+ _gui->updateWindowInfo(tmp, toID, _world->getChildren(toID, true));
+ updateWindow(tmp);
+ }
+}
+
+void MacVentureEngine::toggleExits() {
+ Common::Array<ObjID> exits = _currentSelection;
+ while (!exits.empty()) {
+ ObjID obj = exits.front();
+ exits.remove_at(0);
+ highlightExit(obj);
+ updateWindow(findParentWindow(obj));
+ }
+}
+
+void MacVentureEngine::zoomObject(ObjID objID) {
+ warning("zoomObject: unimplemented");
+}
+
+bool MacVentureEngine::isObjEnqueued(ObjID objID) {
+ Common::Array<QueuedObject>::const_iterator it;
+ for (it = _objQueue.begin(); it != _objQueue.end(); it++) {
+ if ((*it).object == objID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MacVentureEngine::isGameRunning() {
+ return (_gameState == kGameStateInit || _gameState == kGameStatePlaying);
+}
+
+ControlAction MacVenture::MacVentureEngine::referenceToAction(ControlType id) {
+ switch (id) {
+ case MacVenture::kControlExitBox:
+ return kActivateObject;//?? Like this in the original
+ case MacVenture::kControlExamine:
+ return kExamine;
+ case MacVenture::kControlOpen:
+ return kOpen;
+ case MacVenture::kControlClose:
+ return kClose;
+ case MacVenture::kControlSpeak:
+ return kSpeak;
+ case MacVenture::kControlOperate:
+ return kOperate;
+ case MacVenture::kControlGo:
+ return kGo;
+ case MacVenture::kControlHit:
+ return kHit;
+ case MacVenture::kControlConsume:
+ return kConsume;
+ default:
+ return kNoCommand;
+ }
+}
+
+// Data retrieval
+
+bool MacVentureEngine::isPaused() {
+ return _paused;
+}
+
+bool MacVentureEngine::needsClickToContinue() {
+ return _clickToContinue;
+}
+
+Common::String MacVentureEngine::getCommandsPausedString() const {
+ return Common::String("Click to continue");
+}
+
+Common::String MacVentureEngine::getFilePath(FilePathID id) const {
+ if (id <= 3) { // We don't want a file in the subdirectory
+ return _filenames->getString(id);
+ } else { // We want a game file
+ return _filenames->getString(3) + "/" + _filenames->getString(id);
+ }
+}
+
+bool MacVentureEngine::isOldText() const {
+ return _oldTextEncoding;
+}
+
+const HuffmanLists *MacVentureEngine::getDecodingHuffman() const {
+ return _textHuffman;
+}
+
+uint32 MacVentureEngine::randBetween(uint32 min, uint32 max) {
+ return _rnd->getRandomNumber(max - min) + min;
+}
+
+uint32 MacVentureEngine::getInvolvedObjects() {
+ // If there is no valid control selected, we return a number too big
+ // to be useful. There is no control that uses that many objects.
+ return (_selectedControl ? getGlobalSettings()._cmdArgCnts[_selectedControl - 1] : 3000);
+}
+
+Common::Point MacVentureEngine::getObjPosition(ObjID objID) {
+ return Common::Point(_world->getObjAttr(objID, kAttrPosX), _world->getObjAttr(objID, kAttrPosY));
+}
+
+bool MacVentureEngine::isObjVisible(ObjID objID) {
+ return _world->getObjAttr(objID, kAttrInvisible) == 0;
+}
+
+bool MacVentureEngine::isObjClickable(ObjID objID) {
+ return _world->getObjAttr(objID, kAttrUnclickable) == 0;
+}
+
+bool MacVentureEngine::isObjSelected(ObjID objID) {
+ int idx = findObjectInArray(objID, _currentSelection);
+ return idx != -1;
+}
+
+bool MacVentureEngine::isObjExit(ObjID objID) {
+ return _world->getObjAttr(objID, kAttrIsExit);
+}
+
+bool MacVentureEngine::isHiddenExit(ObjID objID) {
+ return _world->getObjAttr(objID, kAttrHiddenExit);
+}
+
+Common::Point MacVentureEngine::getObjExitPosition(ObjID objID) {
+ uint x = _world->getObjAttr(objID, kAttrExitX);
+ uint y = _world->getObjAttr(objID, kAttrExitY);
+ return Common::Point(x, y);
+}
+
+ObjID MacVentureEngine::getParent(ObjID objID) {
+ return _world->getObjAttr(objID, kAttrParentObject);
+}
+
+Common::Rect MacVentureEngine::getObjBounds(ObjID objID) {
+ Common::Point pos = getObjPosition(objID);
+
+ WindowReference win = findParentWindow(objID);
+ if (win != kNoWindow) { // If it's not in a window YET, we don't really care about the border
+ BorderBounds bounds = borderBounds(_gui->getWindowData(win).type); // HACK
+ pos.x += bounds.leftOffset;
+ pos.y += bounds.topOffset;
+ }
+ Common::Point measures = _gui->getObjMeasures(objID);
+ uint w = measures.x;
+ uint h = measures.y;
+ return Common::Rect(pos.x, pos.y, pos.x + w, pos.y + h);
+}
+
+uint MacVentureEngine::getOverlapPercent(ObjID one, ObjID other) {
+ // If it's not the same parent, there's 0 overlap
+ if (_world->getObjAttr(one, kAttrParentObject) !=
+ _world->getObjAttr(other, kAttrParentObject))
+ return 0;
+
+ Common::Rect oneBounds = getObjBounds(one);
+ Common::Rect otherBounds = getObjBounds(other);
+ if (otherBounds.intersects(oneBounds) ||
+ oneBounds.intersects(otherBounds)) {
+ uint areaOne = oneBounds.width() * oneBounds.height();
+ uint areaOther = otherBounds.width() * otherBounds.height();
+ return (areaOne != 0) ? (areaOther * 100 / areaOne) : 0;
+ }
+ return 0;
+}
+
+WindowReference MacVentureEngine::getObjWindow(ObjID objID) {
+ return _gui->getObjWindow(objID);
+}
+
+WindowReference MacVentureEngine::findParentWindow(ObjID objID) {
+ if (objID == 1) {
+ return kSelfWindow;
+ }
+ ObjID parent = _world->getObjAttr(objID, kAttrParentObject);
+ if (parent == 0) {
+ return kNoWindow;
+ }
+ return getObjWindow(parent);
+}
+
+Common::Point MacVentureEngine::getDeltaPoint() {
+ return _deltaPoint;
+}
+
+ObjID MacVentureEngine::getDestObject() {
+ return _destObject;
+}
+
+ControlAction MacVentureEngine::getSelectedControl() {
+ return _selectedControl;
+}
+
+// Data loading
+
+bool MacVentureEngine::loadGlobalSettings() {
+ Common::MacResIDArray resArray;
+
+ if ((resArray = _resourceManager->getResIDArray(MKTAG('G', 'N', 'R', 'L'))).size() == 0)
+ return false;
+
+ Common::SeekableReadStream *res;
+ res = _resourceManager->getResource(MKTAG('G', 'N', 'R', 'L'), kGlobalSettingsID);
+ if (res) {
+ _globalSettings = new GlobalSettings();
+ _globalSettings->loadSettings(res);
+ delete res;
+ return true;
+ }
+ return false;
+}
+
+bool MacVentureEngine::loadTextHuffman() {
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+
+ if ((resArray = _resourceManager->getResIDArray(MKTAG('G', 'N', 'R', 'L'))).size() == 0)
+ return false;
+
+ res = _resourceManager->getResource(MKTAG('G', 'N', 'R', 'L'), kTextHuffmanTableID);
+ if (res) {
+ uint32 numEntries = res->readUint16BE();
+ res->readUint16BE(); // Skip
+
+ uint32 *masks = new uint32[numEntries];
+ for (uint i = 0; i < numEntries - 1; i++) {
+ // For some reason there are one lass mask than entries
+ masks[i] = res->readUint16BE();
+ }
+
+ uint32 *lengths = new uint32[numEntries];
+ for (uint i = 0; i < numEntries; i++) {
+ lengths[i] = res->readByte();
+ }
+
+ uint32 *values = new uint32[numEntries];
+ for (uint i = 0; i < numEntries; i++) {
+ values[i] = res->readByte();
+ }
+
+ _textHuffman = new HuffmanLists(numEntries, lengths, masks, values);
+ debugC(4, kMVDebugMain, "Text is huffman-encoded");
+
+ delete res;
+ delete[] masks;
+ delete[] lengths;
+ delete[] values;
+ return true;
+ }
+ return false;
+}
+
+// Global Settings
+GlobalSettings::GlobalSettings() {
+}
+
+GlobalSettings::~GlobalSettings() {
+
+}
+
+void GlobalSettings::loadSettings(Common::SeekableReadStream *dataStream) {
+ _numObjects = dataStream->readUint16BE();
+ _numGlobals = dataStream->readUint16BE();
+ _numCommands = dataStream->readUint16BE();
+ _numAttributes = dataStream->readUint16BE();
+ _numGroups = dataStream->readUint16BE();
+ dataStream->readUint16BE(); // unknown
+ _invTop = dataStream->readUint16BE();
+ _invLeft = dataStream->readUint16BE();
+ _invWidth = dataStream->readUint16BE();
+ _invHeight = dataStream->readUint16BE();
+ _invOffsetY = dataStream->readUint16BE();
+ _invOffsetX = dataStream->readSint16BE();
+ _defaultFont = dataStream->readUint16BE();
+ _defaultSize = dataStream->readUint16BE();
+
+ uint8 *attrIndices = new uint8[_numAttributes];
+ dataStream->read(attrIndices, _numAttributes);
+ _attrIndices = Common::Array<uint8>(attrIndices, _numAttributes);
+ delete[] attrIndices;
+
+ for (int i = 0; i < _numAttributes; i++) {
+ _attrMasks.push_back(dataStream->readUint16BE());
+ }
+
+ uint8 *attrShifts = new uint8[_numAttributes];
+ dataStream->read(attrShifts, _numAttributes);
+ _attrShifts = Common::Array<uint8>(attrShifts, _numAttributes);
+ delete[] attrShifts;
+
+ uint8 *cmdArgCnts = new uint8[_numCommands];
+ dataStream->read(cmdArgCnts, _numCommands);
+ _cmdArgCnts = Common::Array<uint8>(cmdArgCnts, _numCommands);
+ delete[] cmdArgCnts;
+
+ uint8 *commands = new uint8[_numCommands];
+ dataStream->read(commands, _numCommands);
+ _commands = Common::Array<uint8>(commands, _numCommands);
+ delete[] commands;
+}
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/macventure.h b/engines/macventure/macventure.h
new file mode 100644
index 0000000000..6908bc6050
--- /dev/null
+++ b/engines/macventure/macventure.h
@@ -0,0 +1,369 @@
+/* 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.
+ *
+ */
+
+#ifndef MACVENTURE_MACVENTURE_H
+#define MACVENTURE_MACVENTURE_H
+
+#include "engines/engine.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/random.h"
+#include "common/macresman.h"
+#include "common/huffman.h"
+#include "common/savefile.h"
+
+#include "gui/debugger.h"
+
+#include "macventure/debug.h"
+#include "macventure/gui.h"
+#include "macventure/world.h"
+#include "macventure/hufflists.h"
+#include "macventure/stringtable.h"
+#include "macventure/script.h"
+#include "macventure/controls.h"
+#include "macventure/windows.h"
+#include "macventure/sound.h"
+
+struct ADGameDescription;
+
+namespace MacVenture {
+
+class SaveFileManager;
+
+class Console;
+class World;
+class ScriptEngine;
+
+class SoundManager;
+
+typedef uint32 ObjID;
+
+// HACK, until I find a way to translate correctly
+extern void toASCII(Common::String &str);
+
+enum {
+ kScreenWidth = 512,
+ kScreenHeight = 342
+};
+
+enum {
+ kGlobalSettingsID = 0x80,
+ kDiplomaGeometryID = 0x81,
+ kTextHuffmanTableID = 0x83
+};
+
+enum {
+ kSaveGameStrID = 0x82,
+ kDiplomaFilenameID = 0x83,
+ kClickToContinueTextID = 0x84,
+ kStartGameFilenameID = 0x85
+};
+
+enum FilePathID {
+ kMCVID = 1,
+ kTitlePathID = 2,
+ kSubdirPathID = 3,
+ kObjectPathID = 4,
+ kFilterPathID = 5,
+ kTextPathID = 6,
+ kGraphicPathID = 7,
+ kSoundPathID = 8
+};
+
+
+class GlobalSettings {
+public:
+ GlobalSettings();
+ ~GlobalSettings();
+
+ void loadSettings(Common::SeekableReadStream *dataStream);
+
+// HACK MAybe this should be private, but the class is only here to handle
+// memory allocation/deallocation
+public:
+ uint16 _numObjects; // number of game objects defined
+ uint16 _numGlobals; // number of globals defined
+ uint16 _numCommands; // number of commands defined
+ uint16 _numAttributes; // number of attributes
+ uint16 _numGroups; // number of object groups
+ uint16 _invTop; // inventory window bounds
+ uint16 _invLeft;
+ uint16 _invHeight;
+ uint16 _invWidth;
+ uint16 _invOffsetY; // positioning offset for
+ uint16 _invOffsetX; // new inventory windows
+ uint16 _defaultFont; // default font
+ uint16 _defaultSize; // default font size
+ Common::Array<uint8> _attrIndices; // attribute indices into attribute table
+ Common::Array<uint16> _attrMasks; // attribute masks
+ Common::Array<uint8> _attrShifts; // attribute bit shifts
+ Common::Array<uint8> _cmdArgCnts; // command argument counts
+ Common::Array<uint8> _commands; // command buttons
+};
+
+enum GameState {
+ kGameStateInit,
+ kGameStatePlaying,
+ kGameStateWinnig,
+ kGameStateLosing,
+ kGameStateQuitting
+};
+
+enum ObjectQueueID {
+ kFocusWindow = 2,
+ kOpenWindow = 3,
+ kCloseWindow = 4,
+ kUpdateObject = 7,
+ kUpdateWindow = 8,
+ kSetToPlayerParent = 12,
+ kHightlightExits = 13,
+ kAnimateBack = 14
+};
+
+struct QueuedObject {
+ ObjectQueueID id;
+ ObjID object;
+ ObjID parent;
+ uint x;
+ uint y;
+ uint exitx;
+ uint exity;
+ bool hidden;
+ bool offscreen;
+ bool invisible;
+ ObjID target; // For swapping
+};
+
+enum TextQueueID {
+ kTextNumber = 1,
+ kTextNewLine = 2,
+ kTextPlain = 3
+};
+
+struct QueuedText {
+ TextQueueID id;
+ ObjID source;
+ ObjID destination;
+ ObjID asset;
+};
+
+enum SoundQueueID {
+ kSoundPlay = 1,
+ kSoundPlayAndWait = 2,
+ kSoundWait = 3
+};
+
+struct QueuedSound {
+ SoundQueueID id;
+ ObjID reference;
+};
+
+class MacVentureEngine : public Engine {
+
+public:
+ MacVentureEngine(OSystem *syst, const ADGameDescription *gameDesc);
+ ~MacVentureEngine();
+
+ virtual bool hasFeature(EngineFeature f) const;
+
+ virtual Common::Error run();
+
+ bool scummVMSaveLoadDialog(bool isSave);
+ bool canLoadGameStateCurrently();
+ bool canSaveGameStateCurrently();
+ virtual Common::Error loadGameState(int slot);
+ virtual Common::Error saveGameState(int slot, const Common::String &desc);
+ void newGame();
+ void setInitialFlags();
+ void setNewGameState();
+
+ void initDebugChannels();
+ void reset();
+ void resetInternals();
+ void resetGui();
+ void refreshScreen();
+
+ // datafiles.cpp
+ void loadDataBundle();
+ Common::SeekableReadStream *getBorderFile(MVWindowType windowType, bool isActive);
+
+ void requestQuit();
+ void requestUnpause();
+ void selectControl(ControlAction action);
+ void refreshReady();
+ void preparedToRun();
+ void gameChanged();
+ void winGame();
+ void loseGame();
+ void clickToContinue();
+
+ void updateState(bool pause);
+ void revert();
+
+ void enqueueObject(ObjectQueueID type, ObjID objID, ObjID target = 0);
+ void enqueueText(TextQueueID type, ObjID target, ObjID source, ObjID text);
+ void enqueueSound(SoundQueueID type, ObjID target);
+
+ void runObjQueue();
+ void printTexts();
+ void playSounds(bool pause);
+
+ void handleObjectSelect(ObjID objID, WindowReference win, bool shiftPressed, bool isDoubleClick);
+ void handleObjectDrop(ObjID objID, Common::Point delta, ObjID newParent);
+ void setDeltaPoint(Common::Point newPos);
+ void focusObjWin(ObjID objID);
+ void updateWindow(WindowReference winID);
+
+ bool showTextEntry(ObjID text, ObjID srcObj, ObjID destObj);
+ void setTextInput(Common::String content);
+ Common::String getUserInput();
+
+ // Data retrieval
+ Common::String getStartGameFileName();
+ bool isPaused();
+ bool needsClickToContinue();
+ Common::String getCommandsPausedString() const;
+ const GlobalSettings &getGlobalSettings() const;
+ Common::String getFilePath(FilePathID id) const;
+ bool isOldText() const;
+ const HuffmanLists *getDecodingHuffman() const;
+ uint32 randBetween(uint32 min, uint32 max);
+ uint32 getInvolvedObjects();
+ int findObjectInArray(ObjID objID, const Common::Array<ObjID> &list);
+ uint getPrefixNdx(ObjID obj);
+ Common::String getPrefixString(uint flag, ObjID obj);
+ Common::String getNoun(ObjID ndx);
+
+ // Attributes consult
+ Common::Point getObjPosition(ObjID objID);
+ bool isObjVisible(ObjID objID);
+ bool isObjClickable(ObjID objID);
+ bool isObjSelected(ObjID objID);
+ bool isObjExit(ObjID objID);
+ bool isHiddenExit(ObjID objID);
+ Common::Point getObjExitPosition(ObjID objID);
+ ObjID getParent(ObjID objID);
+
+ // Utils
+ ControlAction referenceToAction(ControlType id);
+
+ // Encapsulation HACK
+ Common::Rect getObjBounds(ObjID objID);
+ uint getOverlapPercent(ObjID one, ObjID other);
+
+ WindowReference getObjWindow(ObjID objID);
+ WindowReference findParentWindow(ObjID objID);
+
+ Common::Point getDeltaPoint();
+ ObjID getDestObject();
+ ControlAction getSelectedControl();
+
+private:
+ void processEvents();
+
+ bool runScriptEngine();
+ void endGame();
+ void updateControls();
+ void resetVars();
+
+ void unselectAll();
+ void selectObject(ObjID objID);
+ void unselectObject(ObjID objID);
+ void highlightExit(ObjID objID);
+ void selectPrimaryObject(ObjID objID);
+ void updateExits();
+
+ // Object queue methods
+ void focusObjectWindow(ObjID objID);
+ void openObject(ObjID objID);
+ void closeObject(ObjID objID);
+ void checkObject(QueuedObject objID);
+ void reflectSwap(ObjID fromID, ObjID toID);
+ void toggleExits();
+ void zoomObject(ObjID objID);
+
+ bool isObjEnqueued(ObjID obj);
+
+ bool isGameRunning();
+
+ // Data loading
+ bool loadGlobalSettings();
+ bool loadTextHuffman();
+
+ const char *getGameFileName() const;
+
+private: // Attributes
+
+ const ADGameDescription *_gameDescription;
+ Common::RandomSource *_rnd;
+
+ Common::MacResManager *_resourceManager;
+
+ Console *_debugger;
+ Gui *_gui;
+ World *_world;
+ ScriptEngine *_scriptEngine;
+
+ // String tables
+ StringTable *_filenames;
+ StringTable *_decodingDirectArticles;
+ StringTable *_decodingNamingArticles;
+ StringTable *_decodingIndirectArticles;
+
+ SoundManager *_soundManager;
+
+ Common::Archive *_dataBundle;
+
+ // Engine state
+ GameState _gameState;
+ GlobalSettings *_globalSettings;
+ HuffmanLists *_textHuffman;
+ bool _oldTextEncoding;
+ bool _paused, _halted, _cmdReady, _prepared;
+ bool _haltedAtEnd, _haltedInSelection;
+ bool _gameChanged;
+ bool _clickToContinue;
+
+ Common::Array<QueuedObject> _objQueue;
+ Common::Array<QueuedObject> _inQueue;
+ Common::Array<QueuedSound> _soundQueue;
+ Common::Array<QueuedText> _textQueue;
+
+ // Selections
+ ObjID _destObject;
+ ControlAction _selectedControl;
+ Common::Array<ObjID> _currentSelection;
+ Common::Point _deltaPoint;
+ Common::String _userInput;
+
+};
+
+
+class Console : public GUI::Debugger {
+public:
+ Console(MacVentureEngine *vm) {}
+ virtual ~Console(void) {}
+};
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/module.mk b/engines/macventure/module.mk
new file mode 100644
index 0000000000..227eb41e28
--- /dev/null
+++ b/engines/macventure/module.mk
@@ -0,0 +1,31 @@
+MODULE := engines/macventure
+
+MODULE_OBJS := \
+ container.o \
+ controls.o \
+ cursor.o \
+ datafiles.o \
+ detection.o \
+ dialog.o \
+ gui.o \
+ image.o \
+ macventure.o \
+ prebuilt_dialogs.o \
+ saveload.o \
+ script.o \
+ sound.o \
+ text.o \
+ windows.o \
+ world.o
+
+
+MODULE_DIRS += \
+ engines/macventure
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_MACVENTURE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/macventure/prebuilt_dialogs.cpp b/engines/macventure/prebuilt_dialogs.cpp
new file mode 100644
index 0000000000..6137fed219
--- /dev/null
+++ b/engines/macventure/prebuilt_dialogs.cpp
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/prebuilt_dialogs.h"
+
+namespace MacVenture {
+
+const PrebuiltDialog g_prebuiltDialogs[kPrebuiltDialogCount] = {
+
+ {/* kSaveAsDialog */
+ {0, 146, 456, 254},
+ {
+ {kDEButton, "YES", kDASaveAs, 24, 68, 120, 22},
+ {kDEButton, "NO", kDACloseDialog, 168, 68, 120, 22},
+ {kDEButton, "CANCEL", kDACloseDialog, 312, 68, 120, 22},
+ {kDEPlainText, "Save As...", kDANone, 100, 10, 340, 38},
+ {kDETextInput, "", kDANone, 100, 30, 340, 20},
+ {kDEEnd, "", kDANone, 0, 0, 0, 0}
+ }
+ },
+
+ { /* kSpeakDialog */
+ {20, 92, 400, 200},
+ {
+ {kDEButton, "OK", kDASubmit, 10, 70, 50, 20},
+ {kDEButton, "CANCEL", kDACloseDialog, 96, 70, 50, 20},
+ {kDEPlainText, "What would you like to say?", kDANone, 10, 10, 400, 20},
+ {kDETextInput, "", kDANone, 10, 25, 350, 40},
+ {kDEEnd, "", kDANone, 0, 0, 0, 0}
+ }
+ },
+
+ { /* kWinGameDialog */
+ {20, 100, 320, 200},
+ {
+ {kDEPlainText, "You Won!", kDANone, 20, 16, 280, 20},
+ {kDEPlainText, "What do you want to do?", kDANone, 20, 30, 280, 20},
+ {kDEButton, "New Game", kDANewGame, 20, 60, 70, 20},
+ {kDEButton, "Load", kDALoadGame, 110, 60, 70, 20},
+ {kDEButton, "Quit", kDAQuit, 200, 60, 70, 20},
+ {kDEEnd, "", kDANone, 0, 0, 0, 0}
+ }
+ },
+
+ { /* kLoseGameDialog */
+ {20, 100, 320, 200},
+ {
+ {kDEPlainText, "You Died", kDANone, 20, 16, 280, 20},
+ {kDEPlainText, "What do you want to do?", kDANone, 20, 30, 280, 20},
+ {kDEButton, "New Game", kDANewGame, 20, 60, 70, 20},
+ {kDEButton, "Load", kDALoadGame, 110, 60, 70, 20},
+ {kDEButton, "Quit", kDAQuit, 200, 60, 70, 20},
+ {kDEEnd, "", kDANone, 0, 0, 0, 0}
+ }
+ }
+};
+} // End of namespace MacVenture
diff --git a/engines/macventure/prebuilt_dialogs.h b/engines/macventure/prebuilt_dialogs.h
new file mode 100644
index 0000000000..9cd8f4c1a4
--- /dev/null
+++ b/engines/macventure/prebuilt_dialogs.h
@@ -0,0 +1,87 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_PREBUIT_DIALOGS_H
+#define MACVENTURE_PREBUIT_DIALOGS_H
+
+#include "common/rect.h"
+
+namespace MacVenture {
+
+enum DialogAction {
+ kDANone,
+ kDACloseDialog,
+ kDASubmit,
+ kDASaveAs,
+ kDALoadGame,
+ kDAQuit,
+ kDANewGame
+};
+
+enum PrebuiltDialogs {
+ kSaveAsDialog = 0, //TODO: Currently unused, we are using ScummVM dialogs instead.
+ kSpeakDialog = 1,
+ kWinGameDialog = 2,
+ kLoseGameDialog = 3,
+ kPrebuiltDialogCount
+};
+
+enum PrebuiltElementType {
+ kDEPlainText,
+ kDEButton,
+ kDETextInput,
+ kDEEnd
+};
+
+struct PrebuiltDialogBounds {
+ uint left;
+ uint top;
+ uint right;
+ uint bottom;
+};
+
+struct PrebuiltDialogElement {
+ PrebuiltElementType type;
+ const char *title;
+ DialogAction action;
+ uint left;
+ uint top;
+ uint width;
+ uint height;
+};
+
+// Prebuilt dialogs
+enum {
+ // HACK
+ kMaxPrebuiltDialogElements = 10
+};
+
+struct PrebuiltDialog {
+ PrebuiltDialogBounds bounds;
+ PrebuiltDialogElement elements[kMaxPrebuiltDialogElements];
+};
+
+extern const PrebuiltDialog g_prebuiltDialogs[];
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/saveload.cpp b/engines/macventure/saveload.cpp
new file mode 100644
index 0000000000..53736302d2
--- /dev/null
+++ b/engines/macventure/saveload.cpp
@@ -0,0 +1,197 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/macventure.h"
+
+#include "common/error.h"
+#include "common/savefile.h"
+#include "engines/savestate.h"
+#include "gui/saveload.h"
+#include "graphics/thumbnail.h"
+
+namespace MacVenture {
+
+#define MACVENTURE_SAVE_HEADER MKTAG('M', 'V', 'S', 'S') // (M)ac(V)enture (S)cummVM (S)ave (0x4d565353, uint32)
+#define MACVENTURE_SAVE_VERSION 1 //1 BYTE
+#define MACVENTURE_DESC_LENGTH 4 //4 BYTE for the metadata length
+
+SaveStateDescriptor loadMetaData(Common::SeekableReadStream *s, int slot) {
+ // Metadata is stored at the end of the file
+ // |THUMBNAIL |
+ // | |
+ // |DESCSIZE| DESCRIPTION |
+ // |HEADER |VERSION|DESCLEN|
+ s->seek(-(5 + MACVENTURE_DESC_LENGTH), SEEK_END);
+ uint32 sig = s->readUint32BE();
+ byte version = s->readByte();
+
+ SaveStateDescriptor desc(-1, ""); // init to an invalid save slot
+
+ if (sig != MACVENTURE_SAVE_HEADER || version > MACVENTURE_SAVE_VERSION)
+ return desc;
+
+ // Save is valid, set its slot number
+ desc.setSaveSlot(slot);
+
+ // Depends on MACVENTURE_DESC_LENGTH
+ uint32 metaSize = s->readUint32BE();
+ s->seek(-(5 + MACVENTURE_DESC_LENGTH + metaSize), SEEK_END);
+
+ // Load the thumbnail
+ Graphics::Surface *thumb = Graphics::loadThumbnail(*s);
+ desc.setThumbnail(thumb);
+
+ // Load the description
+ Common::String name;
+ uint32 descSize = s->readUint32BE();
+ for (uint32 i = 0; i < descSize; ++i) {
+ name += s->readByte();
+ }
+ desc.setDescription(name);
+
+ // Load date
+ uint32 saveDate = s->readUint32LE();
+ int day = (saveDate >> 24) & 0xFF;
+ int month = (saveDate >> 16) & 0xFF;
+ int year = saveDate & 0xFFFF;
+ desc.setSaveDate(year, month, day);
+
+ uint16 saveTime = s->readUint16LE();
+ int hour = (saveTime >> 8) & 0xFF;
+ int minutes = saveTime & 0xFF;
+ desc.setSaveTime(hour, minutes);
+
+ // Load playtime
+ uint32 playTime = s->readUint32LE();
+ desc.setPlayTime(playTime * 1000);
+
+ return desc;
+}
+
+uint saveCurrentDate(Common::OutSaveFile *file) {
+ TimeDate curTime;
+ g_system->getTimeAndDate(curTime);
+
+ uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
+
+ file->writeUint32LE(saveDate);
+ file->writeUint16LE(saveTime);
+
+ // Return the number of bytes occupied
+ return 6;
+}
+
+uint savePlayTime(Common::OutSaveFile *file) {
+ uint32 playTime = g_engine->getTotalPlayTime() / 1000;
+ file->writeUint32LE(playTime);
+ // Return the number of bytes occupied
+ return 4;
+}
+
+void writeMetaData(Common::OutSaveFile *file, Common::String desc) {
+
+ // Write thumbnail
+ uint thumbSize = file->pos();
+ Graphics::saveThumbnail(*file);
+ thumbSize = file->pos() - thumbSize;
+
+ // Write description
+ file->writeUint32BE(desc.size());
+ file->writeString(desc);
+
+ uint dateSize = saveCurrentDate(file);
+ uint playTimeSize = savePlayTime(file);
+
+ file->writeUint32BE(MACVENTURE_SAVE_HEADER);
+ file->writeByte(MACVENTURE_SAVE_VERSION);
+ file->writeUint32BE(4 + desc.size() + dateSize + playTimeSize + thumbSize);
+}
+
+Common::Error MacVentureEngine::loadGameState(int slot) {
+ Common::String saveFileName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
+ Common::InSaveFile *file;
+ if(!(file = getSaveFileManager()->openForLoading(saveFileName))) {
+ error("ENGINE: Missing savegame file %s", saveFileName.c_str());
+ }
+ _world->loadGameFrom(file);
+ reset();
+ return Common::kNoError;
+}
+
+Common::Error MacVentureEngine::saveGameState(int slot, const Common::String &desc) {
+ Common::String saveFileName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
+ Common::SaveFileManager *manager = getSaveFileManager();
+ // HACK Get a real name!
+ Common::OutSaveFile *file = manager->openForSaving(saveFileName);
+ _world->saveGameInto(file);
+ writeMetaData(file, desc);
+
+ file->finalize();
+ if (file->err()) {
+ warning("Could not save '%s' correctly.", saveFileName.c_str());
+ }
+ delete file;
+ return Common::kNoError;
+}
+
+bool MacVentureEngine::scummVMSaveLoadDialog(bool isSave) {
+ if (!isSave) {
+ // do loading
+ GUI::SaveLoadChooser dialog = GUI::SaveLoadChooser(Common::String("Load game:"), Common::String("Load"), false);
+ int slot = dialog.runModalWithCurrentTarget();
+
+ if (slot < 0)
+ return true;
+
+ return loadGameState(slot).getCode() == Common::kNoError;
+ }
+
+ // do saving
+ GUI::SaveLoadChooser dialog = GUI::SaveLoadChooser(Common::String("Save game:"), Common::String("Save"), true);
+ int slot = dialog.runModalWithCurrentTarget();
+ Common::String desc = dialog.getResultString();
+
+ if (desc.empty()) {
+ // create our own description for the saved game, the user didnt enter it
+ desc = dialog.createDefaultSaveDescription(slot);
+ }
+
+ /*
+ if (desc.size() > (1 << MACVENTURE_DESC_LENGTH * 8) - 1)
+ desc = Common::String(desc.c_str(), (1 << MACVENTURE_DESC_LENGTH * 8) - 1);
+ */
+ if (slot < 0)
+ return true;
+
+ return saveGameState(slot, desc).getCode() == Common::kNoError;
+}
+
+bool MacVentureEngine::canLoadGameStateCurrently() {
+ return true;
+}
+
+bool MacVentureEngine::canSaveGameStateCurrently() {
+ return true;
+}
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/script.cpp b/engines/macventure/script.cpp
new file mode 100644
index 0000000000..d3731489e4
--- /dev/null
+++ b/engines/macventure/script.cpp
@@ -0,0 +1,1251 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "common/system.h"
+
+#include "macventure/macventure.h"
+#include "macventure/script.h"
+#include "macventure/world.h"
+#include "macventure/container.h"
+
+namespace MacVenture {
+
+ScriptEngine::ScriptEngine(MacVentureEngine *engine, World *world) {
+ _engine = engine;
+ _world = world;
+ _scripts = new Container(_engine->getFilePath(kFilterPathID));
+}
+
+ScriptEngine::~ScriptEngine() {
+ if (_scripts)
+ delete _scripts;
+}
+
+bool ScriptEngine::runControl(ControlAction action, ObjID source, ObjID destination, Common::Point delta) {
+ EngineFrame frame;
+ frame.action = action;
+ frame.src = source;
+ frame.dest = destination;
+ frame.x = delta.x;
+ frame.y = delta.y;
+ frame.haltedInSaves = false;
+ frame.haltedInFirst = false;
+ frame.haltedInFamily = false;
+ _frames.push_back(frame);
+ debugC(3, kMVDebugScript, "Stored frame %d, action: %d src: %d dest: %d point: (%d, %d)",
+ _frames.size() - 1, frame.action, frame.src, frame.dest, frame.x, frame.y);
+
+ return resume(true);
+}
+
+bool ScriptEngine::resume(bool execAll) {
+ debugC(3, kMVDebugScript, "Resume Script");
+ while (_frames.size()) {
+ bool fail = execFrame(execAll);
+ if (fail) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ScriptEngine::reset() {
+ _frames.clear();
+}
+
+bool ScriptEngine::execFrame(bool execAll) {
+ bool doFirst = execAll;
+ bool doFamily = false;
+ bool fail;
+
+ EngineFrame *frame = &_frames.front();
+
+ // Do first dispatch script (script 0)
+ if (frame->haltedInFirst || doFirst) { // We were stuck or it's the first time
+ frame->haltedInFirst = false;
+ if (doFirst) {
+ fail = loadScript(frame, 0);
+ } else {
+ fail = resumeFunc(frame);
+ }
+ if (fail) {
+ frame->haltedInFirst = true;
+ _engine->preparedToRun();
+ return true;
+ }
+ doFamily = true;
+ frame->familyIdx = 0;
+ }
+
+ // Do scripts in the family of player (ObjID 1)
+ if (frame->haltedInFamily || doFamily) { // We have to do the family or we were stuck here
+ frame->haltedInFamily = false;
+ Common::Array<ObjID> family = _world->getFamily(_world->getObjAttr(1, kAttrParentObject), false);
+ for (uint32 i = frame->familyIdx; i < family.size(); i++) {
+ if (doFamily) {
+ fail = loadScript(frame, family[i]);
+ } else {
+ fail = resumeFunc(frame);
+ }
+ if (fail) { // We are stuck, so we don't shift the frame
+ frame->haltedInFamily = true;
+ frame->familyIdx = i;
+ _engine->preparedToRun();
+ return true;
+ }
+ doFamily = true;
+ }
+ }
+
+ // Halted in saves
+ if (frame->haltedInSaves) {
+ frame->haltedInSaves = false;
+ if (resumeFunc(frame)) {
+ frame->haltedInSaves = true;
+ _engine->preparedToRun();
+ return true;
+ }
+ }
+
+ int highest = 0;
+ uint localHigh = 0;
+ do { // Saved function calls
+ highest = 0;
+ for (uint i = 0; i < frame->saves.size(); i++) {
+ if (highest < frame->saves[i].rank) {
+ highest = frame->saves[i].rank;
+ localHigh = i;
+ }
+ }
+ if (highest) {
+ frame->saves[localHigh].rank = 0;
+ if (loadScript(frame, frame->saves[localHigh].func)) {
+ frame->haltedInSaves = true;
+ _engine->preparedToRun();
+ return true;
+ }
+ }
+ } while (highest);
+
+ _frames.pop_front();
+ return false;
+}
+
+bool ScriptEngine::loadScript(EngineFrame *frame, uint32 scriptID) {
+ if (_scripts->getItemByteSize(scriptID) > 0) {
+ debugC(2, kMVDebugScript, "Loading function %d", scriptID);
+ // Insert the new script at the front
+ frame->scripts.push_front(ScriptAsset(scriptID, _scripts));
+ return runFunc(frame);
+ }
+ return false;
+}
+
+bool ScriptEngine::resumeFunc(EngineFrame *frame) {
+ bool fail = runFunc(frame);
+ if (fail) {
+ return fail;
+ }
+ frame->scripts.pop_front();
+ if (frame->scripts.size())
+ return resumeFunc(frame);
+ return false;
+}
+
+bool ScriptEngine::runFunc(EngineFrame *frame) {
+ ScriptAsset &script = frame->scripts.front();
+ EngineState *state = &frame->state;
+ byte op;
+ while (script.hasNext()) {
+ op = script.fetch();
+ debugC(4, kMVDebugScript, "Running operation %d", op);
+ if (!(op & 0x80)) {
+ state->push(op);
+ } else {
+ switch (op) {
+ case 0x80: //get attribute
+ op80GATT(state, frame);
+ break;
+ case 0x81: //set attribute
+ op81SATT(state, frame);
+ break;
+ case 0x82: //sum children attribute
+ op82SUCH(state, frame);
+ break;
+ case 0x83: //push selected control
+ op83PUCT(state, frame);
+ break;
+ case 0x84: //push selected object
+ op84PUOB(state, frame);
+ break;
+ case 0x85: //push target
+ op85PUTA(state, frame);
+ break;
+ case 0x86: //push deltax
+ op86PUDX(state, frame);
+ break;
+ case 0x87: //push deltay
+ op87PUDY(state, frame);
+ break;
+ case 0x88: //push immediate.b
+ op88PUIB(state, frame, &script);
+ break;
+ case 0x89: //push immediate
+ op89PUI(state, frame, &script);
+ break;
+ case 0x8a: //get global
+ op8aGGLO(state, frame);
+ break;
+ case 0x8b: //set global
+ op8bSGLO(state, frame);
+ break;
+ case 0x8c: //random
+ op8cRAND(state, frame);
+ break;
+ case 0x8d: //copy
+ op8dCOPY(state, frame);
+ break;
+ case 0x8e: //copyn
+ op8eCOPYN(state, frame);
+ break;
+ case 0x8f: //swap
+ op8fSWAP(state, frame);
+ break;
+ case 0x90: //swapn
+ op90SWAPN(state, frame);
+ break;
+ case 0x91: //pop
+ op91POP(state, frame);
+ break;
+ case 0x92: //copy+1
+ op92COPYP(state, frame);
+ break;
+ case 0x93: //copy+n
+ op93COPYPN(state, frame);
+ break;
+ case 0x94: //shuffle
+ op94SHUFF(state, frame);
+ break;
+ case 0x95: //sort
+ op95SORT(state, frame);
+ break;
+ case 0x96: //clear stack
+ op96CLEAR(state, frame);
+ break;
+ case 0x97: //get stack size
+ op97SIZE(state, frame);
+ break;
+ case 0x98: //add
+ op98ADD(state, frame);
+ break;
+ case 0x99: //subtract
+ op99SUB(state, frame);
+ break;
+ case 0x9a: //multiply
+ op9aMUL(state, frame);
+ break;
+ case 0x9b: //divide
+ op9bDIV(state, frame);
+ break;
+ case 0x9c: //mod
+ op9cMOD(state, frame);
+ break;
+ case 0x9d: //divmod
+ op9dDMOD(state, frame);
+ break;
+ case 0x9e: //abs
+ op9eABS(state, frame);
+ break;
+ case 0x9f: //neg
+ op9fNEG(state, frame);
+ break;
+ case 0xa0: //and
+ opa0AND(state, frame);
+ break;
+ case 0xa1: //or
+ opa1OR(state, frame);
+ break;
+ case 0xa2: //xor
+ opa2XOR(state, frame);
+ break;
+ case 0xa3: //not
+ opa3NOT(state, frame);
+ break;
+ case 0xa4: //logical and
+ opa4LAND(state, frame);
+ break;
+ case 0xa5: //logical or
+ opa5LOR(state, frame);
+ break;
+ case 0xa6: //logical xor
+ opa6LXOR(state, frame);
+ break;
+ case 0xa7: //logical not
+ opa7LNOT(state, frame);
+ break;
+ case 0xa8: //gt? unsigned
+ opa8GTU(state, frame);
+ break;
+ case 0xa9: //lt? unsigned
+ opa9LTU(state, frame);
+ break;
+ case 0xaa: //gt? signed
+ opaaGTS(state, frame);
+ break;
+ case 0xab: //lt? signed
+ opabLTS(state, frame);
+ break;
+ case 0xac: //eq?
+ opacEQ(state, frame);
+ break;
+ case 0xad: //eq string?
+ opadEQS(state, frame);
+ break;
+ case 0xae: //contains
+ opaeCONT(state, frame);
+ break;
+ case 0xaf: //contains word
+ opafCONTW(state, frame);
+ break;
+ case 0xb0: //bra
+ opb0BRA(state, frame, &script);
+ break;
+ case 0xb1: //bra.b
+ opb1BRAB(state, frame, &script);
+ break;
+ case 0xb2: //beq
+ opb2BEQ(state, frame, &script);
+ break;
+ case 0xb3: //beq.b
+ opb3BEQB(state, frame, &script);
+ break;
+ case 0xb4: //bne
+ opb4BNE(state, frame, &script);
+ break;
+ case 0xb5: //bne.b
+ opb5BNEB(state, frame, &script);
+ break;
+ case 0xb6: //call later
+ opb6CLAT(state, frame);
+ break;
+ case 0xb7: //cancel call
+ opb7CCA(state, frame);
+ break;
+ case 0xb8: //cancel low priority
+ opb8CLOW(state, frame);
+ break;
+ case 0xb9: //cancel high priority
+ opb9CHI(state, frame);
+ break;
+ case 0xba: //cancel priority range
+ opbaCRAN(state, frame);
+ break;
+ case 0xbb: //fork
+ if (opbbFORK(state, frame))
+ return true;
+ break;
+ case 0xbc: //call
+ if (opbcCALL(state, frame, script))
+ return true;
+ break;
+ case 0xbd: //focus object
+ opbdFOOB(state, frame);
+ break;
+ case 0xbe: //swap objects
+ opbeSWOB(state, frame);
+ break;
+ case 0xbf: //snap object
+ opbfSNOB(state, frame);
+ break;
+ case 0xc0: //toggle exits
+ opc0TEXI(state, frame);
+ break;
+ case 0xc1: //print text
+ opc1PTXT(state, frame);
+ break;
+ case 0xc2: //print newline
+ opc2PNEW(state, frame);
+ break;
+ case 0xc3: //print text+nl
+ opc3PTNE(state, frame);
+ break;
+ case 0xc4: //print nl+text+nl
+ opc4PNTN(state, frame);
+ break;
+ case 0xc5: //print number
+ opc5PNUM(state, frame);
+ break;
+ case 0xc6: //push 2
+ opc6P2(state, frame);
+ break;
+ case 0xc7: //play sound in background
+ opc7PLBG(state, frame);
+ break;
+ case 0xc8: //play sound and wait
+ opc8PLAW(state, frame);
+ break;
+ case 0xc9: //wait for sound to finish?
+ opc9WAIT(state, frame);
+ break;
+ case 0xca: //get current time
+ opcaTIME(state, frame);
+ break;
+ case 0xcb: //get current day
+ opcbDAY(state, frame);
+ break;
+ case 0xcc: //get children
+ opccCHLD(state, frame);
+ break;
+ case 0xcd: //get num children
+ opcdNCHLD(state, frame);
+ break;
+ case 0xce: //get engine version
+ opceVERS(state, frame);
+ break;
+ case 0xcf: //push scenario number
+ opcfPSCE(state, frame);
+ break;
+ case 0xd0: //push 1
+ opd0P1(state, frame);
+ break;
+ case 0xd1: //get object dimensions
+ opd1GOBD(state, frame);
+ break;
+ case 0xd2: //get overlap percent
+ opd2GOVP(state, frame);
+ break;
+ case 0xd3: //capture children
+ opd3CAPC(state, frame);
+ break;
+ case 0xd4: //release children
+ opd4RELC(state, frame);
+ break;
+ case 0xd5: //show speech dialog
+ opd5DLOG(state, frame);
+ return true;
+ case 0xd6: //activate command
+ opd6ACMD(state, frame);
+ break;
+ case 0xd7: //lose game
+ opd7LOSE(state, frame);
+ break;
+ case 0xd8: //win game
+ opd8WIN(state, frame);
+ break;
+ case 0xd9: //sleep
+ opd9SLEEP(state, frame);
+ return true;
+ case 0xda: //click to continue
+ opdaCLICK(state, frame);
+ return true;
+ case 0xdb: //run queue
+ opdbROBQ(state, frame);
+ break;
+ case 0xdc: //run sound queue
+ opdcRSQ(state, frame);
+ break;
+ case 0xdd: //run text queue
+ opddRTQ(state, frame);
+ break;
+ case 0xde: //update screen
+ opdeUPSC(state, frame);
+ break;
+ case 0xdf: //flash main window
+ opdfFMAI(state, frame);
+ return true;
+ case 0xe0: //cache graphic and object
+ ope0CHGR(state, frame);
+ break;
+ case 0xe1: //cache sound
+ ope1CHSO(state, frame);
+ break;
+ case 0xe2: //muldiv
+ ope2MDIV(state, frame);
+ break;
+ case 0xe3: //update object
+ ope3UPOB(state, frame);
+ break;
+ case 0xe4: //currently playing event?
+ ope4PLEV(state, frame);
+ break;
+ case 0xe5: //wait for event to finish
+ ope5WEV(state, frame);
+ break;
+ case 0xe6: //get fibonacci (joke)
+ ope6GFIB(state, frame);
+ break;
+ case 0xe7: //calc fibonacci
+ ope7CFIB(state, frame);
+ break;
+ default:
+ op00NOOP(op);
+ }
+ }
+ }
+ return false;
+}
+
+int16 ScriptEngine::neg16(int16 val) {
+ if (val & 0x8000)
+ val = -((val ^ 0xFFFF) + 1);
+ return val;
+}
+
+int16 ScriptEngine::neg8(int16 val) {
+ if (val & 0x80)
+ val = -((val ^ 0xff) + 1);
+ return val;
+}
+
+int16 ScriptEngine::sumChildrenAttr(int16 obj, int16 attr, bool recursive) {
+ int16 sum = 0;
+ Common::Array<ObjID> children = _world->getChildren(obj, recursive);
+ for (Common::Array<ObjID>::const_iterator it = children.begin(); it != children.end(); it++) {
+ sum += _world->getObjAttr(*it, attr);
+ }
+ return sum;
+}
+
+void ScriptEngine::ensureNonzeroDivisor(int16 divisor, byte opcode) {
+ // TODO Untested, since that occassion rarely comes up.
+ if (divisor == 0) {
+ error("SCRIPT: Attempt to divide by 0 in operation %x", opcode);
+ }
+}
+
+void MacVenture::ScriptEngine::op80GATT(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ int16 attr = state->pop();
+ state->push(_world->getObjAttr(obj, attr));
+}
+
+void ScriptEngine::op81SATT(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ int16 attr = state->pop();
+ int16 val = neg16(state->pop());
+ _world->setObjAttr(obj, attr, val);
+}
+
+void ScriptEngine::op82SUCH(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ int16 attr = state->pop();
+ int16 recursive = neg16(state->pop());
+ state->push(sumChildrenAttr(obj, attr, recursive));
+}
+
+void ScriptEngine::op83PUCT(EngineState *state, EngineFrame *frame) {
+ state->push(frame->action);
+}
+
+void ScriptEngine::op84PUOB(EngineState *state, EngineFrame *frame) {
+ state->push(frame->src);
+}
+
+void ScriptEngine::op85PUTA(EngineState *state, EngineFrame *frame) {
+ state->push(frame->dest);
+}
+
+void ScriptEngine::op86PUDX(EngineState *state, EngineFrame *frame) {
+ state->push(frame->x);
+}
+
+void ScriptEngine::op87PUDY(EngineState *state, EngineFrame *frame) {
+ state->push(frame->y);
+}
+
+void ScriptEngine::op88PUIB(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ state->push(script->fetch());
+}
+
+void ScriptEngine::op89PUI(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val <<= 8;
+ val = val | script->fetch();
+ state->push(val);
+}
+
+void ScriptEngine::op8aGGLO(EngineState *state, EngineFrame *frame) {
+ int16 idx = state->pop();
+ state->push(_world->getGlobal(idx));
+}
+
+void ScriptEngine::op8bSGLO(EngineState *state, EngineFrame *frame) {
+ int16 idx = state->pop();
+ int16 val = neg16(state->pop());
+ _world->setGlobal(idx, val);
+ _engine->gameChanged();
+}
+
+void ScriptEngine::op8cRAND(EngineState *state, EngineFrame *frame) {
+ int16 max = state->pop();
+ state->push(_engine->randBetween(0, max));
+}
+
+void ScriptEngine::op8dCOPY(EngineState *state, EngineFrame *frame) {
+ int16 val = state->pop();
+ state->push(val);
+ state->push(val);
+}
+
+void ScriptEngine::op8eCOPYN(EngineState *state, EngineFrame *frame) {
+ int16 n = state->pop();
+ int16 offs = n - 1;
+ int16 val;
+ while (n) {
+ val = state->peek(offs);
+ state->push(val);
+ n--;
+ }
+}
+
+void ScriptEngine::op8fSWAP(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(b);
+ state->push(a);
+}
+
+void ScriptEngine::op90SWAPN(EngineState *state, EngineFrame *frame) {
+ int16 idx = state->pop();
+ int16 a = state->peek(idx);
+ int16 b = state->peek(0);
+ state->poke(idx, b);
+ state->poke(0, a);
+}
+
+void ScriptEngine::op91POP(EngineState *state, EngineFrame *frame) {
+ state->pop();
+}
+
+void ScriptEngine::op92COPYP(EngineState *state, EngineFrame *frame) {
+ int16 val = state->peek(1);
+ state->push(val);
+}
+
+void ScriptEngine::op93COPYPN(EngineState *state, EngineFrame *frame) {
+ int16 idx = state->pop();
+ int16 val = state->peek(idx);
+ state->push(val);
+}
+
+void ScriptEngine::op94SHUFF(EngineState *state, EngineFrame *frame) {
+ int16 a = state->pop();
+ int16 b = state->pop();
+ int16 c = state->pop();
+ state->push(a);
+ state->push(c);
+ state->push(b);
+}
+
+void ScriptEngine::op95SORT(EngineState *state, EngineFrame *frame) {
+ int16 step = neg16(state->pop());
+ int16 num = neg16(state->pop());
+ step %= num;
+ if (step < 0) {
+ step += num;
+ }
+ int16 end = 0;
+ int16 start = 0;
+ for (int16 i = 1; i < num; i++) {
+ start += step;
+ if (start >= num) {
+ start -= num;
+ }
+ if (start == end) {
+ end++;
+ start = end;
+ } else {
+ int16 a = state->peek(end);
+ int16 b = state->peek(start);
+ state->poke(end, b);
+ state->poke(start, a);
+ }
+ }
+}
+
+void ScriptEngine::op96CLEAR(EngineState *state, EngineFrame *frame) {
+ state->clear();
+}
+
+void ScriptEngine::op97SIZE(EngineState *state, EngineFrame *frame) {
+ state->push(state->size());
+}
+
+void ScriptEngine::op98ADD(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a + b);
+}
+
+void ScriptEngine::op99SUB(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a - b);
+}
+
+void ScriptEngine::op9aMUL(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a * b);
+}
+
+void ScriptEngine::op9bDIV(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ ensureNonzeroDivisor(b, 0x9b);
+ state->push(a / b);
+}
+
+void ScriptEngine::op9cMOD(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a % b);
+}
+
+void ScriptEngine::op9dDMOD(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ ensureNonzeroDivisor(b, 0x9d);
+ state->push(a % b);
+ state->push(a / b);
+}
+
+void ScriptEngine::op9eABS(EngineState *state, EngineFrame *frame) {
+ int16 val = neg16(state->pop());
+ if (val < 0) {
+ val = -val;
+ }
+ state->push(val);
+}
+
+void ScriptEngine::op9fNEG(EngineState *state, EngineFrame *frame) {
+ int16 val = -neg16(state->pop());
+ state->push(val);
+}
+
+void ScriptEngine::opa0AND(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a & b);
+}
+
+void ScriptEngine::opa1OR(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a | b);
+}
+
+void ScriptEngine::opa2XOR(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(a ^ b);
+}
+
+void ScriptEngine::opa3NOT(EngineState *state, EngineFrame *frame) {
+ int16 a = state->pop();
+ state->push(a ^ 0xFFFF);
+}
+
+void ScriptEngine::opa4LAND(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push((a && b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opa5LOR(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push((a || b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opa6LXOR(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push((!a != !b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opa7LNOT(EngineState *state, EngineFrame *frame) {
+ int16 a = state->pop();
+ state->push((a == 0) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opa8GTU(EngineState *state, EngineFrame *frame) {
+ uint16 b = state->pop();
+ uint16 a = state->pop();
+ state->push((a > b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opa9LTU(EngineState *state, EngineFrame *frame) {
+ uint16 b = state->pop();
+ uint16 a = state->pop();
+ state->push((a < b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opaaGTS(EngineState *state, EngineFrame *frame) {
+ // HACK !!! May not need the neg16, since it's already a signed int!!
+ int16 b = neg16(state->pop());
+ int16 a = neg16(state->pop());
+ state->push((a > b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opabLTS(EngineState *state, EngineFrame *frame) {
+ // HACK !!! May not need the neg16, since it's already a signed int!!
+ int16 b = neg16(state->pop());
+ int16 a = neg16(state->pop());
+ state->push((a < b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opacEQ(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push((a == b) ? 0xFFFF : 0);
+}
+
+void ScriptEngine::opadEQS(EngineState *state, EngineFrame *frame) {
+ Common::String b = _world->getText(state->pop(), 0, 0); // HACK, these destinations might be wrong
+ Common::String a = _world->getText(state->pop(), 0, 0);
+ state->push((a == b) ? 1 : 0);
+}
+
+void ScriptEngine::opaeCONT(EngineState *state, EngineFrame *frame) {
+ Common::String needle = _world->getText(state->pop(), 0, 0);
+ Common::String haystack = _world->getText(state->pop(), 0, 0);
+ haystack.toLowercase();
+ state->push(haystack.contains(needle) ? 1 : 0);
+}
+
+void ScriptEngine::opafCONTW(EngineState *state, EngineFrame *frame) {
+ Common::String needle = _world->getText(state->pop(), 0, 0);
+ Common::String haystack = _world->getText(state->pop(), 0, 0);
+ haystack.toLowercase();
+ state->push(haystack.contains(needle) ? 1 : 0);
+}
+
+void ScriptEngine::opb0BRA(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val <<= 8;
+ val = val | script->fetch();
+ val = neg16(val);
+ script->branch(val);
+}
+
+void ScriptEngine::opb1BRAB(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val = neg8(val);
+ script->branch(val);
+}
+
+void ScriptEngine::opb2BEQ(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val <<= 8;
+ val = val | script->fetch();
+ val = neg16(val);
+ int16 b = state->pop();
+ if (b != 0) {
+ script->branch(val);
+ }
+}
+
+void ScriptEngine::opb3BEQB(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val = neg8(val);
+ int16 b = state->pop();
+ if (b != 0) {
+ script->branch(val);
+ }
+}
+
+void ScriptEngine::opb4BNE(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val <<= 8;
+ val = val | script->fetch();
+ val = neg16(val);
+ int16 b = state->pop();
+ if (b == 0) {
+ script->branch(val);
+ }
+}
+
+void ScriptEngine::opb5BNEB(EngineState *state, EngineFrame *frame, ScriptAsset *script) {
+ int16 val = script->fetch();
+ val = neg8(val);
+ int16 b = state->pop();
+ if (b == 0) {
+ script->branch(val);
+ }
+}
+
+void ScriptEngine::opb6CLAT(EngineState *state, EngineFrame *frame) {
+ int16 rank = state->pop();
+ int16 func = state->pop();
+ frame->saves.push_back(FunCall(func, rank));
+}
+
+void ScriptEngine::opb7CCA(EngineState *state, EngineFrame *frame) {
+ int16 func = state->pop();
+ for (uint i = 0; i < frame->saves.size(); i++) {
+ if (frame->saves[i].func == func)
+ frame->saves[i].rank = 0;
+ }
+}
+
+void ScriptEngine::opb8CLOW(EngineState *state, EngineFrame *frame) {
+ int16 hi = state->pop();
+ for (uint i = 0; i < frame->saves.size(); i++)
+ if (frame->saves[i].rank <= hi)
+ frame->saves[i].rank = 0;
+}
+
+void ScriptEngine::opb9CHI(EngineState *state, EngineFrame *frame) {
+ int16 lo = state->pop();
+ for (uint i = 0; i < frame->saves.size(); i++) {
+ if (frame->saves[i].rank >= lo) {
+ frame->saves[i].rank = 0;
+ }
+ }
+}
+
+void ScriptEngine::opbaCRAN(EngineState *state, EngineFrame *frame) {
+ int16 hi = state->pop();
+ int16 lo = state->pop();
+ for (uint i = 0; i < frame->saves.size(); i++) {
+ if (frame->saves[i].rank >= lo &&
+ frame->saves[i].rank <= hi) {
+ frame->saves[i].rank = 0;
+ }
+ }
+}
+
+bool ScriptEngine::opbbFORK(EngineState *state, EngineFrame *frame) {
+ EngineFrame newframe;
+ newframe.action = (ControlAction)state->pop();
+ newframe.src = state->pop();
+ newframe.dest = state->pop();
+ newframe.x = state->pop();
+ newframe.y = state->pop();
+ newframe.haltedInFamily = false;
+ newframe.haltedInFirst = false;
+ newframe.haltedInSaves = false;
+ _frames.push_front(newframe);
+ if (execFrame(true)) {
+ return true;
+ }
+ return false;
+}
+
+bool ScriptEngine::opbcCALL(EngineState *state, EngineFrame *frame, ScriptAsset &script) {
+ int16 id = state->pop();
+ ScriptAsset newfun = ScriptAsset(id, _scripts);
+ ScriptAsset current = script;
+ debugC(2, kMVDebugScript, "Call function: %d", id);
+ if (loadScript(frame, id))
+ return true;
+ frame->scripts.pop_front();
+ script = frame->scripts.front();
+ debugC(2, kMVDebugScript, "Return from fuction %d", id);
+ return false;
+}
+
+void ScriptEngine::opbdFOOB(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ _engine->enqueueObject(kFocusWindow, obj);
+}
+
+void ScriptEngine::opbeSWOB(EngineState *state, EngineFrame *frame) {
+ ObjID from = state->pop();
+ ObjID to = state->pop();
+ _engine->enqueueObject(kUpdateWindow, from, to);
+ _world->setObjAttr(to, kAttrContainerOpen, _world->getObjAttr(from, 6));
+ _world->setObjAttr(from, kAttrContainerOpen, 0);
+ Common::Array<ObjID> children = _world->getChildren(from, true);
+ for (uint i = 0; i < children.size(); i++) {
+ _world->setObjAttr(children[i], 0, to);
+ }
+}
+
+void ScriptEngine::opbfSNOB(EngineState *state, EngineFrame *frame) {
+ _engine->enqueueObject(kAnimateBack, frame->src);
+}
+
+void ScriptEngine::opc0TEXI(EngineState *state, EngineFrame *frame) {
+ _engine->enqueueObject(kHightlightExits, 0);
+}
+
+void ScriptEngine::opc1PTXT(EngineState *state, EngineFrame *frame) {
+ int16 tid = state->pop();
+ _engine->enqueueText(kTextPlain, frame->dest, frame->src, tid);
+}
+
+void ScriptEngine::opc2PNEW(EngineState *state, EngineFrame *frame) {
+ _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0);
+}
+
+void ScriptEngine::opc3PTNE(EngineState *state, EngineFrame *frame) {
+ int16 tid = state->pop();
+ _engine->enqueueText(kTextPlain, frame->dest, frame->src, tid);
+ _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0);
+}
+
+void ScriptEngine::opc4PNTN(EngineState *state, EngineFrame *frame) {
+ int16 tid = state->pop();
+ _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0);
+ _engine->enqueueText(kTextPlain, frame->dest, frame->src, tid);
+ _engine->enqueueText(kTextNewLine, frame->dest, frame->src, 0);
+}
+
+void ScriptEngine::opc5PNUM(EngineState *state, EngineFrame *frame) {
+ int16 tid = state->pop();
+ _engine->enqueueText(kTextNumber, frame->dest, frame->src, tid);
+}
+
+void ScriptEngine::opc6P2(EngineState *state, EngineFrame *frame) {
+ state->push(2);
+}
+
+void ScriptEngine::opc7PLBG(EngineState *state, EngineFrame *frame) {
+ int16 target = state->pop();
+ _engine->enqueueSound(kSoundPlay, target);
+}
+
+void ScriptEngine::opc8PLAW(EngineState *state, EngineFrame *frame) {
+ int16 target = state->pop();
+ _engine->enqueueSound(kSoundPlayAndWait, target);
+}
+
+void ScriptEngine::opc9WAIT(EngineState *state, EngineFrame *frame) {
+ _engine->enqueueSound(kSoundWait, 0);
+}
+
+void ScriptEngine::opcaTIME(EngineState *state, EngineFrame *frame) {
+ for (uint i = 0; i < 3; i++) {// We skip year, month and date
+ state->push(0x00);
+ }
+
+ uint32 totalPlayTime = _engine->getTotalPlayTime() / 1000; // In seconds
+ int16 hours = totalPlayTime / 3600;
+ totalPlayTime %= 3600;
+ state->push(hours);
+ int16 minutes = totalPlayTime / 60;
+ totalPlayTime %= 60;
+ state->push(minutes);
+ state->push(totalPlayTime);
+ debugC(2, kMVDebugScript, "Saved time: h[%d] m[%d] s[%d]", hours, minutes, totalPlayTime);
+}
+
+void ScriptEngine::opcbDAY(EngineState *state, EngineFrame *frame) {
+ // Probaby irrelevant, so we push Day [9]
+ state->push(9);
+}
+
+void ScriptEngine::opccCHLD(EngineState *state, EngineFrame *frame) {
+ bool recursive = state->pop() != 0;
+ int16 obj = state->pop();
+ Common::Array<ObjID> children = _world->getChildren(obj, recursive);
+ for (Common::Array<ObjID>::const_iterator it = children.begin(); it != children.end(); it++) {
+ state->push(*it);
+ }
+ state->push(children.size());
+}
+
+void ScriptEngine::opcdNCHLD(EngineState *state, EngineFrame *frame) {
+ bool recursive = state->pop() != 0;
+ int16 obj = state->pop();
+ Common::Array<ObjID> children = _world->getChildren(obj, recursive);
+ state->push(children.size());
+}
+
+void ScriptEngine::opceVERS(EngineState *state, EngineFrame *frame) {
+ state->push(86); // Engine version is irrelevant. Like this in the original.
+}
+
+void ScriptEngine::opcfPSCE(EngineState *state, EngineFrame *frame) {
+ state->push(0); // Any value greater than 0 indicates "Release".
+}
+
+void ScriptEngine::opd0P1(EngineState *state, EngineFrame *frame) {
+ state->push(1);
+}
+
+void ScriptEngine::opd1GOBD(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ Common::Rect bounds = _engine->getObjBounds(obj);
+ state->push(bounds.width());
+ state->push(bounds.height());
+}
+
+void ScriptEngine::opd2GOVP(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ state->push(_engine->getOverlapPercent(b, a));
+}
+
+void ScriptEngine::opd3CAPC(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ _world->captureChildren(obj);
+}
+
+void ScriptEngine::opd4RELC(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ _world->releaseChildren(obj);
+}
+
+void ScriptEngine::opd5DLOG(EngineState *state, EngineFrame *frame) {
+ int16 txt = state->pop();
+ if (_engine->showTextEntry(txt, frame->src, frame->dest)) {
+ state->push(0xFF);
+ } else {
+ state->push(0x00);
+ }
+}
+
+void ScriptEngine::opd6ACMD(EngineState *state, EngineFrame *frame) {
+ _engine->selectControl((ControlAction)state->pop());
+}
+
+void ScriptEngine::opd7LOSE(EngineState *state, EngineFrame *frame) {
+ _engine->loseGame();
+}
+
+void ScriptEngine::opd8WIN(EngineState *state, EngineFrame *frame) {
+ _engine->winGame();
+}
+
+void ScriptEngine::opd9SLEEP(EngineState *state, EngineFrame *frame) {
+ int16 ticks = state->pop();
+ g_system->delayMillis((ticks / 60) * 1000);
+ _engine->preparedToRun();
+}
+
+void ScriptEngine::opdaCLICK(EngineState *state, EngineFrame *frame) {
+ _engine->updateState(false);
+ _engine->clickToContinue();
+}
+
+void ScriptEngine::opdbROBQ(EngineState *state, EngineFrame *frame) {
+ _engine->runObjQueue();
+}
+
+void ScriptEngine::opdcRSQ(EngineState *state, EngineFrame *frame) {
+ _engine->playSounds(true);
+}
+
+void ScriptEngine::opddRTQ(EngineState *state, EngineFrame *frame) {
+ _engine->printTexts();
+}
+
+void ScriptEngine::opdeUPSC(EngineState *state, EngineFrame *frame) {
+ _engine->updateState(true);
+}
+
+void ScriptEngine::opdfFMAI(EngineState *state, EngineFrame *frame) {
+ int16 ticks = state->pop();
+ g_system->delayMillis((ticks / 60) * 1000);
+ _engine->revert();
+}
+
+void ScriptEngine::ope0CHGR(EngineState *state, EngineFrame *frame) {
+ state->pop();
+}
+
+void ScriptEngine::ope1CHSO(EngineState *state, EngineFrame *frame) {
+ state->pop();
+}
+
+void ScriptEngine::ope2MDIV(EngineState *state, EngineFrame *frame) {
+ int16 b = state->pop();
+ int16 a = state->pop();
+ a *= b;
+ int16 c = state->pop();
+ ensureNonzeroDivisor(c, 0xe2);
+ a /= c;
+ state->push(a);
+}
+
+void ScriptEngine::ope3UPOB(EngineState *state, EngineFrame *frame) {
+ int16 obj = state->pop();
+ _world->updateObj(obj);
+}
+
+void ScriptEngine::ope4PLEV(EngineState *state, EngineFrame *frame) {
+ state->push(0);
+}
+
+void ScriptEngine::ope5WEV(EngineState *state, EngineFrame *frame) {
+ op00NOOP(0xe5);
+}
+
+void ScriptEngine::ope6GFIB(EngineState *state, EngineFrame *frame) {
+ state->push(0);
+ op00NOOP(0xe6);
+}
+
+void ScriptEngine::ope7CFIB(EngineState *state, EngineFrame *frame) {
+ state->pop();
+ op00NOOP(0xe7);
+}
+
+void ScriptEngine::op00NOOP(byte op) {
+ warning("SCRIPT: Opcode not implemented => %x", op);
+}
+
+
+
+ScriptAsset::ScriptAsset(ObjID id, Container *container) {
+ _id = id;
+ _container = container;
+ _ip = 0x0;
+ loadInstructions();
+}
+
+void ScriptAsset::reset() {
+ _ip = 0x0;
+}
+
+uint8 ScriptAsset::fetch() {
+ uint8 ins = _instructions[_ip];
+ _ip++;
+ return ins;
+}
+
+bool ScriptAsset::hasNext() {
+ return _ip < _instructions.size();
+}
+
+void ScriptAsset::branch(int16 amount) {
+ _ip += amount;
+}
+
+ObjID ScriptAsset::getId() {
+ return _id;
+}
+
+void ScriptAsset::loadInstructions() {
+ uint32 amount = _container->getItemByteSize(_id);
+ Common::SeekableReadStream *res = _container->getItem(_id);
+ for (uint i = 0; i < amount; i++) {
+ _instructions.push_back(res->readByte());
+ }
+ delete res;
+ debugC(2, kMVDebugScript, "Load %d instructions for script %d", amount, _id);
+}
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/script.h b/engines/macventure/script.h
new file mode 100644
index 0000000000..0e81e41168
--- /dev/null
+++ b/engines/macventure/script.h
@@ -0,0 +1,285 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_SCRIPT_H
+#define MACVENTURE_SCRIPT_H
+
+#include "macventure/container.h"
+#include "macventure/world.h"
+#include "macventure/macventure.h"
+#include "macventure/controls.h"
+
+namespace MacVenture {
+
+class Container;
+class World;
+
+typedef uint32 ObjID;
+
+class ScriptAsset {
+public:
+ ScriptAsset(ObjID id, Container *container);
+ ~ScriptAsset() {}
+
+ void reset();
+ uint8 fetch();
+ bool hasNext();
+ void branch(int16 amount);
+
+ ObjID getId();
+
+private:
+
+ void loadInstructions();
+
+private:
+ ObjID _id;
+ Container *_container;
+
+ Common::Array<uint8> _instructions;
+ uint32 _ip; // Instruction pointer
+};
+
+class EngineState {
+public:
+ EngineState() {
+ clear();
+ }
+
+ void push(int16 data) {
+ sp--;
+ stack[sp] = unneg16(data);
+ }
+
+ int16 pop() {
+ int16 v = stack[sp];
+ sp++;
+ return v;
+ }
+
+ int16 peek(int16 off) {
+ return stack[sp + off];
+ }
+
+ void poke(int16 off, int16 val) {
+ stack[sp + off] = unneg16(val);
+ }
+
+ void clear() {
+ sp = 0x80;
+ for (int i = 0; i < sp; i++) {
+ stack[i] = 0;
+ }
+ }
+
+ int16 size() {
+ return 0x80 - sp;
+ }
+
+private:
+ int16 unneg16(int16 data) {
+ if (data < 0)
+ data = ((-data) ^ 0xFFFF) + 1;
+
+ return data;
+ }
+
+private:
+
+ int16 stack[0x80];
+ int16 sp;
+};
+
+struct FunCall {
+ int16 func;
+ int16 rank;
+
+ FunCall(int16 f, int16 r) {
+ func = f;
+ rank = r;
+ }
+};
+
+struct EngineFrame {
+ ControlAction action;
+ ObjID src;
+ ObjID dest;
+ int x;
+ int y;
+ EngineState state;
+ Common::List<ScriptAsset> scripts;
+ Common::Array<FunCall> saves;
+ uint32 familyIdx;
+
+ bool haltedInFirst;
+ bool haltedInFamily;
+ bool haltedInSaves;
+};
+
+class ScriptEngine {
+public:
+ ScriptEngine(MacVentureEngine *engine, World *world);
+ ~ScriptEngine();
+
+public:
+ bool runControl(ControlAction action, ObjID source, ObjID destination, Common::Point delta);
+ bool resume(bool execAll);
+ void reset();
+
+private:
+ bool execFrame(bool execAll);
+ bool loadScript(EngineFrame *frame, uint32 scriptID);
+ bool resumeFunc(EngineFrame *frame);
+ bool runFunc(EngineFrame *frame);
+
+private:
+
+ // Aux
+ int16 neg16(int16 val);
+ int16 neg8(int16 val);
+ int16 sumChildrenAttr(int16 obj, int16 attr, bool recursive);
+ void ensureNonzeroDivisor(int16 divisor, byte opcode);
+
+ // Opcodes
+ void op80GATT(EngineState *state, EngineFrame *frame); //get attribute
+ void op81SATT(EngineState *state, EngineFrame *frame); //set attribute
+ void op82SUCH(EngineState *state, EngineFrame *frame); //sum children attribute
+ void op83PUCT(EngineState *state, EngineFrame *frame); //push selected control
+ void op84PUOB(EngineState *state, EngineFrame *frame); //push selected object
+ void op85PUTA(EngineState *state, EngineFrame *frame); //push target
+ void op86PUDX(EngineState *state, EngineFrame *frame); //push deltax
+ void op87PUDY(EngineState *state, EngineFrame *frame); //push deltay
+ void op88PUIB(EngineState *state, EngineFrame *frame, ScriptAsset *script);//push immediate.b
+ void op89PUI(EngineState *state, EngineFrame *frame, ScriptAsset *script);//push immediate
+ void op8aGGLO(EngineState *state, EngineFrame *frame); //get global
+ void op8bSGLO(EngineState *state, EngineFrame *frame); //set global
+ void op8cRAND(EngineState *state, EngineFrame *frame); //random
+ void op8dCOPY(EngineState *state, EngineFrame *frame); //copy
+ void op8eCOPYN(EngineState *state, EngineFrame *frame); //copyn
+ void op8fSWAP(EngineState *state, EngineFrame *frame); //swap
+
+ void op90SWAPN(EngineState *state, EngineFrame *frame); //swapn
+ void op91POP(EngineState *state, EngineFrame *frame); //pop
+ void op92COPYP(EngineState *state, EngineFrame *frame); //copy+1
+ void op93COPYPN(EngineState *state, EngineFrame *frame);//copy+n
+ void op94SHUFF(EngineState *state, EngineFrame *frame); //shuffle
+ void op95SORT(EngineState *state, EngineFrame *frame); //sort
+ void op96CLEAR(EngineState *state, EngineFrame *frame); //clear stack
+ void op97SIZE(EngineState *state, EngineFrame *frame); //get stack size
+ void op98ADD(EngineState *state, EngineFrame *frame); //add
+ void op99SUB(EngineState *state, EngineFrame *frame); //subtract
+ void op9aMUL(EngineState *state, EngineFrame *frame); //multiply
+ void op9bDIV(EngineState *state, EngineFrame *frame); //divide
+ void op9cMOD(EngineState *state, EngineFrame *frame); //mod
+ void op9dDMOD(EngineState *state, EngineFrame *frame); //divmod
+ void op9eABS(EngineState *state, EngineFrame *frame); //abs
+ void op9fNEG(EngineState *state, EngineFrame *frame); //neg
+
+ void opa0AND(EngineState *state, EngineFrame *frame); //and
+ void opa1OR(EngineState *state, EngineFrame *frame); //or
+ void opa2XOR(EngineState *state, EngineFrame *frame); //xor
+ void opa3NOT(EngineState *state, EngineFrame *frame); //not
+ void opa4LAND(EngineState *state, EngineFrame *frame); //logical and
+ void opa5LOR(EngineState *state, EngineFrame *frame); //logical or
+ void opa6LXOR(EngineState *state, EngineFrame *frame); //logical xor
+ void opa7LNOT(EngineState *state, EngineFrame *frame); //logical not
+ void opa8GTU(EngineState *state, EngineFrame *frame); //gt? unsigned
+ void opa9LTU(EngineState *state, EngineFrame *frame); //lt? unsigned
+ void opaaGTS(EngineState *state, EngineFrame *frame); //gt? signed
+ void opabLTS(EngineState *state, EngineFrame *frame); //lt? signed
+ void opacEQ(EngineState *state, EngineFrame *frame); //eq?
+ void opadEQS(EngineState *state, EngineFrame *frame); //eq string?
+ void opaeCONT(EngineState *state, EngineFrame *frame); //contains
+ void opafCONTW(EngineState *state, EngineFrame *frame); //contains word
+
+ void opb0BRA(EngineState *state, EngineFrame *frame, ScriptAsset *script); //bra
+ void opb1BRAB(EngineState *state, EngineFrame *frame, ScriptAsset *script); //bra.b
+ void opb2BEQ(EngineState *state, EngineFrame *frame, ScriptAsset *script); //beq
+ void opb3BEQB(EngineState *state, EngineFrame *frame, ScriptAsset *script); //beq.b
+ void opb4BNE(EngineState *state, EngineFrame *frame, ScriptAsset *script); //bne
+ void opb5BNEB(EngineState *state, EngineFrame *frame, ScriptAsset *script); //bne.b
+ void opb6CLAT(EngineState *state, EngineFrame *frame); //call later
+ void opb7CCA(EngineState *state, EngineFrame *frame); //cancel call
+ void opb8CLOW(EngineState *state, EngineFrame *frame); //cancel low priority
+ void opb9CHI(EngineState *state, EngineFrame *frame); //cancel high priority
+ void opbaCRAN(EngineState *state, EngineFrame *frame); //cancel priority range
+ bool opbbFORK(EngineState *state, EngineFrame *frame); //fork
+ bool opbcCALL(EngineState *state, EngineFrame *frame, ScriptAsset &script); //call
+ void opbdFOOB(EngineState *state, EngineFrame *frame); //focus object
+ void opbeSWOB(EngineState *state, EngineFrame *frame); //swap objects
+ void opbfSNOB(EngineState *state, EngineFrame *frame); //snap object
+
+ void opc0TEXI(EngineState *state, EngineFrame *frame); //toggle exits
+ void opc1PTXT(EngineState *state, EngineFrame *frame); //print text
+ void opc2PNEW(EngineState *state, EngineFrame *frame); //print newline
+ void opc3PTNE(EngineState *state, EngineFrame *frame); //print text+nl
+ void opc4PNTN(EngineState *state, EngineFrame *frame); //print nl+text+nl
+ void opc5PNUM(EngineState *state, EngineFrame *frame); //print number
+ void opc6P2(EngineState *state, EngineFrame *frame); //push 2
+ void opc7PLBG(EngineState *state, EngineFrame *frame); //play sound in background
+ void opc8PLAW(EngineState *state, EngineFrame *frame); //play sound and wait
+ void opc9WAIT(EngineState *state, EngineFrame *frame); //wait for sound to finish?
+ void opcaTIME(EngineState *state, EngineFrame *frame); //get current time
+ void opcbDAY(EngineState *state, EngineFrame *frame); //get current day
+ void opccCHLD(EngineState *state, EngineFrame *frame); //get children
+ void opcdNCHLD(EngineState *state, EngineFrame *frame); //get num children
+ void opceVERS(EngineState *state, EngineFrame *frame); //get engine version
+ void opcfPSCE(EngineState *state, EngineFrame *frame); //push scenario number
+
+ void opd0P1(EngineState *state, EngineFrame *frame); //push 1
+ void opd1GOBD(EngineState *state, EngineFrame *frame); //get object dimensions
+ void opd2GOVP(EngineState *state, EngineFrame *frame); //get overlap percent
+ void opd3CAPC(EngineState *state, EngineFrame *frame); //capture children
+ void opd4RELC(EngineState *state, EngineFrame *frame); //release children
+ void opd5DLOG(EngineState *state, EngineFrame *frame); //show speech dialog
+ void opd6ACMD(EngineState *state, EngineFrame *frame); //activate command
+ void opd7LOSE(EngineState *state, EngineFrame *frame); //lose game
+ void opd8WIN(EngineState *state, EngineFrame *frame); //win game
+ void opd9SLEEP(EngineState *state, EngineFrame *frame); //sleep
+ void opdaCLICK(EngineState *state, EngineFrame *frame); //click to continue
+ void opdbROBQ(EngineState *state, EngineFrame *frame); //run queue
+ void opdcRSQ(EngineState *state, EngineFrame *frame); //run sound queue
+ void opddRTQ(EngineState *state, EngineFrame *frame); //run text queue
+ void opdeUPSC(EngineState *state, EngineFrame *frame); //update screen
+ void opdfFMAI(EngineState *state, EngineFrame *frame); //flash main window
+
+ void ope0CHGR(EngineState *state, EngineFrame *frame); //cache graphic and object
+ void ope1CHSO(EngineState *state, EngineFrame *frame); //cache sound
+ void ope2MDIV(EngineState *state, EngineFrame *frame); //muldiv
+ void ope3UPOB(EngineState *state, EngineFrame *frame); //update object
+ void ope4PLEV(EngineState *state, EngineFrame *frame); //currently playing event?
+ void ope5WEV(EngineState *state, EngineFrame *frame); //wait for event to finish
+ void ope6GFIB(EngineState *state, EngineFrame *frame); //get fibonacci (joke)
+ void ope7CFIB(EngineState *state, EngineFrame *frame); //calc fibonacci
+
+ void op00NOOP(byte op);
+
+private:
+ MacVentureEngine *_engine;
+ World *_world;
+ Common::List<EngineFrame> _frames;
+ Container *_scripts;
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/sound.cpp b/engines/macventure/sound.cpp
new file mode 100644
index 0000000000..422e365790
--- /dev/null
+++ b/engines/macventure/sound.cpp
@@ -0,0 +1,282 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/sound.h"
+
+#include "audio/mixer.h"
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+
+namespace MacVenture {
+
+// SoundManager
+SoundManager::SoundManager(MacVentureEngine *engine, Audio::Mixer *mixer) {
+ _container = NULL;
+ Common::String filename = engine->getFilePath(kSoundPathID);
+ _container = new Container(filename);
+ _mixer = mixer;
+ debugC(1, kMVDebugSound, "Created sound manager with file %s", filename.c_str());
+}
+
+SoundManager::~SoundManager() {
+ if (_container)
+ delete _container;
+
+ Common::HashMap<ObjID, SoundAsset*>::iterator it;
+ Common::HashMap<ObjID, SoundAsset*>::iterator end = _assets.end();
+ for (it = _assets.begin(); it != end; it++) {
+ delete it->_value;
+ }
+}
+
+uint32 SoundManager::playSound(ObjID sound) {
+ ensureLoaded(sound);
+ _assets[sound]->play(_mixer, &_handle);
+ return _assets[sound]->getPlayLength();
+}
+
+void SoundManager::ensureLoaded(ObjID sound) {
+ if (!_assets.contains(sound))
+ _assets[sound] = new SoundAsset(_container, sound);
+}
+
+SoundAsset::SoundAsset(Container *container, ObjID id) :
+ _container(container), _id(id), _length(0), _frequency(1) {
+ if (_container->getItemByteSize(_id) == 0)
+ warning("Trying to load an empty sound asset (%d).", _id);
+
+ Common::SeekableReadStream *stream = _container->getItem(_id);
+
+ stream->seek(5, SEEK_SET);
+ SoundType type = (SoundType)stream->readByte();
+ debugC(2, kMVDebugSound, "Decoding sound of type %x", type);
+ switch(type) {
+ case kSound10:
+ decode10(stream);
+ break;
+ case kSound12:
+ decode12(stream);
+ break;
+ case kSound18:
+ decode18(stream);
+ break;
+ case kSound1a:
+ decode1a(stream);
+ break;
+ case kSound44:
+ decode44(stream);
+ break;
+ case kSound78:
+ decode78(stream);
+ break;
+ case kSound7e:
+ decode7e(stream);
+ break;
+ default:
+ warning("Unrecognized sound type: %x", type);
+ break;
+ }
+
+ delete stream;
+}
+
+SoundAsset::~SoundAsset() {
+ debugC(3, kMVDebugSound, "~SoundAsset(%d)", _id);
+}
+
+void SoundAsset::play(Audio::Mixer *mixer, Audio::SoundHandle *soundHandle) {
+ if (_data.size() == 0) {
+ return;
+ }
+ Audio::AudioStream *sound = Audio::makeRawStream(&_data.front(), _length, _frequency, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
+ mixer->playStream(Audio::Mixer::kPlainSoundType, soundHandle, sound);
+}
+
+uint32 SoundAsset::getPlayLength() {
+ // Transform to milliseconds
+ return _length * 1000 / _frequency;
+}
+
+void SoundAsset::decode10(Common::SeekableReadStream *stream) {
+ warning("Decode sound 0x10 untested");
+ Common::Array<byte> wavtable;
+ stream->seek(0x198, SEEK_SET);
+ for (uint i = 0; i < 16; i++) {
+ wavtable.push_back(stream->readByte());
+ }
+ _length = stream->readUint32BE() * 2;
+ //Unused
+ stream->readUint16BE();
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ byte ch = 0;
+ for (uint i = 0; i < _length; i++) {
+ if (i & 1) {
+ ch >>= 4;
+ } else {
+ ch = stream->readByte();
+ }
+ _data.push_back(wavtable[ch & 0xf]);
+ }
+}
+
+void SoundAsset::decode12(Common::SeekableReadStream *stream) {
+ warning("Decode sound 0x12 untested");
+ stream->seek(0xc, SEEK_SET);
+ uint32 repeat = stream->readUint16BE();
+ stream->seek(0x34, SEEK_SET);
+ uint32 base = stream->readUint16BE() + 0x34;
+ stream->seek(base, SEEK_SET);
+ _length = stream->readUint32BE() - 6;
+ stream->readUint16BE();
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ stream->seek(0xe2, SEEK_SET);
+ // TODO: Possible source of bugs, the original just assigns the seek to the scales
+ uint32 scales = stream->pos() + 0xe2;
+ for (uint i = 0; i < repeat; i++) {
+ stream->seek(scales + i * 2, SEEK_SET);
+ uint32 scale = stream->readUint16BE();
+ stream->seek(base + 0xa, SEEK_SET);
+ for (uint j = 0; j < _length; j++) {
+ byte ch = stream->readByte();
+ if (ch & 0x80) {
+ ch -= 0x80;
+ uint32 env = ch * scale;
+ ch = (env >> 8) & 0xff;
+ if (ch & 0x80) {
+ ch = 0x7f;
+ }
+ ch += 0x80;
+ } else {
+ ch = (ch ^ 0xff) + 1;
+ ch -= 0x80;
+ uint32 env = ch * scale;
+ ch = (env >> 8) & 0xff;
+ if (ch & 0x80) {
+ ch = 0x7f;
+ }
+ ch += 0x80;
+ ch = (ch ^ 0xff) + 1;
+ }
+ _data.push_back(ch);
+ }
+ }
+}
+
+void SoundAsset::decode18(Common::SeekableReadStream *stream) {
+ warning("Decode sound 0x18 untested");
+ Common::Array<byte> wavtable;
+ stream->seek(0x252, SEEK_SET);
+ for (uint i = 0; i < 16; i++) {
+ wavtable.push_back(stream->readByte());
+ }
+ _length = stream->readUint32BE() * 2;
+ //Unused
+ stream->readUint16BE();
+ // TODO: It had `| 0` at the end of this line, possible source of bugs.
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ byte ch = 0;
+ for (uint i = 0; i < _length; i++) {
+ if (i & 1) {
+ ch >>= 4;
+ } else {
+ ch = stream->readByte();
+ }
+ _data.push_back(wavtable[ch & 0xf]);
+ }
+}
+
+void SoundAsset::decode1a(Common::SeekableReadStream *stream) {
+ warning("Decode sound 0x1a untested");
+ Common::Array<byte> wavtable;
+ stream->seek(0x220, SEEK_SET);
+ for (uint i = 0; i < 16; i++) {
+ wavtable.push_back(stream->readByte());
+ }
+ _length = stream->readUint32BE();
+ //Unused
+ stream->readUint16BE();
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ byte ch = 0;
+ for (uint i = 0; i < _length; i++) {
+ if (i & 1) {
+ ch >>= 4;
+ } else {
+ ch = stream->readByte();
+ }
+ _data.push_back(wavtable[ch & 0xf]);
+ }
+}
+
+void SoundAsset::decode44(Common::SeekableReadStream *stream) {
+ stream->seek(0x5e, SEEK_SET);
+ _length = stream->readUint32BE();
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ for (uint i = 0; i < _length; i++) {
+ _data.push_back(stream->readByte());
+ }
+}
+
+void SoundAsset::decode78(Common::SeekableReadStream *stream) {
+ Common::Array<byte> wavtable;
+ stream->seek(0xba, SEEK_SET);
+ for (uint i = 0; i < 16; i++) {
+ wavtable.push_back(stream->readByte());
+ }
+ //Unused
+ stream->readUint32BE();
+ _length = stream->readUint32BE();
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ byte ch = 0;
+ for (uint i = 0; i < _length; i++) {
+ if (i & 1) {
+ ch <<= 4;
+ } else {
+ ch = stream->readByte();
+ }
+ _data.push_back(wavtable[(ch >> 4) & 0xf]);
+ }
+}
+
+void SoundAsset::decode7e(Common::SeekableReadStream *stream) {
+ Common::Array<byte> wavtable;
+ stream->seek(0xc2, SEEK_SET);
+ for (uint i = 0; i < 16; i++) {
+ wavtable.push_back(stream->readByte());
+ }
+ //Unused
+ stream->readUint32BE();
+ _length = stream->readUint32BE();
+ _frequency = (stream->readUint32BE() * 22100 / 0x10000);
+ uint32 last = 0x80;
+ byte ch = 0;
+ for (uint i = 0; i < _length; i++) {
+ if (i & 1) {
+ ch <<= 4;
+ } else {
+ ch = stream->readByte();
+ }
+ last += wavtable[(ch >> 4) & 0xf];
+ _data.push_back(last & 0xff);
+ }
+}
+
+} //End of namespace MacVenture
diff --git a/engines/macventure/sound.h b/engines/macventure/sound.h
new file mode 100644
index 0000000000..d1b1bb8a37
--- /dev/null
+++ b/engines/macventure/sound.h
@@ -0,0 +1,95 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_SOUND_H
+#define MACVENTURE_SOUND_H
+
+#include "macventure/macventure.h"
+#include "macventure/container.h"
+
+#include "common/file.h"
+#include "common/hashmap.h"
+
+#include "audio/mixer.h"
+
+namespace MacVenture {
+
+enum SoundType {
+ kSound10 = 0x10,
+ kSound12 = 0x12,
+ kSound18 = 0x18,
+ kSound1a = 0x1a,
+ kSound44 = 0x44,
+ kSound78 = 0x78,
+ kSound7e = 0x7e
+};
+
+class SoundAsset {
+
+public:
+ SoundAsset(Container *container, ObjID id);
+ ~SoundAsset();
+
+ void play(Audio::Mixer *mixer, Audio::SoundHandle *soundHandle);
+ uint32 getPlayLength();
+
+private:
+
+ void decode10(Common::SeekableReadStream *stream);
+ void decode12(Common::SeekableReadStream *stream);
+ void decode18(Common::SeekableReadStream *stream);
+ void decode1a(Common::SeekableReadStream *stream);
+ void decode44(Common::SeekableReadStream *stream);
+ void decode78(Common::SeekableReadStream *stream);
+ void decode7e(Common::SeekableReadStream *stream);
+
+private:
+
+ Container *_container;
+ ObjID _id;
+
+ Common::Array<byte> _data;
+ uint32 _length;
+ uint32 _frequency;
+};
+
+class SoundManager {
+public:
+ SoundManager(MacVentureEngine *engine, Audio::Mixer *mixer);
+ ~SoundManager();
+
+ uint32 playSound(ObjID sound);
+
+private:
+ void ensureLoaded(ObjID sound);
+
+private:
+
+ Container *_container;
+ Common::HashMap<ObjID, SoundAsset*> _assets;
+ Audio::SoundHandle _handle;
+ Audio::Mixer *_mixer;
+
+};
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/stringtable.h b/engines/macventure/stringtable.h
new file mode 100644
index 0000000000..2f1d671696
--- /dev/null
+++ b/engines/macventure/stringtable.h
@@ -0,0 +1,109 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_STRINGTABLE_H
+#define MACVENTURE_STRINGTABLE_H
+
+#include "macventure/macventure.h"
+
+#include "common/file.h"
+
+namespace MacVenture {
+
+extern void toASCII(Common::String &str);
+
+enum StringTableID {
+ kErrorStringTableID = 0x80,
+ kFilenamesStringTableID = 0x81,
+ kCommonArticlesStringTableID = 0x82,
+ kNamingArticlesStringTableID = 0x83,
+ kIndirectArticlesStringTableID = 0x84
+};
+
+class StringTable {
+public:
+ StringTable(MacVentureEngine *engine, Common::MacResManager *resMan, StringTableID id) {
+ _engine = engine;
+ _resourceManager = resMan;
+ _id = id;
+
+ if (!loadStrings())
+ error("ENGINE: Could not load string table %x", id);
+ }
+
+ ~StringTable() {
+
+ }
+
+ const Common::Array<Common::String> &getStrings() {
+ return _strings;
+ }
+
+ Common::String getString(uint ndx) {
+ assert(ndx < _strings.size());
+ return _strings[ndx];
+ }
+
+private:
+
+ bool loadStrings() {
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+
+ if ((resArray = _resourceManager->getResIDArray(MKTAG('S', 'T', 'R', '#'))).size() == 0)
+ return false;
+
+ res = _resourceManager->getResource(MKTAG('S', 'T', 'R', '#'), _id);
+
+ _strings.push_back("dummy"); // String tables are 1-indexed
+ uint16 numStrings = res->readUint16BE();
+ uint8 strLength = 0;
+ for (uint i = 0; i < numStrings; ++i) {
+ strLength = res->readByte();
+ char *str = new char[strLength + 1];
+ res->read(str, strLength);
+ str[strLength] = '\0';
+ // HACK until a proper special char implementation is found, this will have to do.
+ Common::String result = Common::String(str);
+ toASCII(result);
+ debugC(4, kMVDebugText, "Loaded string %s", str);
+ _strings.push_back(Common::String(result));
+ delete[] str;
+ }
+
+ delete res;
+ return true;
+ }
+
+private:
+
+ MacVentureEngine *_engine;
+ Common::MacResManager *_resourceManager;
+
+ StringTableID _id;
+
+ Common::Array<Common::String> _strings;
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/text.cpp b/engines/macventure/text.cpp
new file mode 100644
index 0000000000..6671c28750
--- /dev/null
+++ b/engines/macventure/text.cpp
@@ -0,0 +1,201 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/text.h"
+
+namespace MacVenture {
+TextAsset::TextAsset(MacVentureEngine *engine, ObjID objid, ObjID source, ObjID target, Container *container, bool isOld, const HuffmanLists *huffman) {
+ _id = objid;
+ _sourceObj = source;
+ _targetObj = target;
+ _container = container;
+ _huffman = huffman;
+ _isOld = isOld;
+ _engine = engine;
+
+ if (_isOld) {
+ decodeOld();
+ } else {
+ decodeHuffman();
+ }
+}
+
+void TextAsset::decodeOld() {
+ Common::SeekableReadStream *res = _container->getItem(_id);
+ uint16 strLen = res->readUint16BE();
+ Common::BitStream32BELSB stream(res, true);
+ char *str = new char[strLen + 1];
+ bool lowercase = false;
+ char c;
+ for (uint16 i = 0; i < strLen; i++) {
+ char val = stream.getBits(5);
+ if (val == 0x0) { // Space
+ c = ' ';
+ } else if (val >= 0x1 && val <= 0x1A) {
+ if (lowercase) { // Ascii a-z
+ c = val + 0x60;
+ } else { // Ascii A-Z
+ c = val + 0x40;
+ }
+ lowercase = true;
+ } else if (val == 0x1B) {
+ if (lowercase) {
+ c = '.';
+ } else {
+ c = ',';
+ }
+ lowercase = true;
+ } else if (val == 0x1C) {
+ if (lowercase) {
+ c = '\'';
+ } else {
+ c = '"';
+ }
+ lowercase = true;
+ } else if (val == 0x1D) { // Composite
+ ObjID subval = stream.getBits(16);
+ Common::String child;
+ if (subval & 0x8000) {
+ // Composite object id
+ subval ^= 0xFFFF;
+ child = getNoun(subval);
+ } else {
+ // Just another id
+ // HACK, see below in getNoun()
+ child = *TextAsset(_engine, subval, _sourceObj, _targetObj, _container, _isOld, _huffman).decode();
+ }
+ if (child.size() > 0) {
+ c = '?'; // HACK Will fix later, should append
+ }
+ lowercase = true;
+ } else if (val == 0x1E) {
+ c = stream.getBits(8);
+ lowercase = true;
+ } else if (val == 0x1F) {
+ lowercase = !lowercase;
+ } else {
+ warning("Unrecognized char in old text %d, pos %d", _id, i);
+ }
+ str[i] = c;
+ }
+
+ str[strLen] = '\0';
+ debugC(3, kMVDebugText, "Decoded string [%d] (old encoding): %s", _id, str);
+ _decoded = Common::String(str);
+}
+
+void TextAsset::decodeHuffman() {
+ _decoded = Common::String("");
+ Common::SeekableReadStream *res = _container->getItem(_id);
+ Common::BitStream8MSB stream(res, true);
+ uint16 strLen = 0;
+ if (stream.getBit()) {
+ strLen = stream.getBits(15);
+ } else {
+ strLen = stream.getBits(7);
+ }
+ uint32 mask = 0;
+ uint32 symbol = 0;
+ char c;
+ for (uint16 i = 0; i < strLen; i++) {
+ mask = stream.peekBits(16);
+
+ uint32 entry;
+ // Find the length index
+ for (entry = 0; entry < _huffman->getNumEntries(); entry++) {
+ if (mask < _huffman->getMask(entry)) {
+ break;
+ }
+ }
+
+ stream.skip(_huffman->getLength(entry));
+
+ symbol = _huffman->getSymbol(entry);
+
+ if (symbol == 1) { // 7-bit ascii
+ c = stream.getBits(7);
+ _decoded += c;
+ } else if (symbol == 2) { // Composite
+ if (stream.getBit()) { // TextID
+ ObjID embedId = stream.getBits(15);
+ uint pos = stream.pos(); // HACK, part 1
+ TextAsset embedded(_engine, embedId, _sourceObj, _targetObj, _container, _isOld, _huffman);
+ stream.rewind();// HACK, part 2
+ stream.skip(pos);
+
+ _decoded.replace(_decoded.end(), _decoded.end(), *embedded.decode());
+
+ // Another HACK, to get around that EOS char I insert at the end
+ _decoded.replace(_decoded.end() - 1, _decoded.end(), "");
+ } else { //Composite obj string
+ ObjID embedId = stream.getBits(8);
+ uint pos = stream.pos(); // HACK, part 1
+
+ _decoded.replace(_decoded.end(), _decoded.end(), getNoun(embedId));
+ stream.rewind();// HACK, part 2
+ stream.skip(pos);
+
+ // Another HACK, to get around that EOS char I insert at the end
+ _decoded.replace(_decoded.end() - 1, _decoded.end(), "");
+ }
+ } else { // Plain ascii
+ c = symbol & 0xFF;
+ _decoded.replace(_decoded.end(), _decoded.end(), Common::String(c));
+ }
+ }
+ _decoded += '\0';
+ debugC(3, kMVDebugText, "Decoded string [%d] (new encoding): %s", _id, _decoded.c_str());
+}
+Common::String TextAsset::getNoun(ObjID subval) {
+ ObjID obj;
+ Common::String name;
+ if (subval & 8) {
+ obj = _targetObj;
+ } else {
+ obj = _sourceObj;
+ }
+ if ((subval & 3) == 1) {
+ uint idx = _engine->getPrefixNdx(obj);
+ idx = ((idx >> 4) & 3) + 1;
+ name = _engine->getNoun(idx);
+ } else {
+ // HACK, there should be a pool of assets or something like in the GUI
+ name = *TextAsset(_engine, obj, _sourceObj, _targetObj, _container, _isOld, _huffman).decode();
+ switch (subval & 3) {
+ case 2:
+ name = _engine->getPrefixString(0, obj) + name;
+ break;
+ case 3:
+ name = _engine->getPrefixString(2, obj) + name;
+ break;
+ }
+ }
+ if (name.size() && (subval & 4)) {
+ Common::String tmp = name;
+ name.toUppercase();
+ name.replace(1, name.size() - 1, tmp, 1, tmp.size() - 1);
+ }
+
+ return name;
+}
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/text.h b/engines/macventure/text.h
new file mode 100644
index 0000000000..88dd112681
--- /dev/null
+++ b/engines/macventure/text.h
@@ -0,0 +1,64 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_TEXT_H
+#define MACVENTURE_TEXT_H
+
+#include "macventure/macventure.h"
+#include "macventure/hufflists.h"
+
+namespace MacVenture {
+typedef uint32 ObjID;
+class MacVentureEngine;
+
+class TextAsset {
+public:
+ TextAsset(MacVentureEngine *engine, ObjID objid, ObjID source, ObjID target, Container *container, bool isOld, const HuffmanLists *huffman);
+ ~TextAsset() {}
+
+ const Common::String *decode() {
+ return &_decoded;
+ }
+
+private:
+ void decodeOld();
+ void decodeHuffman();
+
+ Common::String getNoun(ObjID id);
+
+private:
+ MacVentureEngine *_engine;
+
+ Container *_container;
+ ObjID _id;
+ ObjID _targetObj;
+ ObjID _sourceObj;
+ const HuffmanLists *_huffman;
+ bool _isOld;
+
+ Common::String _decoded;
+
+};
+
+} // End of namespace MacVenture
+
+#endif
diff --git a/engines/macventure/windows.cpp b/engines/macventure/windows.cpp
new file mode 100644
index 0000000000..b3d544924f
--- /dev/null
+++ b/engines/macventure/windows.cpp
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "macventure/windows.h"
+
+namespace MacVenture {
+
+BorderBounds borderBounds(MVWindowType type) {
+ switch (type) {
+ case MacVenture::kDocument:
+ break;
+ case MacVenture::kDBox:
+ break;
+ case MacVenture::kPlainDBox:
+ return BorderBounds(3, 3, 3, 3);
+ case MacVenture::kAltBox:
+ return BorderBounds(0, 0, 0, 0); // Hand-tested
+ case MacVenture::kNoGrowDoc:
+ return BorderBounds(1, 20, 1, 1);
+ case MacVenture::kMovableDBox:
+ break;
+ case MacVenture::kZoomDoc:
+ return BorderBounds(1, 20, 17, 1);
+ case MacVenture::kZoomNoGrow:
+ break;
+ case MacVenture::kInvWindow:
+ return BorderBounds(1, 20, 17, 17);
+ case MacVenture::kRDoc16:
+ break;
+ case MacVenture::kRDoc4:
+ return BorderBounds(1, 20, 1, 1);
+ case MacVenture::kRDoc6:
+ break;
+ case MacVenture::kRDoc10:
+ break;
+ default:
+ break;
+ }
+
+ return BorderBounds(0, 0, 0, 0);
+}
+} // End of namespace MacVenture
diff --git a/engines/macventure/windows.h b/engines/macventure/windows.h
new file mode 100644
index 0000000000..fd3acefa7b
--- /dev/null
+++ b/engines/macventure/windows.h
@@ -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.
+*
+*/
+
+#ifndef MACVENTURE_WINDOWS_H
+#define MACVENTURE_WINDOWS_H
+
+#include "common/rect.h"
+#include "common/array.h"
+
+namespace MacVenture {
+// massive HACK
+typedef uint32 ObjID;
+
+enum WindowReference {
+ kNoWindow = 0,
+ kInventoryStart = 1,
+ kCommandsWindow = 0x80,
+ kMainGameWindow = 0x81,
+ kOutConsoleWindow = 0x82,
+ kSelfWindow = 0x83,
+ kExitsWindow = 0x84,
+ kDiplomaWindow = 0x85
+};
+
+enum MVWindowType {
+ kDocument = 0x00,
+ kDBox = 0x01,
+ kPlainDBox = 0x02,
+ kAltBox = 0x03,
+ kNoGrowDoc = 0x04,
+ kMovableDBox = 0x05,
+ kZoomDoc = 0x08,
+ kZoomNoGrow = 0x0c,
+ // WebVenture assigns arbitrary kinds post-loading
+ kInvWindow = 0x0e,
+ kRDoc16 = 0x10,
+ kRDoc4 = 0x12,
+ kRDoc6 = 0x14,
+ kRDoc10 = 0x16,
+ kNoType = 0xFF
+};
+
+struct DrawableObject {
+ ObjID obj;
+ byte mode;
+ DrawableObject(ObjID id, byte md) {
+ obj = id;
+ mode = md;
+ }
+};
+
+enum {
+ kScrollAmount = 10
+};
+
+struct WindowData {
+ Common::Rect bounds;
+ MVWindowType type;
+ ObjID objRef;
+ uint16 visible;
+ uint16 hasCloseBox;
+ WindowReference refcon;
+ uint8 titleLength;
+ Common::String title;
+ Common::Array<DrawableObject> children;
+ bool updateScroll;
+ Common::Point scrollPos;
+};
+
+struct BorderBounds {
+ uint16 leftOffset;
+ uint16 topOffset;
+ uint16 rightOffset;
+ uint16 bottomOffset;
+
+ BorderBounds(uint16 l, uint16 t, uint16 r, uint16 b) :
+ leftOffset(l), topOffset(t), rightOffset(r), bottomOffset(b) {}
+};
+}
+#endif
diff --git a/engines/macventure/world.cpp b/engines/macventure/world.cpp
new file mode 100644
index 0000000000..7c14618f3e
--- /dev/null
+++ b/engines/macventure/world.cpp
@@ -0,0 +1,350 @@
+#include "macventure/world.h"
+#include "macventure/macventure.h"
+
+#include "common/file.h"
+
+namespace MacVenture {
+
+World::World(MacVentureEngine *engine, Common::MacResManager *resMan) {
+ _resourceManager = resMan;
+ _engine = engine;
+ _saveGame = NULL;
+ _gameText = NULL;
+
+ startNewGame();
+
+ _objectConstants = new Container(_engine->getFilePath(kObjectPathID));
+ calculateObjectRelations();
+
+ _gameText = new Container(_engine->getFilePath(kTextPathID));
+}
+
+
+World::~World() {
+
+ if (_saveGame)
+ delete _saveGame;
+
+ if (_objectConstants)
+ delete _objectConstants;
+
+ if (_gameText)
+ delete _gameText;
+}
+
+void World::startNewGame() {
+ if (_saveGame)
+ delete _saveGame;
+
+ if ((_startGameFileName = _engine->getStartGameFileName()) == "")
+ error("WORLD: Could not load initial game configuration");
+
+ Common::File saveGameFile;
+ if (!saveGameFile.open(_startGameFileName))
+ error("WORLD: Could not load initial game configuration");
+
+ debugC(2, kMVDebugMain, "Loading save game state from %s", _startGameFileName.c_str());
+ Common::SeekableReadStream *saveGameRes = saveGameFile.readStream(saveGameFile.size());
+
+ _saveGame = new SaveGame(_engine, saveGameRes);
+
+ calculateObjectRelations();
+
+ delete saveGameRes;
+ saveGameFile.close();
+}
+
+uint32 World::getObjAttr(ObjID objID, uint32 attrID) {
+ uint res;
+ uint32 index = _engine->getGlobalSettings()._attrIndices[attrID];
+ // HACK, but if I try to initialize it in the else clause, it goes out of scope and segfaults
+ Common::SeekableReadStream *objStream = _objectConstants->getItem(objID);
+ if (!(index & 0x80)) { // It's not a constant
+ res = _saveGame->getAttr(objID, index);
+ } else {
+ index &= 0x7F;
+ if (objStream->size() == 0) {
+ return 0;
+ }
+ // Look for the right attribute inside the object
+ objStream->skip(index * 2);
+ res = objStream->readByte() << 8;
+ res |= objStream->readByte();
+ }
+ res &= _engine->getGlobalSettings()._attrMasks[attrID];
+ res >>= _engine->getGlobalSettings()._attrShifts[attrID];
+ if (res & 0x8000)
+ res = -((res ^ 0xffff) + 1);
+ debugC(5, kMVDebugMain, "Attribute %x from object %x is %x", attrID, objID, res);
+ delete objStream;
+ return res;
+}
+
+void World::setObjAttr(ObjID objID, uint32 attrID, Attribute value) {
+ if (attrID == kAttrPosX || attrID == kAttrPosY) {
+ // Round to scale
+ // Intentionally empty, we don't seem to require this functionality
+ }
+
+ if (attrID == kAttrParentObject)
+ setParent(objID, value);
+
+ if (attrID < kAttrOtherDoor)
+ _engine->enqueueObject(kUpdateObject, objID);
+
+ uint32 idx = _engine->getGlobalSettings()._attrIndices[attrID];
+ value <<= _engine->getGlobalSettings()._attrShifts[attrID];
+ value &= _engine->getGlobalSettings()._attrMasks[attrID];
+ Attribute oldVal = _saveGame->getAttr(objID, idx);
+ oldVal &= ~_engine->getGlobalSettings()._attrMasks[attrID];
+ _saveGame->setAttr(idx, objID, (value | oldVal));
+ _engine->gameChanged();
+}
+
+bool MacVenture::World::isObjActive(ObjID obj) {
+ ObjID destObj = _engine->getDestObject();
+ Common::Point p = _engine->getDeltaPoint();
+ ControlAction selectedControl = _engine->getSelectedControl();
+ if (!getAncestor(obj)) {
+ return false; // If our ancestor is the garbage (obj 0), we're inactive
+ }
+ if (_engine->getInvolvedObjects() >= 2 && // If (we need > 1 objs for the command) &&
+ destObj > 0 && // we have a destination object &&
+ !getAncestor(destObj)) { // but that destination object is in the garbage
+ return false;
+ }
+ if (selectedControl != kMoveObject) {
+ return true; // We only need one
+ }
+ // Handle move object
+ if (!isObjDraggable(obj)) {
+ return false; // We can't move it
+ }
+ if (getObjAttr(1, kAttrParentObject) != destObj) {
+ return true; // if the target is not the player's parent, we can go
+ }
+ Common::Rect rect(kScreenWidth, kScreenHeight);
+ rect.top -= getObjAttr(obj, kAttrPosY) + p.y;
+ rect.left -= getObjAttr(obj, kAttrPosX) + p.x;
+ return intersects(obj, rect);
+}
+
+ObjID World::getAncestor(ObjID objID) {
+ ObjID root = getObjAttr(1, kAttrParentObject);
+ while (objID != 0 && objID != 1 && objID != root) {
+ objID = getObjAttr(objID, kAttrParentObject);
+ }
+ return objID;
+}
+
+Common::Array<ObjID> World::getFamily(ObjID objID, bool recursive) {
+ Common::Array<ObjID> res;
+ res.push_back(objID);
+ res.push_back(getChildren(objID, recursive));
+ return res;
+}
+
+Common::Array<ObjID> World::getChildren(ObjID objID, bool recursive) {
+ Common::Array<ObjID> res;
+ ObjID child = _relations[objID * 2];
+ while (child) {
+ res.push_back(child);
+ if (!recursive)
+ res.push_back(getChildren(child, false));
+ child = _relations[child * 2 + 1];
+ }
+ return res;
+}
+
+Attribute World::getGlobal(uint32 attrID) {
+ return _saveGame->getGlobals()[attrID];
+}
+
+void World::setGlobal(uint32 attrID, Attribute value) {
+ _saveGame->setGlobal(attrID, value);
+}
+
+void World::updateObj(ObjID objID) {
+ WindowReference win;
+ if (getObjAttr(1, kAttrParentObject) == objID) {
+ win = kMainGameWindow;
+ } else {
+ win = _engine->getObjWindow(objID);
+ }
+ if (win) {
+ _engine->focusObjWin(objID);
+ _engine->runObjQueue();
+ _engine->updateWindow(win);
+ }
+}
+
+void World::captureChildren(ObjID objID) {
+ warning("Capture children unimplemented!");
+}
+
+void World::releaseChildren(ObjID objID) {
+ warning("Release children unimplemented!");
+}
+
+Common::String World::getText(ObjID objID, ObjID source, ObjID target) {
+ if (objID & 0x8000) {
+ return _engine->getUserInput();
+ }
+ TextAsset text = TextAsset(_engine, objID, source, target, _gameText, _engine->isOldText(), _engine->getDecodingHuffman());
+
+ return *text.decode();
+}
+
+
+bool World::isObjDraggable(ObjID objID) {
+ return (getObjAttr(objID, kAttrInvisible) == 0 &&
+ getObjAttr(objID, kAttrUnclickable) == 0 &&
+ getObjAttr(objID, kAttrUndraggable) == 0);
+}
+
+bool World::intersects(ObjID objID, Common::Rect rect) {
+ return _engine->getObjBounds(objID).intersects(rect);
+}
+
+void World::calculateObjectRelations() {
+ _relations.clear();
+ ObjID val, next;
+ uint32 numObjs = _engine->getGlobalSettings()._numObjects;
+ const AttributeGroup &parents = *_saveGame->getGroup(0);
+ for (uint i = 0; i < numObjs * 2; i++) {
+ _relations.push_back(0);
+ }
+ for (uint i = numObjs - 1; i > 0; i--) {
+ val = parents[i];
+ next = _relations[val * 2];
+ if (next) {
+ _relations[i * 2 + 1] = next;
+ }
+ _relations[val * 2] = i;
+ }
+}
+
+void World::setParent(ObjID child, ObjID newParent) {
+ ObjID old = _saveGame->getAttr(child, kAttrParentObject);
+ if (newParent == child)
+ return;
+
+ ObjID oldNdx = old * 2;
+ old = _relations[oldNdx];
+ while (old != child) {
+ oldNdx = (old * 2) + 1;
+ old = _relations[oldNdx];
+ }
+ _relations[oldNdx] = _relations[(old * 2) + 1];
+ oldNdx = newParent * 2;
+ old = _relations[oldNdx];
+ while (old && old <= child) {
+ oldNdx = (old * 2) + 1;
+ old = _relations[oldNdx];
+ }
+ _relations[child * 2 + 1] = old;
+ _relations[oldNdx] = child;
+}
+
+void World::loadGameFrom(Common::InSaveFile *file) {
+ if (_saveGame) {
+ delete _saveGame;
+ }
+ _saveGame = new SaveGame(_engine, file);
+ calculateObjectRelations();
+}
+
+void World::saveGameInto(Common::OutSaveFile *file) {
+ _saveGame->saveInto(file);
+}
+
+// SaveGame
+SaveGame::SaveGame(MacVentureEngine *engine, Common::SeekableReadStream *res) {
+ _groups = Common::Array<AttributeGroup>();
+ loadGroups(engine, res);
+ _globals = Common::Array<uint16>();
+ loadGlobals(engine, res);
+ _text = Common::String();
+ loadText(engine, res);
+}
+
+SaveGame::~SaveGame() {
+}
+
+
+Attribute SaveGame::getAttr(ObjID objID, uint32 attrID) {
+ return _groups[attrID][objID];
+}
+
+void SaveGame::setAttr(uint32 attrID, ObjID objID, Attribute value) {
+ _groups[attrID][objID] = value;
+}
+
+const Common::Array<AttributeGroup> &MacVenture::SaveGame::getGroups() {
+ return _groups;
+}
+
+const AttributeGroup *SaveGame::getGroup(uint32 groupID) {
+ assert(groupID < _groups.size());
+ return &(_groups[groupID]);
+}
+
+void SaveGame::setGlobal(uint32 attrID, Attribute value) {
+ _globals[attrID] = value;
+}
+
+const Common::Array<uint16> &SaveGame::getGlobals() {
+ return _globals;
+}
+
+const Common::String &SaveGame::getText() {
+ return _text;
+}
+
+void SaveGame::saveInto(Common::OutSaveFile *file) {
+ warning("Saving the game not yet tested!");
+ // Save attibutes
+ Common::Array<AttributeGroup>::const_iterator itg;
+ for (itg = _groups.begin(); itg != _groups.end(); itg++) {
+ Common::Array<Attribute>::const_iterator ita;
+ for (ita = itg->begin(); ita != itg->end(); ita++) {
+ file->writeUint16BE((*ita));
+ }
+ }
+ // Save globals
+ Common::Array<uint16>::const_iterator global;
+ for (global = _globals.begin(); global != _globals.end(); global++) {
+ file->writeUint16BE((*global));
+ }
+ // Save text
+ // TODO: Insert text from GUI console
+ _text = "Hello";
+ file->write(_text.c_str(), _text.size());
+}
+
+void SaveGame::loadGroups(MacVentureEngine *engine, Common::SeekableReadStream *res) {
+ GlobalSettings settings = engine->getGlobalSettings();
+ for (int i = 0; i < settings._numGroups; ++i) {
+ AttributeGroup g;
+ for (int j = 0; j < settings._numObjects; ++j) {
+ g.push_back(res->readUint16BE());
+ }
+
+ _groups.push_back(g);
+ }
+}
+
+void SaveGame::loadGlobals(MacVentureEngine *engine, Common::SeekableReadStream *res) {
+ GlobalSettings settings = engine->getGlobalSettings();
+ for (int i = 0; i < settings._numGlobals; ++i) {
+ _globals.push_back(res->readUint16BE());
+ }
+}
+
+void SaveGame::loadText(MacVentureEngine *engine, Common::SeekableReadStream *res) {
+ // TODO: Load console text. For now, the GUI doesn't even look at this.
+ _text = "Placeholder Console Text";
+}
+
+
+} // End of namespace MacVenture
diff --git a/engines/macventure/world.h b/engines/macventure/world.h
new file mode 100644
index 0000000000..e78ab7f0db
--- /dev/null
+++ b/engines/macventure/world.h
@@ -0,0 +1,139 @@
+/* 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.
+*
+*/
+
+#ifndef MACVENTURE_WORLD_H
+#define MACVENTURE_WORLD_H
+
+#include "macventure/container.h"
+#include "macventure/text.h"
+
+namespace MacVenture {
+
+typedef uint32 ObjID;
+typedef uint16 Attribute;
+typedef Common::Array<Attribute> AttributeGroup;
+class TextAsset;
+
+enum ObjectAttributeID {
+ kAttrParentObject = 0,
+ kAttrPosX = 1,
+ kAttrPosY = 2,
+ kAttrInvisible = 3,
+ kAttrUnclickable = 4,
+ kAttrUndraggable = 5,
+ kAttrContainerOpen = 6,
+ kAttrPrefixes = 7,
+ kAttrIsExit = 8,
+ kAttrExitX = 9,
+ kAttrExitY = 10,
+ kAttrHiddenExit = 11,
+ kAttrOtherDoor = 12,
+ kAttrIsOpen = 13,
+ kAttrIsLocked = 14,
+ kAttrWeight = 16,
+ kAttrSize = 17,
+ kAttrHasDescription = 19,
+ kAttrIsDoor = 20,
+ kAttrIsContainer = 22,
+ kAttrIsOperable = 23,
+ kAttrIsEnterable = 24,
+ kAttrIsEdible = 25
+};
+
+class SaveGame {
+public:
+ SaveGame(MacVentureEngine *engine, Common::SeekableReadStream *res);
+ ~SaveGame();
+
+ Attribute getAttr(ObjID objID, uint32 attrID);
+ void setAttr(uint32 attrID, ObjID objID, Attribute value);
+
+ void setGlobal(uint32 attrID, Attribute value);
+ const Common::Array<uint16> &getGlobals();
+
+ const Common::Array<AttributeGroup> &getGroups();
+ const AttributeGroup *getGroup(uint32 groupID);
+ const Common::String &getText();
+
+ void saveInto(Common::OutSaveFile *file);
+
+private:
+ void loadGroups(MacVentureEngine *engine, Common::SeekableReadStream *res);
+ void loadGlobals(MacVentureEngine *engine, Common::SeekableReadStream *res);
+ void loadText(MacVentureEngine *engine, Common::SeekableReadStream *res);
+
+private:
+ Common::Array<AttributeGroup> _groups;
+ Common::Array<uint16> _globals;
+ Common::String _text;
+};
+
+class World {
+public:
+ World(MacVentureEngine *engine, Common::MacResManager *resMan);
+ ~World();
+
+ void startNewGame();
+
+ void setObjAttr(ObjID objID, uint32 attrID, Attribute value);
+ void setGlobal(uint32 attrID, Attribute value);
+ void updateObj(ObjID objID);
+ void captureChildren(ObjID objID);
+ void releaseChildren(ObjID objID);
+
+ uint32 getObjAttr(ObjID objID, uint32 attrID);
+ Attribute getGlobal(uint32 attrID);
+ Common::String getText(ObjID objID, ObjID source, ObjID target);
+
+ bool isObjActive(ObjID objID);
+
+ ObjID getAncestor(ObjID objID);
+ Common::Array<ObjID> getFamily(ObjID objID, bool recursive);
+ Common::Array<ObjID> getChildren(ObjID objID, bool recursive);
+
+ void loadGameFrom(Common::InSaveFile *file);
+ void saveGameInto(Common::OutSaveFile *file);
+
+private:
+ bool isObjDraggable(ObjID objID);
+ bool intersects(ObjID objID, Common::Rect rect);
+
+ void calculateObjectRelations();
+ void setParent(ObjID child, ObjID newParent);
+
+private:
+ MacVentureEngine *_engine;
+ Common::MacResManager *_resourceManager;
+
+ Common::String _startGameFileName;
+
+ SaveGame *_saveGame;
+
+ Container *_objectConstants;
+ Container *_gameText;
+
+ Common::Array<ObjID> _relations; // Parent-child relations, stored in Williams Heap format
+};
+
+} // End of namespace MacVenture
+
+#endif