aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorStrangerke2012-09-05 03:20:30 -0700
committerStrangerke2012-09-05 03:20:30 -0700
commit5780748b62119988dc219d5c636681766065bd75 (patch)
treeb9a1a11849d6cd956b709ec0e21c820b042edac6 /engines
parent6472ef86bbee92bc02a67d87677dc6b0925a0362 (diff)
parentc737e6429866f18638a6b61103e4e1c7095407e6 (diff)
downloadscummvm-rg350-5780748b62119988dc219d5c636681766065bd75.tar.gz
scummvm-rg350-5780748b62119988dc219d5c636681766065bd75.tar.bz2
scummvm-rg350-5780748b62119988dc219d5c636681766065bd75.zip
Merge pull request #273 from fuzzie/tony
Tony engine (Tony Tough)
Diffstat (limited to 'engines')
-rw-r--r--engines/agos/installshield_cab.cpp220
-rw-r--r--engines/agos/module.mk1
-rw-r--r--engines/agos/res.cpp8
-rw-r--r--engines/configure.engines1
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/plugins_table.h3
-rw-r--r--engines/tony/custom.cpp2581
-rw-r--r--engines/tony/custom.h71
-rw-r--r--engines/tony/debugger.cpp130
-rw-r--r--engines/tony/debugger.h (renamed from engines/agos/installshield_cab.h)30
-rw-r--r--engines/tony/detection.cpp194
-rw-r--r--engines/tony/detection_tables.h145
-rw-r--r--engines/tony/font.cpp1200
-rw-r--r--engines/tony/font.h379
-rw-r--r--engines/tony/game.cpp1617
-rw-r--r--engines/tony/game.h340
-rw-r--r--engines/tony/gfxcore.cpp2212
-rw-r--r--engines/tony/gfxcore.h516
-rw-r--r--engines/tony/gfxengine.cpp862
-rw-r--r--engines/tony/gfxengine.h139
-rw-r--r--engines/tony/globals.cpp135
-rw-r--r--engines/tony/globals.h286
-rw-r--r--engines/tony/input.cpp157
-rw-r--r--engines/tony/input.h88
-rw-r--r--engines/tony/inventory.cpp953
-rw-r--r--engines/tony/inventory.h241
-rw-r--r--engines/tony/loc.cpp2322
-rw-r--r--engines/tony/loc.h582
-rw-r--r--engines/tony/module.mk33
-rw-r--r--engines/tony/mpal/expr.cpp433
-rw-r--r--engines/tony/mpal/expr.h79
-rw-r--r--engines/tony/mpal/loadmpc.cpp790
-rw-r--r--engines/tony/mpal/loadmpc.h59
-rw-r--r--engines/tony/mpal/lzo.cpp511
-rw-r--r--engines/tony/mpal/lzo.h111
-rw-r--r--engines/tony/mpal/memory.cpp129
-rw-r--r--engines/tony/mpal/memory.h76
-rw-r--r--engines/tony/mpal/mpal.cpp2102
-rw-r--r--engines/tony/mpal/mpal.h516
-rw-r--r--engines/tony/mpal/mpaldll.h251
-rw-r--r--engines/tony/mpal/mpalutils.cpp115
-rw-r--r--engines/tony/mpal/mpalutils.h74
-rw-r--r--engines/tony/resid.h71
-rw-r--r--engines/tony/sound.cpp688
-rw-r--r--engines/tony/sound.h377
-rw-r--r--engines/tony/tony.cpp795
-rw-r--r--engines/tony/tony.h243
-rw-r--r--engines/tony/tonychar.cpp1945
-rw-r--r--engines/tony/tonychar.h482
-rw-r--r--engines/tony/utils.cpp448
-rw-r--r--engines/tony/utils.h176
-rw-r--r--engines/tony/window.cpp336
-rw-r--r--engines/tony/window.h98
53 files changed, 26119 insertions, 237 deletions
diff --git a/engines/agos/installshield_cab.cpp b/engines/agos/installshield_cab.cpp
deleted file mode 100644
index d4e636f7b3..0000000000
--- a/engines/agos/installshield_cab.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-/* 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.
- *
- */
-
-// The following code is based on unshield
-// Original copyright:
-
-// Copyright (c) 2003 David Eriksson <twogood@users.sourceforge.net>
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-#include "agos/installshield_cab.h"
-
-#include "common/debug.h"
-#include "common/file.h"
-#include "common/memstream.h"
-#include "common/zlib.h"
-
-namespace AGOS {
-
-class InstallShieldCabinet : public Common::Archive {
- Common::String _installShieldFilename;
-
-public:
- InstallShieldCabinet(const Common::String &filename);
- ~InstallShieldCabinet();
-
- // Common::Archive API implementation
- bool hasFile(const Common::String &name) const;
- int listMembers(Common::ArchiveMemberList &list) const;
- const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
- Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
-
-private:
- struct FileEntry {
- uint32 uncompressedSize;
- uint32 compressedSize;
- uint32 offset;
- uint16 flags;
- };
-
- typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
- FileMap _map;
-};
-
-InstallShieldCabinet::~InstallShieldCabinet() {
- _map.clear();
-}
-
-InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _installShieldFilename(filename) {
- Common::File installShieldFile;
-
- if (!installShieldFile.open(_installShieldFilename)) {
- warning("InstallShieldCabinet::InstallShieldCabinet(): Could not find the archive file %s", _installShieldFilename.c_str());
- return;
- }
-
- // Note that we only support a limited subset of cabinet files
- // Only single cabinet files and ones without data shared between
- // cabinets.
-
- // Check for the magic uint32
- if (installShieldFile.readUint32LE() != 0x28635349) {
- warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match");
- return;
- }
-
- uint32 version = installShieldFile.readUint32LE();
-
- if (version != 0x01000004) {
- warning("Unsupported CAB version %08x", version);
- return;
- }
-
- /* uint32 volumeInfo = */ installShieldFile.readUint32LE();
- uint32 cabDescriptorOffset = installShieldFile.readUint32LE();
- /* uint32 cabDescriptorSize = */ installShieldFile.readUint32LE();
-
- installShieldFile.seek(cabDescriptorOffset);
-
- installShieldFile.skip(12);
- uint32 fileTableOffset = installShieldFile.readUint32LE();
- installShieldFile.skip(4);
- uint32 fileTableSize = installShieldFile.readUint32LE();
- uint32 fileTableSize2 = installShieldFile.readUint32LE();
- uint32 directoryCount = installShieldFile.readUint32LE();
- installShieldFile.skip(8);
- uint32 fileCount = installShieldFile.readUint32LE();
-
- if (fileTableSize != fileTableSize2)
- warning("file table sizes do not match");
-
- // We're ignoring file groups and components since we
- // should not need them. Moving on to the files...
-
- installShieldFile.seek(cabDescriptorOffset + fileTableOffset);
- uint32 fileTableCount = directoryCount + fileCount;
- uint32 *fileTableOffsets = new uint32[fileTableCount];
- for (uint32 i = 0; i < fileTableCount; i++)
- fileTableOffsets[i] = installShieldFile.readUint32LE();
-
- for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) {
- installShieldFile.seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]);
- uint32 nameOffset = installShieldFile.readUint32LE();
- /* uint32 directoryIndex = */ installShieldFile.readUint32LE();
-
- // First read in data needed by us to get at the file data
- FileEntry entry;
- entry.flags = installShieldFile.readUint16LE();
- entry.uncompressedSize = installShieldFile.readUint32LE();
- entry.compressedSize = installShieldFile.readUint32LE();
- installShieldFile.skip(20);
- entry.offset = installShieldFile.readUint32LE();
-
- // Then let's get the string
- installShieldFile.seek(cabDescriptorOffset + fileTableOffset + nameOffset);
- Common::String fileName;
-
- char c = installShieldFile.readByte();
- while (c) {
- fileName += c;
- c = installShieldFile.readByte();
- }
- _map[fileName] = entry;
- }
-
- delete[] fileTableOffsets;
-}
-
-bool InstallShieldCabinet::hasFile(const Common::String &name) const {
- return _map.contains(name);
-}
-
-int InstallShieldCabinet::listMembers(Common::ArchiveMemberList &list) const {
- for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++)
- list.push_back(getMember(it->_key));
-
- return _map.size();
-}
-
-const Common::ArchiveMemberPtr InstallShieldCabinet::getMember(const Common::String &name) const {
- return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
-}
-
-Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const {
- if (!_map.contains(name))
- return 0;
-
- const FileEntry &entry = _map[name];
-
- Common::File archiveFile;
- archiveFile.open(_installShieldFilename);
- archiveFile.seek(entry.offset);
-
- if (!(entry.flags & 0x04)) {
- // Not compressed
- return archiveFile.readStream(entry.uncompressedSize);
- }
-
-#ifdef USE_ZLIB
- byte *src = (byte *)malloc(entry.compressedSize);
- byte *dst = (byte *)malloc(entry.uncompressedSize);
-
- archiveFile.read(src, entry.compressedSize);
-
- bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize);
- free(src);
-
- if (!result) {
- warning("failed to inflate CAB file '%s'", name.c_str());
- free(dst);
- return 0;
- }
-
- return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES);
-#else
- warning("zlib required to extract compressed CAB file '%s'", name.c_str());
- return 0;
-#endif
-}
-
-Common::Archive *makeInstallShieldArchive(const Common::String &name) {
- return new InstallShieldCabinet(name);
-}
-
-} // End of namespace AGOS
diff --git a/engines/agos/module.mk b/engines/agos/module.mk
index 7ae5e17bf2..7069d8005b 100644
--- a/engines/agos/module.mk
+++ b/engines/agos/module.mk
@@ -51,7 +51,6 @@ ifdef ENABLE_AGOS2
MODULE_OBJS += \
animation.o \
feeble.o \
- installshield_cab.o \
oracle.o \
script_dp.o \
script_ff.o \
diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp
index 0305879390..2e44a6575c 100644
--- a/engines/agos/res.cpp
+++ b/engines/agos/res.cpp
@@ -23,6 +23,8 @@
// Resource file routines for Simon1/Simon2
+#include "common/archive.h"
+#include "common/installshield_cab.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/textconsole.h"
@@ -31,7 +33,6 @@
#include "agos/agos.h"
#include "agos/intern.h"
#include "agos/sound.h"
-#include "agos/installshield_cab.h"
#include "common/zlib.h"
@@ -43,7 +44,10 @@ ArchiveMan::ArchiveMan() {
#ifdef ENABLE_AGOS2
void ArchiveMan::registerArchive(const Common::String &filename, int priority) {
- add(filename, makeInstallShieldArchive(filename), priority);
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(filename);
+
+ if (stream)
+ add(filename, makeInstallShieldArchive(stream, DisposeAfterUse::YES), priority);
}
#endif
diff --git a/engines/configure.engines b/engines/configure.engines
index b8b1677e2a..81cf1766f3 100644
--- a/engines/configure.engines
+++ b/engines/configure.engines
@@ -43,6 +43,7 @@ add_engine tinsel "Tinsel" yes
add_engine toltecs "3 Skulls of the Toltecs" no
add_engine toon "Toonstruck" yes
add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes
+add_engine tony "Tony Tough and the Night of Roasted Moths" no
add_engine tsage "TsAGE" yes
add_engine tucker "Bud Tucker in Double Trouble" yes
add_engine wintermute "Wintermute" no "" "png zlib vorbis"
diff --git a/engines/engines.mk b/engines/engines.mk
index d5d912f8b7..fd97f67c08 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -197,6 +197,11 @@ DEFINES += -DENABLE_TOLTECS=$(ENABLE_TOLTECS)
MODULES += engines/toltecs
endif
+ifdef ENABLE_TONY
+DEFINES += -DENABLE_TONY=$(ENABLE_TONY)
+MODULES += engines/tony
+endif
+
ifdef ENABLE_TOON
DEFINES += -DENABLE_TOON=$(ENABLE_TOON)
MODULES += engines/toon
diff --git a/engines/plugins_table.h b/engines/plugins_table.h
index fb35969b0b..9e6dd4ac80 100644
--- a/engines/plugins_table.h
+++ b/engines/plugins_table.h
@@ -89,6 +89,9 @@ LINK_PLUGIN(TINSEL)
#if PLUGIN_ENABLED_STATIC(TOLTECS)
LINK_PLUGIN(TOLTECS)
#endif
+#if PLUGIN_ENABLED_STATIC(TONY)
+LINK_PLUGIN(TONY)
+#endif
#if PLUGIN_ENABLED_STATIC(TOON)
LINK_PLUGIN(TOON)
#endif
diff --git a/engines/tony/custom.cpp b/engines/tony/custom.cpp
new file mode 100644
index 0000000000..f0a9197c6d
--- /dev/null
+++ b/engines/tony/custom.cpp
@@ -0,0 +1,2581 @@
+/* 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.
+ *
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/memory.h"
+#include "tony/custom.h"
+#include "tony/font.h"
+#include "tony/game.h"
+#include "tony/gfxcore.h"
+#include "tony/tony.h"
+#include "tony/tonychar.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+const char *ambianceFile[] = {
+ "None",
+ "1.ADP", // Grilli.WAV
+ "2.ADP", // Grilli-Ovattati.WAV
+ "3.ADP", // Grilli-Vento.WAV
+ "3.ADP", // Grilli-Vento1.WAV
+ "5.ADP", // Vento1.WAV
+ "4.ADP", // Mare1.WAV
+ "6.ADP" // Mare1.WAV half volume
+};
+
+struct MusicFileEntry {
+ const char *name;
+ int sync;
+};
+
+const MusicFileEntry musicFiles[] = {
+ {"00.ADP", 0}, {"01.ADP", 0},
+ {"02.ADP", 0}, {"03.ADP", 0},
+ {"04.ADP", 0}, {"05.ADP", 0},
+ {"06.ADP", 0}, {"07.ADP", 0},
+ {"08.ADP", 2450}, {"09.ADP", 0},
+ {"10.ADP", 0}, {"11.ADP", 0},
+ {"12.ADP", 0}, {"13.ADP", 0},
+ {"14.ADP", 0}, {"15.ADP", 0},
+ {"16.ADP", 0}, {"17.ADP", 0},
+ {"18.ADP", 0}, {"19.ADP", 0},
+ {"20.ADP", 0}, {"21.ADP", 0},
+ {"22.ADP", 0}, {"23.ADP", 0},
+ {"24.ADP", 0}, {"25.ADP", 0},
+ {"26.ADP", 0}, {"27.ADP", 0},
+ {"28.ADP", 1670}, {"29.ADP", 0},
+ {"30.ADP", 0}, {"31.ADP", 0},
+ {"32.ADP", 2900}, {"33.ADP", 0},
+ {"34.ADP", 0}, {"35.ADP", 0},
+ {"36.ADP", 0}, {"37.ADP", 0},
+ {"38.ADP", 0}, {"39.ADP", 0},
+ {"40.ADP", 0}, {"41.ADP", 1920},
+ {"42.ADP", 1560}, {"43.ADP", 1920},
+ {"44.ADP", 1920}, {"45.ADP", 1920},
+ {"46.ADP", 1920}, {"47.ADP", 1920},
+ {"48.ADP", 1920}, {"49.ADP", 1920},
+ {"50.ADP", 1920}, {"51.ADP", 1920},
+ {"52.ADP", 1920}, {"53.ADP", 0},
+ {"54.ADP", 0}, {"55.ADP", 0},
+ {"56.ADP", 0}, {"57.ADP", 0},
+ {"58.ADP", 0}, {"59.ADP", 0}
+};
+
+
+const char *jingleFileNames[] = {
+ "S00.ADP", "S01.ADP",
+ "S02.ADP", "S03.ADP",
+ "S04.ADP", "S05.ADP",
+ "S06.ADP", "S07.ADP",
+ "S08.ADP", "S09.ADP",
+ "S10.ADP", "S11.ADP",
+ "S12.ADP", "S13.ADP",
+ "S14.ADP", "S15.ADP",
+ "S16.ADP", "S17.ADP",
+ "S18.ADP"
+};
+
+
+void ReapplyChangedHotspot() {
+ int i;
+ for (i = 0; i < GLOBALS._curChangedHotspot; i++)
+ GLOBALS._loc->getItemFromCode(GLOBALS._changedHotspot[i]._dwCode)->changeHotspot(RMPoint(GLOBALS._changedHotspot[i]._nX, GLOBALS._changedHotspot[i]._nY));
+}
+
+void SaveChangedHotspot(Common::OutSaveFile *f) {
+ f->writeByte(GLOBALS._curChangedHotspot);
+ if (GLOBALS._curChangedHotspot > 0) {
+ for (int i = 0; i < GLOBALS._curChangedHotspot; ++i)
+ GLOBALS._changedHotspot[i].save(f);
+ }
+}
+
+void LoadChangedHotspot(Common::InSaveFile *f) {
+ GLOBALS._curChangedHotspot = f->readByte();
+
+ if (GLOBALS._curChangedHotspot > 0) {
+ for (int i = 0; i < GLOBALS._curChangedHotspot; ++i)
+ GLOBALS._changedHotspot[i].load(f);
+ }
+}
+
+
+/**
+ * Classes required for custom functions
+ *
+ * Tony (To Move him) -> You can do MPAL through the animation? I really think so
+ *
+ * SendMessage -> I'd say just theEngine.SendMessage()
+ * ChangeLocation -> theEngine.ChangeLocation()
+ * AddInventory -> theEngine.AddInventory()
+*/
+
+void MCharResetCodes() {
+ for (int i = 0; i < 10; i++)
+ GLOBALS._mCharacter[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[i]._code);
+ for (int i = 0; i < 10; i++)
+ GLOBALS._character[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._character[i]._code);
+}
+
+void CharsSaveAll(Common::OutSaveFile *f) {
+ for (int i = 0; i < 10; i++) {
+ f->writeByte(GLOBALS._isMChar[i]);
+ if (GLOBALS._isMChar[i]) {
+ GLOBALS._mCharacter[i].save(f);
+ } else {
+ GLOBALS._character[i].save(f);
+ }
+ }
+}
+
+void CharsLoadAll(Common::InSaveFile *f) {
+ for (int i = 0; i < 10; i++) {
+ GLOBALS._isMChar[i] = f->readByte();
+ if (GLOBALS._isMChar[i])
+ GLOBALS._mCharacter[i].load(f);
+ else
+ GLOBALS._character[i].load(f);
+ }
+}
+
+DECLARE_CUSTOM_FUNCTION(FaceToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDDOWN);
+}
+
+DECLARE_CUSTOM_FUNCTION(BackToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDUP);
+}
+
+DECLARE_CUSTOM_FUNCTION(LeftToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDLEFT);
+}
+
+DECLARE_CUSTOM_FUNCTION(RightToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonySetPerorate)(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) {
+ g_vm->getEngine()->setPerorate(bStatus);
+}
+
+DECLARE_CUSTOM_FUNCTION(MySleep)(CORO_PARAM, uint32 dwTime, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_1(CoroScheduler.sleep, dwTime);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(SetAlwaysDisplay)(CORO_PARAM, uint32 val, uint32, uint32, uint32) {
+ GLOBALS._bAlwaysDisplay = (val != 0);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(SetPointer)(CORO_PARAM, uint32 dwPointer, uint32, uint32, uint32) {
+ switch (dwPointer) {
+ case 1:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWUP);
+ break;
+ case 2:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWDOWN);
+ break;
+ case 3:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWLEFT);
+ break;
+ case 4:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWRIGHT);
+ break;
+ case 5:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWMAP);
+ break;
+
+ default:
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_NONE);
+ break;
+ }
+}
+
+VoiceHeader *SearchVoiceHeader(uint32 codehi, uint32 codelo) {
+ int code = (codehi << 16) | codelo;
+
+ if (g_vm->_voices.size() == 0)
+ return NULL;
+
+ for (uint i = 0; i < g_vm->_voices.size(); i++) {
+ if (g_vm->_voices[i]._code == code)
+ return &g_vm->_voices[i];
+ }
+
+ return NULL;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(SendTonyMessage)(CORO_PARAM, uint32 dwMessage, uint32 nX, uint32 nY, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage msg;
+ int i;
+ int curOffset;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ RMTextDialog text;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curOffset = 0;
+
+ if (GLOBALS._bSkipIdle)
+ return;
+
+ _ctx->msg.load(dwMessage);
+ if (!_ctx->msg.isValid())
+ return;
+
+ _ctx->curVoc = SearchVoiceHeader(0, dwMessage);
+ _ctx->voice = NULL;
+ if (_ctx->curVoc) {
+ // Is positioned within the database of entries beginning at the first
+ _ctx->curOffset = _ctx->curVoc->_offset;
+
+ // First time allocation
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ g_vm->_theSound.createSfx(&_ctx->voice);
+
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+
+ _ctx->voice->setLoop(false);
+ }
+
+ if (GLOBALS._nTonyNextTalkType != GLOBALS._tony->TALK_NORMAL) {
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType);
+
+ if (!GLOBALS._bStaticTalk)
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+ } else {
+ if (_ctx->msg.numPeriods() > 1)
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_HIPS);
+ else
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_NORMAL);
+ }
+
+ if (GLOBALS._curBackText)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+
+ GLOBALS._bTonyIsSpeaking = true;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg.numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ _ctx->text.setInput(GLOBALS._input);
+
+ // Alignment
+ _ctx->text.setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+
+ // Color
+ _ctx->text.setColor(0, 255, 0);
+
+ // Writes the text
+ _ctx->text.writeText(_ctx->msg[_ctx->i], 0);
+
+ // Set the position
+ if (nX == 0 && nY == 0)
+ _ctx->text.setPosition(GLOBALS._tony->position() - RMPoint(0, 130) - GLOBALS._loc->scrollPosition());
+ else
+ _ctx->text.setPosition(RMPoint(nX, nY) - GLOBALS._loc->scrollPosition());
+
+ // Handling for always display
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text.setAlwaysDisplay();
+ _ctx->text.forceTime();
+ }
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(&_ctx->text);
+
+ if (_ctx->curVoc) {
+ if (_ctx->i == 0) {
+ _ctx->voice->play();
+ _ctx->text.setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ } else {
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+ _ctx->voice->setLoop(false);
+ _ctx->voice->play();
+ _ctx->text.setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ }
+ }
+
+ // Wait for the end of the display
+ _ctx->text.setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text.waitForEndDisplay);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+ }
+
+ GLOBALS._bTonyIsSpeaking = false;
+ if (GLOBALS._curBackText)
+ GLOBALS._curBackText->show();
+
+ CORO_INVOKE_0(GLOBALS._tony->endTalk);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(ChangeBoxStatus)(CORO_PARAM, uint32 nLoc, uint32 nBox, uint32 nStatus, uint32) {
+ GLOBALS._boxes->changeBoxStatus(nLoc, nBox, nStatus);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(CustLoadLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._curChangedHotspot = 0;
+ if (bUseStartPos != 0)
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), GLOBALS._startLocPos[nLoc]);
+ else
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), RMPoint(-1, -1));
+
+ _ctx->h = mpalQueryDoAction(0, nLoc, 0);
+
+ // On Enter?
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(SendFullscreenMsgStart)(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ RMGfxClearTask clear;
+ int i;
+ RMTextDialog text;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(nMsg);
+
+ GLOBALS._fullScreenMessageLoc = GLOBALS._loc->TEMPGetNumLoc();
+ GLOBALS._fullScreenMessagePt = GLOBALS._tony->position();
+
+ if (GLOBALS._bSkipIdle)
+ return;
+
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, NULL);
+ GLOBALS._tony->hide();
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ _ctx->text.setInput(GLOBALS._input);
+
+ // Alignment
+ _ctx->text.setAlignType(RMText::HCENTER, RMText::VCENTER);
+
+ // Forces the text to disappear in time
+ _ctx->text.forceTime();
+
+ // Color
+ _ctx->text.setColor(255, 255, 255);
+
+ // Write the text
+ if (nFont == 0)
+ _ctx->text.writeText((*_ctx->msg)[_ctx->i], 1);
+ else if (nFont == 1)
+ _ctx->text.writeText((*_ctx->msg)[_ctx->i], 0);
+
+ // Set the position
+ _ctx->text.setPosition(RMPoint(320, 240));
+
+ _ctx->text.setAlwaysDisplay();
+ _ctx->text.forceTime();
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(&_ctx->clear);
+ g_vm->getEngine()->linkGraphicTask(&_ctx->text);
+
+ // Wait for the end of display
+ _ctx->text.setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text.waitForEndDisplay);
+ }
+
+ delete _ctx->msg;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(ClearScreen)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ char buf[256];
+ RMGfxClearTask clear;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ g_vm->getEngine()->linkGraphicTask(&_ctx->clear);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ // WORKAROUND: This fixes a bug in the original source where the linked clear task
+ // didn't have time to be drawn and removed from the draw list before the method
+ // ended, thus remaining in the draw list and causing a later crash
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(SendFullscreenMsgEnd)(CORO_PARAM, uint32 bNotEnableTony, uint32, uint32, uint32) {
+ g_vm->getEngine()->loadLocation(GLOBALS._fullScreenMessageLoc, RMPoint(GLOBALS._fullScreenMessagePt._x, GLOBALS._fullScreenMessagePt._y), RMPoint(-1, -1));
+ if (!bNotEnableTony)
+ GLOBALS._tony->show();
+
+ MCharResetCodes();
+ ReapplyChangedHotspot();
+}
+
+
+DECLARE_CUSTOM_FUNCTION(SendFullscreenMessage)(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(SendFullscreenMsgStart, nMsg, nFont, 0, 0);
+ CORO_INVOKE_4(SendFullscreenMsgEnd, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(NoBullsEye)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bNoBullsEye = true;
+}
+
+DECLARE_CUSTOM_FUNCTION(CloseLocation)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!GLOBALS._bNoBullsEye) {
+ g_vm->getEngine()->initWipe(1);
+ CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd);
+ }
+
+ g_vm->stopMusic(4);
+
+ // On exit, unload
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, true, NULL);
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(ChangeLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!GLOBALS._bNoBullsEye) {
+ g_vm->getEngine()->initWipe(1);
+ CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd);
+ }
+
+ if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) {
+ g_vm->stopMusic(4);
+ }
+
+ // On exit, unfreeze
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, true, NULL);
+
+ GLOBALS._curChangedHotspot = 0;
+ if (bUseStartPos != 0)
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), GLOBALS._startLocPos[nLoc]);
+ else
+ g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), RMPoint(-1, -1));
+
+ if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) {
+ GLOBALS._lastTappeto = GLOBALS._ambiance[nLoc];
+ if (GLOBALS._lastTappeto != 0)
+ g_vm->playMusic(4, ambianceFile[GLOBALS._lastTappeto], 0, true, 2000);
+ }
+
+ if (!GLOBALS._bNoBullsEye) {
+ g_vm->getEngine()->initWipe(2);
+ }
+
+ _ctx->h = mpalQueryDoAction(0, nLoc, 0);
+
+ if (!GLOBALS._bNoBullsEye) {
+ CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd);
+ g_vm->getEngine()->closeWipe();
+ }
+
+ GLOBALS._bNoBullsEye = false;
+
+ // On Enter?
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(SetLocStartPosition)(CORO_PARAM, uint32 nLoc, uint32 lX, uint32 lY, uint32) {
+ GLOBALS._startLocPos[nLoc].set(lX, lY);
+}
+
+DECLARE_CUSTOM_FUNCTION(SaveTonyPosition)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._saveTonyPos = GLOBALS._tony->position();
+ GLOBALS._saveTonyLoc = GLOBALS._loc->TEMPGetNumLoc();
+}
+
+DECLARE_CUSTOM_FUNCTION(RestoreTonyPosition)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(ChangeLocation, GLOBALS._saveTonyLoc, GLOBALS._saveTonyPos._x, GLOBALS._saveTonyPos._y, 0);
+
+ MCharResetCodes();
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(DisableInput)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->getEngine()->disableInput();
+}
+
+DECLARE_CUSTOM_FUNCTION(EnableInput)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->getEngine()->enableInput();
+}
+
+DECLARE_CUSTOM_FUNCTION(StopTony)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->stopNoAction(coroParam);
+}
+
+DECLARE_CUSTOM_FUNCTION(CustEnableGUI)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS.EnableGUI();
+}
+
+DECLARE_CUSTOM_FUNCTION(CustDisableGUI)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS.DisableGUI();
+}
+
+
+
+void TonyGenericTake1(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->take(nDirection, 0);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ CORO_END_CODE;
+}
+
+void TonyGenericTake2(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->take(nDirection, 1);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ GLOBALS._tony->take(nDirection, 2);
+
+ CORO_END_CODE;
+}
+
+void TonyGenericPut1(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->put(nDirection, 0);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ CORO_END_CODE;
+}
+
+void TonyGenericPut2(CORO_PARAM, uint32 nDirection) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->put(nDirection, 1);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ GLOBALS._tony->put(nDirection, 2);
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyTakeUp1)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericTake1(coroParam, 0);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyTakeMid1)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericTake1(coroParam, 1);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyTakeDown1)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericTake1(coroParam, 2);
+}
+
+
+
+DECLARE_CUSTOM_FUNCTION(TonyTakeUp2)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericTake2(coroParam, 0);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyTakeMid2)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericTake2(coroParam, 1);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyTakeDown2)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericTake2(coroParam, 2);
+}
+
+
+
+DECLARE_CUSTOM_FUNCTION(TonyPutUp1)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericPut1(coroParam, 0);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyPutMid1)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericPut1(coroParam, 1);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyPutDown1)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericPut1(coroParam, 2);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyPutUp2)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericPut2(coroParam, 0);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyPutMid2)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericPut2(coroParam, 1);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyPutDown2)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ TonyGenericPut2(coroParam, 2);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyOnTheFloor)(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) {
+ if (dwParte == 0)
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORLEFT);
+ else
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORRIGHT);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyGetUp)(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (dwParte == 0)
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_GETUPLEFT);
+ else
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_GETUPRIGHT);
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyShepherdess)(CORO_PARAM, uint32 bIsPast, uint32, uint32, uint32) {
+ GLOBALS._tony->setShepherdess(bIsPast);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWhistle)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WHISTLERIGHT);
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT);
+
+ CORO_END_CODE;
+}
+
+
+void TonySetNumTexts(uint32 dwText) {
+ GLOBALS._dwTonyNumTexts = dwText;
+ GLOBALS._bTonyInTexts = false;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyLaugh)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyGiggle)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH2;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyHips)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_HIPS;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonySing)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SING;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyIndicate)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_INDICATE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyScaredWithHands)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyScaredWithoutHands)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED2;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithHammer)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHHAMMER;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHHAMMER);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithGlasses)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHGLASSES;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHGLASSES);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithWorm)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHWORM;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHWORM);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRope)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHROPE;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHROPE);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithSecretary)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSECRETARY;
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHSECRETARY);
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRabbitANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBIT;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRecipeANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithCardsANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDS;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMAN;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMANSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHSNOWMANSTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHSNOWMANSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRabbitStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBITSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHRABBITSTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRabbitEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHRABBITSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRecipeStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPESTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHRECIPESTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithRecipeEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHRECIPESTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithCardsStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDSSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHCARDSSTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithCardsEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHCARDSSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithNotebookStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITH_NOTEBOOK;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITH_NOTEBOOK);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithNotebookEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITH_NOTEBOOK);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithMegaphoneStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHMEGAPHONESTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHMEGAPHONESTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithMegaphoneEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHMEGAPHONESTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithBeardStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHBEARDSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHBEARDSTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyWithBeardEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHBEARDSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyScaredStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCAREDSTATIC;
+ GLOBALS._bStaticTalk = true;
+ CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_SCAREDSTATIC);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyScaredEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_SCAREDSTATIC);
+ GLOBALS._bStaticTalk = false;
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(TonyDisgusted)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_DISGUSTED;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonySniffLeft)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_LEFT);
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+ CORO_INVOKE_4(LeftToMe, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonySniffRight)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_RIGHT);
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern);
+ CORO_INVOKE_4(RightToMe, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonySarcastic)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) {
+ TonySetNumTexts(dwText);
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SARCASTIC;
+}
+
+DECLARE_CUSTOM_FUNCTION(TonyMacbeth)(CORO_PARAM, uint32 nPos, uint32, uint32, uint32) {
+ switch (nPos) {
+ case 1:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH1;
+ break;
+ case 2:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH2;
+ break;
+ case 3:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH3;
+ break;
+ case 4:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH4;
+ break;
+ case 5:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH5;
+ break;
+ case 6:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH6;
+ break;
+ case 7:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH7;
+ break;
+ case 8:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH8;
+ break;
+ case 9:
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH9;
+ break;
+ }
+}
+
+
+DECLARE_CUSTOM_FUNCTION(EnableTony)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._tony->show();
+}
+
+DECLARE_CUSTOM_FUNCTION(DisableTony)(CORO_PARAM, uint32 bShowShadow, uint32, uint32, uint32) {
+ GLOBALS._tony->hide(bShowShadow);
+}
+
+DECLARE_CUSTOM_FUNCTION(WaitForPatternEnd)(CORO_PARAM, uint32 nItem, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMItem *item;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->item = GLOBALS._loc->getItemFromCode(nItem);
+
+ if (!GLOBALS._bSkipIdle && _ctx->item != NULL)
+ CORO_INVOKE_1(_ctx->item->waitForEndPattern, GLOBALS._hSkipIdle);
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(SetTonyPosition)(CORO_PARAM, uint32 nX, uint32 nY, uint32 nLoc, uint32) {
+ GLOBALS._tony->setPosition(RMPoint(nX, nY), nLoc);
+}
+
+DECLARE_CUSTOM_FUNCTION(MoveTonyAndWait)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // WORKAROUND: Delay for a frame before starting the move to give any previous move time to finish.
+ // This fixes a bug in the first scene where if you immediately 'Use Door', Tony moves to the door,
+ // and then floats to the right rather than properly walking.
+ CORO_SLEEP(1);
+
+ CORO_INVOKE_1(GLOBALS._tony->move, RMPoint(nX, nY));
+
+ if (!GLOBALS._bSkipIdle)
+ CORO_INVOKE_0(GLOBALS._tony->waitForEndMovement);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(MoveTony)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) {
+ GLOBALS._tony->move(coroParam, RMPoint(nX, nY));
+}
+
+DECLARE_CUSTOM_FUNCTION(ScrollLocation)(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) {
+ CORO_BEGIN_CONTEXT;
+ int lx, ly;
+ RMPoint pt;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Take the scroll coordinates
+ _ctx->lx = (int32)nX;
+ _ctx->ly = (int32)nY;
+
+ _ctx->pt = GLOBALS._loc->scrollPosition();
+
+ while ((_ctx->lx != 0 || _ctx->ly != 0) && !GLOBALS._bSkipIdle) {
+ if (_ctx->lx > 0) {
+ _ctx->lx -= (int32)sX;
+ if (_ctx->lx < 0)
+ _ctx->lx = 0;
+ _ctx->pt.offset((int32)sX, 0);
+ } else if (_ctx->lx < 0) {
+ _ctx->lx += (int32)sX;
+ if (_ctx->lx > 0)
+ _ctx->lx = 0;
+ _ctx->pt.offset(-(int32)sX, 0);
+ }
+
+ if (_ctx->ly > 0) {
+ _ctx->ly -= sY;
+ if (_ctx->ly < 0)
+ _ctx->ly = 0;
+ _ctx->pt.offset(0, sY);
+ } else if (_ctx->ly < 0) {
+ _ctx->ly += sY;
+ if (_ctx->ly > 0)
+ _ctx->ly = 0;
+ _ctx->pt.offset(0, -(int32)sY);
+ }
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ GLOBALS._loc->setScrollPosition(_ctx->pt);
+ GLOBALS._tony->setScrollPosition(_ctx->pt);
+ }
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(SyncScrollLocation)(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) {
+ CORO_BEGIN_CONTEXT;
+ int lx, ly;
+ RMPoint pt, startpt;
+ uint32 dwStartTime, dwCurTime, dwTotalTime;
+ uint32 stepX, stepY;
+ int dimx, dimy;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Take the scroll coordinates
+ _ctx->lx = (int32)nX;
+ _ctx->ly = (int32)nY;
+ _ctx->dimx = _ctx->lx;
+ _ctx->dimy = _ctx->ly;
+ if (_ctx->lx < 0)
+ _ctx->dimx = -_ctx->lx;
+
+ if (_ctx->ly < 0)
+ _ctx->dimy = -_ctx->ly;
+
+ _ctx->stepX = sX;
+ _ctx->stepY = sY;
+
+ _ctx->startpt = GLOBALS._loc->scrollPosition();
+
+ _ctx->dwStartTime = g_vm->getTime();
+
+ if (sX)
+ _ctx->dwTotalTime = _ctx->dimx * (1000 / 35) / sX;
+ else
+ _ctx->dwTotalTime = _ctx->dimy * (1000 / 35) / sY;
+
+ while ((_ctx->lx != 0 || _ctx->ly != 0) && !GLOBALS._bSkipIdle) {
+ _ctx->dwCurTime = g_vm->getTime() - _ctx->dwStartTime;
+ if (_ctx->dwCurTime > _ctx->dwTotalTime)
+ break;
+
+ _ctx->pt = _ctx->startpt;
+
+ if (sX) {
+ if (_ctx->lx > 0)
+ _ctx->pt._x += (_ctx->dimx * _ctx->dwCurTime) / _ctx->dwTotalTime;
+ else
+ _ctx->pt._x -= (_ctx->dimx * _ctx->dwCurTime) / _ctx->dwTotalTime;
+ } else {
+ if (_ctx->ly > 0)
+ _ctx->pt._y += (_ctx->dimy * _ctx->dwCurTime) / _ctx->dwTotalTime;
+ else
+ _ctx->pt._y -= (_ctx->dimy * _ctx->dwCurTime) / _ctx->dwTotalTime;
+
+ }
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ GLOBALS._loc->setScrollPosition(_ctx->pt);
+ GLOBALS._tony->setScrollPosition(_ctx->pt);
+
+ }
+
+
+ // Set the position finale
+ if (sX) {
+ if (_ctx->lx > 0)
+ _ctx->pt._x = _ctx->startpt._x + _ctx->dimx;
+ else
+ _ctx->pt._x = _ctx->startpt._x - _ctx->dimx;
+ } else {
+ if (_ctx->ly > 0)
+ _ctx->pt._y = _ctx->startpt._y + _ctx->dimy;
+ else
+ _ctx->pt._y = _ctx->startpt._y - _ctx->dimy;
+ }
+
+ GLOBALS._loc->setScrollPosition(_ctx->pt);
+ GLOBALS._tony->setScrollPosition(_ctx->pt);
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(ChangeHotspot)(CORO_PARAM, uint32 dwCode, uint32 nX, uint32 nY, uint32) {
+ int i;
+
+ for (i = 0; i < GLOBALS._curChangedHotspot; i++) {
+ if (GLOBALS._changedHotspot[i]._dwCode == dwCode) {
+ GLOBALS._changedHotspot[i]._nX = nX;
+ GLOBALS._changedHotspot[i]._nY = nY;
+ break;
+ }
+ }
+
+ if (i == GLOBALS._curChangedHotspot) {
+ GLOBALS._changedHotspot[i]._dwCode = dwCode;
+ GLOBALS._changedHotspot[i]._nX = nX;
+ GLOBALS._changedHotspot[i]._nY = nY;
+ GLOBALS._curChangedHotspot++;
+ }
+
+ GLOBALS._loc->getItemFromCode(dwCode)->changeHotspot(RMPoint(nX, nY));
+}
+
+
+DECLARE_CUSTOM_FUNCTION(AutoSave)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->autoSave(coroParam);
+}
+
+DECLARE_CUSTOM_FUNCTION(AbortGame)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->abortGame();
+}
+
+DECLARE_CUSTOM_FUNCTION(ShakeScreen)(CORO_PARAM, uint32 nScosse, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ uint32 i;
+ uint32 curTime;
+ int dirx, diry;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curTime = g_vm->getTime();
+
+ _ctx->dirx = 1;
+ _ctx->diry = 1;
+
+ while (g_vm->getTime() < _ctx->curTime + nScosse) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ GLOBALS._loc->setFixedScroll(RMPoint(1 * _ctx->dirx, 1 * _ctx->diry));
+ GLOBALS._tony->setFixedScroll(RMPoint(1 * _ctx->dirx, 1 * _ctx->diry));
+
+ _ctx->i = g_vm->_randomSource.getRandomNumber(2);
+
+ if (_ctx->i == 0 || _ctx->i == 2)
+ _ctx->dirx = -_ctx->dirx;
+ else if (_ctx->i == 1 || _ctx->i == 2)
+ _ctx->diry = -_ctx->diry;
+ }
+
+ GLOBALS._loc->setFixedScroll(RMPoint(0, 0));
+ GLOBALS._tony->setFixedScroll(RMPoint(0, 0));
+
+ CORO_END_CODE;
+}
+
+/*
+ * Characters
+ */
+
+DECLARE_CUSTOM_FUNCTION(CharSetCode)(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._code = nCode;
+ GLOBALS._character[nChar]._item = GLOBALS._loc->getItemFromCode(nCode);
+ GLOBALS._character[nChar]._r = 255;
+ GLOBALS._character[nChar]._g = 255;
+ GLOBALS._character[nChar]._b = 255;
+ GLOBALS._character[nChar]._talkPattern = 0;
+ GLOBALS._character[nChar]._startTalkPattern = 0;
+ GLOBALS._character[nChar]._endTalkPattern = 0;
+ GLOBALS._character[nChar]._standPattern = 0;
+
+ GLOBALS._isMChar[nChar] = false;
+}
+
+DECLARE_CUSTOM_FUNCTION(CharSetColor)(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._r = r;
+ GLOBALS._character[nChar]._g = g;
+ GLOBALS._character[nChar]._b = b;
+}
+
+DECLARE_CUSTOM_FUNCTION(CharSetTalkPattern)(CORO_PARAM, uint32 nChar, uint32 tp, uint32 sp, uint32) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._talkPattern = tp;
+ GLOBALS._character[nChar]._standPattern = sp;
+}
+
+DECLARE_CUSTOM_FUNCTION(CharSetStartEndTalkPattern)(CORO_PARAM, uint32 nChar, uint32 sp, uint32 ep, uint32) {
+ assert(nChar < 16);
+ GLOBALS._character[nChar]._startTalkPattern = sp;
+ GLOBALS._character[nChar]._endTalkPattern = ep;
+}
+
+DECLARE_CUSTOM_FUNCTION(CharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ int i;
+ RMPoint pt;
+ RMTextDialog *text;
+ int curOffset;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(dwMessage);
+ _ctx->curOffset = 0;
+
+ assert(nChar < 16);
+ _ctx->pt = GLOBALS._character[nChar]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+
+ if (GLOBALS._character[nChar]._startTalkPattern != 0) {
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._startTalkPattern);
+
+ CORO_INVOKE_0(GLOBALS._character[nChar]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._talkPattern);
+
+ _ctx->curVoc = SearchVoiceHeader(0, dwMessage);
+ _ctx->voice = NULL;
+ if (_ctx->curVoc) {
+ // Position within the database of entries, beginning at the first
+ g_vm->_vdbFP.seek(_ctx->curVoc->_offset);
+ _ctx->curOffset = _ctx->curVoc->_offset;
+ }
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ if (bIsBack) {
+ GLOBALS._curBackText = _ctx->text = new RMTextDialogScrolling(GLOBALS._loc);
+ if (GLOBALS._bTonyIsSpeaking)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+ } else
+ _ctx->text = new RMTextDialog;
+
+ _ctx->text->setInput(GLOBALS._input);
+
+ // Skipping
+ _ctx->text->setSkipStatus(!bIsBack);
+
+ // Alignment
+ _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+
+ // Color
+ _ctx->text->setColor(GLOBALS._character[nChar]._r, GLOBALS._character[nChar]._g, GLOBALS._character[nChar]._b);
+
+ // Write the text
+ _ctx->text->writeText((*_ctx->msg)[_ctx->i], 0);
+
+ // Set the position
+ _ctx->text->setPosition(_ctx->pt);
+
+ // Set the always display
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text->setAlwaysDisplay();
+ _ctx->text->forceTime();
+ }
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(_ctx->text);
+
+ if (_ctx->curVoc) {
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->voice->setLoop(false);
+ if (bIsBack)
+ _ctx->voice->setVolume(55);
+ _ctx->voice->play();
+ _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+ }
+
+ // Wait for the end of display
+ _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text->waitForEndDisplay);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+
+
+ GLOBALS._curBackText = NULL;
+ delete _ctx->text;
+ }
+
+ if (GLOBALS._character[nChar]._endTalkPattern != 0) {
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._endTalkPattern);
+ CORO_INVOKE_0(GLOBALS._character[nChar]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._standPattern);
+ delete _ctx->msg;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(AddInventory)(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) {
+ GLOBALS._inventory->addItem(dwCode);
+}
+
+DECLARE_CUSTOM_FUNCTION(RemoveInventory)(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) {
+ GLOBALS._inventory->removeItem(dwCode);
+}
+
+DECLARE_CUSTOM_FUNCTION(ChangeInventoryStatus)(CORO_PARAM, uint32 dwCode, uint32 dwStatus, uint32, uint32) {
+ GLOBALS._inventory->changeItemStatus(dwCode, dwStatus);
+}
+
+
+/*
+ * Master Characters
+ */
+
+DECLARE_CUSTOM_FUNCTION(MCharSetCode)(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) {
+ assert(nChar < 10);
+ GLOBALS._mCharacter[nChar]._code = nCode;
+ if (nCode == 0)
+ GLOBALS._mCharacter[nChar]._item = NULL;
+ else
+ GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(nCode);
+ GLOBALS._mCharacter[nChar]._r = 255;
+ GLOBALS._mCharacter[nChar]._g = 255;
+ GLOBALS._mCharacter[nChar]._b = 255;
+ GLOBALS._mCharacter[nChar]._x = -1;
+ GLOBALS._mCharacter[nChar]._y = -1;
+ GLOBALS._mCharacter[nChar]._bAlwaysBack = 0;
+
+ for (int i = 0; i < 10; i++)
+ GLOBALS._mCharacter[nChar]._numTalks[i] = 1;
+
+ GLOBALS._mCharacter[nChar]._curGroup = 0;
+
+ GLOBALS._isMChar[nChar] = true;
+}
+
+DECLARE_CUSTOM_FUNCTION(MCharResetCode)(CORO_PARAM, uint32 nChar, uint32, uint32, uint32) {
+ GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[nChar]._code);
+}
+
+
+DECLARE_CUSTOM_FUNCTION(MCharSetPosition)(CORO_PARAM, uint32 nChar, uint32 nX, uint32 nY, uint32) {
+ assert(nChar < 10);
+ GLOBALS._mCharacter[nChar]._x = nX;
+ GLOBALS._mCharacter[nChar]._y = nY;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(MCharSetColor)(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) {
+ assert(nChar < 10);
+ GLOBALS._mCharacter[nChar]._r = r;
+ GLOBALS._mCharacter[nChar]._g = g;
+ GLOBALS._mCharacter[nChar]._b = b;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(MCharSetNumTalksInGroup)(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32 nTalks, uint32) {
+ assert(nChar < 10);
+ assert(nGroup < 10);
+
+ GLOBALS._mCharacter[nChar]._numTalks[nGroup] = nTalks;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(MCharSetCurrentGroup)(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32, uint32) {
+ assert(nChar < 10);
+ assert(nGroup < 10);
+
+ GLOBALS._mCharacter[nChar]._curGroup = nGroup;
+}
+
+DECLARE_CUSTOM_FUNCTION(MCharSetNumTexts)(CORO_PARAM, uint32 nChar, uint32 nTexts, uint32, uint32) {
+ assert(nChar < 10);
+
+ GLOBALS._mCharacter[nChar]._numTexts = nTexts - 1;
+ GLOBALS._mCharacter[nChar]._bInTexts = false;
+}
+
+DECLARE_CUSTOM_FUNCTION(MCharSetAlwaysBack)(CORO_PARAM, uint32 nChar, uint32 bAlwaysBack, uint32, uint32) {
+ assert(nChar < 10);
+
+ GLOBALS._mCharacter[nChar]._bAlwaysBack = bAlwaysBack;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(MCharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32 nFont) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ int i;
+ int parm;
+ RMPoint pt;
+ uint32 h;
+ RMTextDialog *text;
+ int curOffset;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(dwMessage);
+ _ctx->curOffset = 0;
+
+ assert(nChar < 10);
+
+ bIsBack |= GLOBALS._mCharacter[nChar]._bAlwaysBack ? 1 : 0;
+
+ // Calculates the position of the text according to the current frame
+ if (GLOBALS._mCharacter[nChar]._x == -1)
+ _ctx->pt = GLOBALS._mCharacter[nChar]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+ else
+ _ctx->pt = RMPoint(GLOBALS._mCharacter[nChar]._x, GLOBALS._mCharacter[nChar]._y);
+
+ // Parameter for special actions: random between the spoken
+ _ctx->parm = (GLOBALS._mCharacter[nChar]._curGroup * 10) + g_vm->_randomSource.getRandomNumber(
+ GLOBALS._mCharacter[nChar]._numTalks[GLOBALS._mCharacter[nChar]._curGroup] - 1) + 1;
+
+ // Try to run the custom function to initialize the speech
+ if (GLOBALS._mCharacter[nChar]._item) {
+ _ctx->h = mpalQueryDoAction(30, GLOBALS._mCharacter[nChar]._item->mpalCode(), _ctx->parm);
+ if (_ctx->h != CORO_INVALID_PID_VALUE) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+ }
+ }
+
+ _ctx->curVoc = SearchVoiceHeader(0, dwMessage);
+ _ctx->voice = NULL;
+ if (_ctx->curVoc) {
+ // Position within the database of entries, beginning at the first
+ // fseek(g_vm->m_vdbFP, curVoc->offset, SEEK_SET);
+ g_vm->_vdbFP.seek(_ctx->curVoc->_offset);
+ _ctx->curOffset = _ctx->curVoc->_offset;
+ }
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) {
+ // Create a different object depending on whether it's background or not
+ if (bIsBack) {
+ GLOBALS._curBackText = _ctx->text = new RMTextDialogScrolling(GLOBALS._loc);
+ if (GLOBALS._bTonyIsSpeaking)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+ } else
+ _ctx->text = new RMTextDialog;
+
+ _ctx->text->setInput(GLOBALS._input);
+
+ // Skipping
+ _ctx->text->setSkipStatus(!bIsBack);
+
+ // Alignment
+ _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+
+ // Color
+ _ctx->text->setColor(GLOBALS._mCharacter[nChar]._r, GLOBALS._mCharacter[nChar]._g, GLOBALS._mCharacter[nChar]._b);
+
+ // Write the text
+ _ctx->text->writeText((*_ctx->msg)[_ctx->i], nFont);
+
+ // Set the position
+ _ctx->text->setPosition(_ctx->pt);
+
+ // Set the always display
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text->setAlwaysDisplay();
+ _ctx->text->forceTime();
+ }
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(_ctx->text);
+
+ if (_ctx->curVoc) {
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ g_vm->_vdbFP.seek(_ctx->curOffset);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->voice->setLoop(false);
+ if (bIsBack)
+ _ctx->voice->setVolume(55);
+ _ctx->voice->play();
+ _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ _ctx->curOffset = g_vm->_vdbFP.pos();
+ }
+
+ // Wait for the end of display
+ _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text->waitForEndDisplay);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+
+ GLOBALS._curBackText = NULL;
+ delete _ctx->text;
+ }
+
+ delete _ctx->msg;
+
+ // Try to run the custom function to close the speech
+ if (GLOBALS._mCharacter[nChar]._item) {
+ _ctx->h = mpalQueryDoAction(31, GLOBALS._mCharacter[nChar]._item->mpalCode(), _ctx->parm);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+ }
+
+ CORO_END_CODE;
+}
+
+/*
+ * Dialogs
+ */
+
+int g_curDialog;
+
+DECLARE_CUSTOM_FUNCTION(SendDialogMessage)(CORO_PARAM, uint32 nPers, uint32 nMsg, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ char *string;
+ RMTextDialog *text;
+ int parm;
+ uint32 h;
+ bool bIsBack;
+ VoiceHeader *curVoc;
+ FPSfx *voice;
+ RMPoint pt;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bIsBack = false;
+
+ // The SendDialogMessage can go in the background if it is a character
+ if (nPers != 0 && GLOBALS._isMChar[nPers] && GLOBALS._mCharacter[nPers]._bAlwaysBack)
+ _ctx->bIsBack = true;
+
+ _ctx->curVoc = SearchVoiceHeader(g_curDialog, nMsg);
+ _ctx->voice = NULL;
+
+ if (_ctx->curVoc) {
+ // Position within the database of entries, beginning at the first
+ g_vm->_vdbFP.seek(_ctx->curVoc->_offset);
+ g_vm->_theSound.createSfx(&_ctx->voice);
+ _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP);
+ _ctx->voice->setLoop(false);
+ if (_ctx->bIsBack)
+ _ctx->voice->setVolume(55);
+ }
+
+ _ctx->string = mpalQueryDialogPeriod(nMsg);
+
+ if (nPers == 0) {
+ _ctx->text = new RMTextDialog;
+ _ctx->text->setColor(0, 255, 0);
+ _ctx->text->setPosition(GLOBALS._tony->position() - RMPoint(0, 130) - GLOBALS._loc->scrollPosition());
+ _ctx->text->writeText(_ctx->string, 0);
+
+ if (GLOBALS._dwTonyNumTexts > 0) {
+ if (!GLOBALS._bTonyInTexts) {
+ if (GLOBALS._nTonyNextTalkType != GLOBALS._tony->TALK_NORMAL) {
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType);
+ if (!GLOBALS._bStaticTalk)
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+ } else
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_NORMAL);
+
+ GLOBALS._bTonyInTexts = true;
+ }
+ GLOBALS._dwTonyNumTexts--;
+ } else {
+ CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType);
+ if (!GLOBALS._bStaticTalk)
+ GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL;
+ }
+ } else if (!GLOBALS._isMChar[nPers]) {
+ _ctx->text = new RMTextDialog;
+
+ _ctx->pt = GLOBALS._character[nPers]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+
+ if (GLOBALS._character[nPers]._startTalkPattern != 0) {
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._startTalkPattern);
+ CORO_INVOKE_0(GLOBALS._character[nPers]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._talkPattern);
+
+ _ctx->text->setColor(GLOBALS._character[nPers]._r, GLOBALS._character[nPers]._g, GLOBALS._character[nPers]._b);
+ _ctx->text->writeText(_ctx->string, 0);
+ _ctx->text->setPosition(_ctx->pt);
+ } else {
+ if (GLOBALS._mCharacter[nPers]._x == -1)
+ _ctx->pt = GLOBALS._mCharacter[nPers]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition();
+ else
+ _ctx->pt = RMPoint(GLOBALS._mCharacter[nPers]._x, GLOBALS._mCharacter[nPers]._y);
+
+ // Parameter for special actions. Random between the spoken.
+ _ctx->parm = (GLOBALS._mCharacter[nPers]._curGroup * 10) + g_vm->_randomSource.getRandomNumber(
+ GLOBALS._mCharacter[nPers]._numTalks[GLOBALS._mCharacter[nPers]._curGroup] - 1) + 1;
+
+ if (GLOBALS._mCharacter[nPers]._numTexts != 0 && GLOBALS._mCharacter[nPers]._bInTexts) {
+ GLOBALS._mCharacter[nPers]._numTexts--;
+ } else {
+ // Try to run the custom function to initialize the speech
+ _ctx->h = mpalQueryDoAction(30, GLOBALS._mCharacter[nPers]._item->mpalCode(), _ctx->parm);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ GLOBALS._mCharacter[nPers]._curTalk = _ctx->parm;
+
+ if (GLOBALS._mCharacter[nPers]._numTexts != 0) {
+ GLOBALS._mCharacter[nPers]._bInTexts = true;
+ GLOBALS._mCharacter[nPers]._numTexts--;
+ }
+ }
+
+ if (GLOBALS._mCharacter[nPers]._bAlwaysBack) {
+ _ctx->text = GLOBALS._curBackText = new RMTextDialogScrolling(GLOBALS._loc);
+ if (GLOBALS._bTonyIsSpeaking)
+ CORO_INVOKE_0(GLOBALS._curBackText->hide);
+
+ _ctx->bIsBack = true;
+ } else
+ _ctx->text = new RMTextDialog;
+
+ _ctx->text->setSkipStatus(!GLOBALS._mCharacter[nPers]._bAlwaysBack);
+ _ctx->text->setColor(GLOBALS._mCharacter[nPers]._r, GLOBALS._mCharacter[nPers]._g, GLOBALS._mCharacter[nPers]._b);
+ _ctx->text->writeText(_ctx->string, 0);
+ _ctx->text->setPosition(_ctx->pt);
+ }
+
+ if (!GLOBALS._bSkipIdle) {
+ _ctx->text->setInput(GLOBALS._input);
+ if (GLOBALS._bAlwaysDisplay) {
+ _ctx->text->setAlwaysDisplay();
+ _ctx->text->forceTime();
+ }
+ _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM);
+ g_vm->getEngine()->linkGraphicTask(_ctx->text);
+
+ if (_ctx->curVoc) {
+ _ctx->voice->play();
+ _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer);
+ }
+
+ // Wait for the end of display
+ _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle);
+ CORO_INVOKE_0(_ctx->text->waitForEndDisplay);
+ }
+
+ if (_ctx->curVoc) {
+ _ctx->voice->stop();
+ _ctx->voice->release();
+ _ctx->voice = NULL;
+ }
+
+ if (nPers != 0) {
+ if (!GLOBALS._isMChar[nPers]) {
+ if (GLOBALS._character[nPers]._endTalkPattern != 0) {
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._endTalkPattern);
+ CORO_INVOKE_0(GLOBALS._character[nPers]._item->waitForEndPattern);
+ }
+
+ GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._standPattern);
+ delete _ctx->text;
+ } else {
+ if ((GLOBALS._mCharacter[nPers]._bInTexts && GLOBALS._mCharacter[nPers]._numTexts == 0) || !GLOBALS._mCharacter[nPers]._bInTexts) {
+ // Try to run the custom function to close the speech
+ GLOBALS._mCharacter[nPers]._curTalk = (GLOBALS._mCharacter[nPers]._curTalk % 10) + GLOBALS._mCharacter[nPers]._curGroup * 10;
+ _ctx->h = mpalQueryDoAction(31, GLOBALS._mCharacter[nPers]._item->mpalCode(), GLOBALS._mCharacter[nPers]._curTalk);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+
+ GLOBALS._mCharacter[nPers]._bInTexts = false;
+ GLOBALS._mCharacter[nPers]._numTexts = 0;
+ }
+
+ GLOBALS._curBackText = NULL;
+ delete _ctx->text;
+ }
+ } else {
+ if ((GLOBALS._dwTonyNumTexts == 0 && GLOBALS._bTonyInTexts) || !GLOBALS._bTonyInTexts) {
+ CORO_INVOKE_0(GLOBALS._tony->endTalk);
+ GLOBALS._dwTonyNumTexts = 0;
+ GLOBALS._bTonyInTexts = false;
+ }
+
+ delete _ctx->text;
+ }
+
+ globalDestroy(_ctx->string);
+
+ CORO_END_CODE;
+}
+
+
+// @@@@ This cannot be skipped!!!!!!!!!!!!!!!!!!!
+
+DECLARE_CUSTOM_FUNCTION(StartDialog)(CORO_PARAM, uint32 nDialog, uint32 nStartGroup, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ uint32 nChoice;
+ uint32 *sl;
+ uint32 i, num;
+ char *string;
+ RMDialogChoice dc;
+ int sel;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ g_curDialog = nDialog;
+
+ // Call MPAL to start the dialog
+ mpalQueryDoDialog(nDialog, nStartGroup);
+
+ // Wait until a choice is selected
+ mpalQueryDialogWaitForChoice(&_ctx->nChoice);
+ while (_ctx->nChoice != (uint32) - 1) {
+ // Get the list of options
+ _ctx->sl = mpalQueryDialogSelectList(_ctx->nChoice);
+ for (_ctx->num = 0; _ctx->sl[_ctx->num] != 0; _ctx->num++)
+ ;
+
+ // If there is only one option, do it automatically, and wait for the next choice
+ if (_ctx->num == 1) {
+ mpalQueryDialogSelectionDWORD(_ctx->nChoice, _ctx->sl[0]);
+ globalDestroy(_ctx->sl);
+
+ // Wait for the next choice to be made
+ mpalQueryDialogWaitForChoice(&_ctx->nChoice);
+ continue;
+ }
+
+ // Making a choice for dialog
+ _ctx->dc.init();
+ _ctx->dc.setNumChoices(_ctx->num);
+
+ // Writeall the possible options
+ for (_ctx->i = 0; _ctx->i < _ctx->num; _ctx->i++) {
+ _ctx->string = mpalQueryDialogPeriod(_ctx->sl[_ctx->i]);
+ assert(_ctx->string != NULL);
+ _ctx->dc.addChoice(_ctx->string);
+ globalDestroy(_ctx->string);
+ }
+
+ // Activate the object
+ g_vm->getEngine()->linkGraphicTask(&_ctx->dc);
+ CORO_INVOKE_0(_ctx->dc.show);
+
+ // Draw the pointer
+ GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_NONE);
+ g_vm->getEngine()->enableMouse();
+
+ while (!(GLOBALS._input->mouseLeftClicked() && ((_ctx->sel = _ctx->dc.getSelection()) != -1))) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_1(_ctx->dc.doFrame, GLOBALS._input->mousePos());
+ }
+
+ // Hide the pointer
+ g_vm->getEngine()->disableMouse();
+
+ CORO_INVOKE_0(_ctx->dc.hide);
+ mpalQueryDialogSelectionDWORD(_ctx->nChoice, _ctx->sl[_ctx->sel]);
+
+ // Closes the choice
+ _ctx->dc.close();
+
+ globalDestroy(_ctx->sl);
+
+ // Wait for the next choice to be made
+ mpalQueryDialogWaitForChoice(&_ctx->nChoice);
+ }
+
+ CORO_END_CODE;
+}
+
+
+/*
+ * Sync between idle and mpal
+ */
+
+DECLARE_CUSTOM_FUNCTION(TakeOwnership)(CORO_PARAM, uint32 num, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) {
+ // The mutex is currently owned by a different process.
+ // Wait for the event to be signalled, which means the mutex is free.
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._mut[num]._eventId, CORO_INFINITE);
+ GLOBALS._mut[num]._ownerPid = (uint32)CoroScheduler.getCurrentPID();
+ }
+
+ GLOBALS._mut[num]._lockCount++;
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(ReleaseOwnership)(CORO_PARAM, uint32 num, uint32, uint32, uint32) {
+ if (!GLOBALS._mut[num]._lockCount) {
+ warning("ReleaseOwnership tried to release mutex %d, which isn't held", num);
+ return;
+ }
+
+ if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID())
+ error("ReleaseOwnership tried to release mutex %d, which is held by a different process", num);
+
+ GLOBALS._mut[num]._lockCount--;
+ if (!GLOBALS._mut[num]._lockCount) {
+ GLOBALS._mut[num]._ownerPid = 0;
+
+ // Signal the event, to wake up processes waiting for the lock.
+ CoroScheduler.setEvent(GLOBALS._mut[num]._eventId);
+ }
+}
+
+/*
+ * Music
+ * -----
+ *
+ * Fadeout effects supposed:
+ *
+ * nFX = 0 - The new music replaces the old one
+ * nFX=1 - The new music interfades with the old one
+ * nFX=2 - The new music takes over in time from the old
+ *
+ */
+
+void ThreadFadeInMusic(CORO_PARAM, const void *nMusic) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ int nChannel = *(const int *)nMusic;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ debug("Start FadeIn Music");
+
+ for (_ctx->i = 0; _ctx->i < 16; _ctx->i++) {
+ g_vm->setMusicVolume(nChannel, _ctx->i * 4);
+
+ CORO_INVOKE_1(CoroScheduler.sleep, 100);
+ }
+ g_vm->setMusicVolume(nChannel, 64);
+
+ debug("End FadeIn Music");
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+void ThreadFadeOutMusic(CORO_PARAM, const void *nMusic) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ int startVolume;
+ CORO_END_CONTEXT(_ctx);
+
+ int nChannel = *(const int *)nMusic;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->startVolume = g_vm->getMusicVolume(nChannel);
+
+ for (_ctx->i = 16; _ctx->i > 0 && !GLOBALS._bFadeOutStop; _ctx->i--) {
+ if (_ctx->i * 4 < _ctx->startVolume)
+ g_vm->setMusicVolume(nChannel, _ctx->i * 4);
+
+ CORO_INVOKE_1(CoroScheduler.sleep, 100);
+ }
+
+ if (!GLOBALS._bFadeOutStop)
+ g_vm->setMusicVolume(nChannel, 0);
+
+ // If a jingle is played, stop it
+ if (nChannel == 2)
+ g_vm->stopMusic(2);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(FadeInSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CoroScheduler.createProcess(ThreadFadeInMusic, &GLOBALS._curSoundEffect, sizeof(int));
+}
+
+DECLARE_CUSTOM_FUNCTION(FadeOutSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bFadeOutStop = false;
+ CoroScheduler.createProcess(ThreadFadeOutMusic, &GLOBALS._curSoundEffect, sizeof(int));
+}
+
+DECLARE_CUSTOM_FUNCTION(FadeOutJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bFadeOutStop = false;
+ int channel = 2;
+ CoroScheduler.createProcess(ThreadFadeOutMusic, &channel, sizeof(int));
+}
+
+DECLARE_CUSTOM_FUNCTION(FadeInJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ int channel = 2;
+ CoroScheduler.createProcess(ThreadFadeInMusic, &channel, sizeof(int));
+}
+
+DECLARE_CUSTOM_FUNCTION(StopSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->stopMusic(GLOBALS._curSoundEffect);
+}
+
+DECLARE_CUSTOM_FUNCTION(StopJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->stopMusic(2);
+}
+
+DECLARE_CUSTOM_FUNCTION(MuteSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->setMusicVolume(GLOBALS._curSoundEffect, 0);
+}
+
+DECLARE_CUSTOM_FUNCTION(DemuteSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bFadeOutStop = true;
+ g_vm->setMusicVolume(GLOBALS._curSoundEffect, 64);
+}
+
+DECLARE_CUSTOM_FUNCTION(MuteJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->setMusicVolume(2, 0);
+}
+
+DECLARE_CUSTOM_FUNCTION(DemuteJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ g_vm->setMusicVolume(2, 64);
+}
+
+void CustPlayMusic(uint32 nChannel, const char *mFN, uint32 nFX, bool bLoop, int nSync = 0) {
+ if (nSync == 0)
+ nSync = 2000;
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "Start CustPlayMusic");
+ g_vm->playMusic(nChannel, mFN, nFX, bLoop, nSync);
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "End CustPlayMusic");
+}
+
+DECLARE_CUSTOM_FUNCTION(PlaySoundEffect)(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bNoLoop, uint32) {
+ if (nFX == 0 || nFX == 1 || nFX == 2) {
+ debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "PlaySoundEffect stop fadeout");
+ GLOBALS._bFadeOutStop = true;
+ }
+
+ GLOBALS._lastMusic = nMusic;
+ CustPlayMusic(GLOBALS._curSoundEffect, musicFiles[nMusic].name, nFX, bNoLoop ? false : true, musicFiles[nMusic].sync);
+}
+
+DECLARE_CUSTOM_FUNCTION(PlayJingle)(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bLoop, uint32) {
+ CustPlayMusic(2, jingleFileNames[nMusic], nFX, bLoop);
+}
+
+DECLARE_CUSTOM_FUNCTION(PlayItemSfx)(CORO_PARAM, uint32 nItem, uint32 nSFX, uint32, uint32) {
+ if (nItem == 0) {
+ GLOBALS._tony->playSfx(nSFX);
+ } else {
+ RMItem *item = GLOBALS._loc->getItemFromCode(nItem);
+ if (item)
+ item->playSfx(nSFX);
+ }
+}
+
+
+void RestoreMusic(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(PlaySoundEffect, GLOBALS._lastMusic, 0, 0, 0);
+
+ if (GLOBALS._lastTappeto != 0)
+ CustPlayMusic(4, ambianceFile[GLOBALS._lastTappeto], 0, true);
+
+ CORO_END_CODE;
+}
+
+void SaveMusic(Common::OutSaveFile *f) {
+ f->writeByte(GLOBALS._lastMusic);
+ f->writeByte(GLOBALS._lastTappeto);
+}
+
+void LoadMusic(Common::InSaveFile *f) {
+ GLOBALS._lastMusic = f->readByte();
+ GLOBALS._lastTappeto = f->readByte();
+}
+
+
+DECLARE_CUSTOM_FUNCTION(JingleFadeStart)(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(FadeOutSoundEffect, 0, 0, 0, 0);
+ CORO_INVOKE_4(MuteJingle, 0, 0, 0, 0);
+ CORO_INVOKE_4(PlayJingle, nJingle, 0, bLoop, 0);
+ CORO_INVOKE_4(FadeInJingle, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(JingleFadeEnd)(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_4(FadeOutJingle, 0, 0, 0, 0);
+ CORO_INVOKE_4(FadeInSoundEffect, 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+
+
+
+DECLARE_CUSTOM_FUNCTION(MustSkipIdleStart)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bSkipIdle = true;
+ CoroScheduler.setEvent(GLOBALS._hSkipIdle);
+}
+
+DECLARE_CUSTOM_FUNCTION(MustSkipIdleEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ GLOBALS._bSkipIdle = false;
+ CoroScheduler.resetEvent(GLOBALS._hSkipIdle);
+}
+
+DECLARE_CUSTOM_FUNCTION(PatIrqFreeze)(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) {
+ // Unused in ScummVM.
+}
+
+DECLARE_CUSTOM_FUNCTION(OpenInitLoadMenu)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_0(g_vm->openInitLoadMenu);
+
+ CORO_END_CODE;
+}
+
+DECLARE_CUSTOM_FUNCTION(OpenInitOptions)(CORO_PARAM, uint32, uint32, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_0(g_vm->openInitOptions);
+
+ CORO_END_CODE;
+}
+
+
+DECLARE_CUSTOM_FUNCTION(DoCredits)(CORO_PARAM, uint32 nMsg, uint32 dwTime, uint32, uint32) {
+ CORO_BEGIN_CONTEXT;
+ RMMessage *msg;
+ RMTextDialog *text;
+ uint32 hDisable;
+ int i;
+ uint32 startTime;
+
+ ~CoroContextTag() {
+ delete msg;
+ delete[] text;
+ }
+
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->msg = new RMMessage(nMsg);
+ _ctx->hDisable = CoroScheduler.createEvent(true, false);
+
+ _ctx->text = new RMTextDialog[_ctx->msg->numPeriods()];
+
+ for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods(); _ctx->i++) {
+ _ctx->text[_ctx->i].setInput(GLOBALS._input);
+
+ // Alignment
+ if ((*_ctx->msg)[_ctx->i][0] == '@') {
+ _ctx->text[_ctx->i].setAlignType(RMText::HCENTER, RMText::VTOP);
+ _ctx->text[_ctx->i].writeText(&(*_ctx->msg)[_ctx->i][1], 3);
+ _ctx->text[_ctx->i].setPosition(RMPoint(414, 70 + _ctx->i * 26)); // 70
+ } else {
+ _ctx->text[_ctx->i].setAlignType(RMText::HLEFT, RMText::VTOP);
+ _ctx->text[_ctx->i].writeText((*_ctx->msg)[_ctx->i], 3);
+ _ctx->text[_ctx->i].setPosition(RMPoint(260, 70 + _ctx->i * 26));
+ }
+
+
+ // Set the position
+ _ctx->text[_ctx->i].setAlwaysDisplay();
+ _ctx->text[_ctx->i].setForcedTime(dwTime * 1000);
+ _ctx->text[_ctx->i].setNoTab();
+
+ // Wait for the end of display
+ _ctx->text[_ctx->i].setCustomSkipHandle(_ctx->hDisable);
+
+ // Record the text
+ g_vm->getEngine()->linkGraphicTask(&_ctx->text[_ctx->i]);
+ }
+
+ _ctx->startTime = g_vm->getTime();
+
+ while (_ctx->startTime + dwTime * 1000 > g_vm->getTime()) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ if (GLOBALS._input->mouseLeftClicked() || GLOBALS._input->mouseRightClicked())
+ break;
+ if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB))
+ break;
+ }
+
+ CoroScheduler.setEvent(_ctx->hDisable);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ delete[] _ctx->text;
+ delete _ctx->msg;
+ _ctx->text = NULL;
+ _ctx->msg = NULL;
+
+ CORO_END_CODE;
+}
+
+
+
+BEGIN_CUSTOM_FUNCTION_MAP()
+
+ASSIGN(1, CustLoadLocation)
+ASSIGN(2, MySleep)
+ASSIGN(3, SetPointer)
+ASSIGN(5, MoveTony)
+ASSIGN(6, FaceToMe)
+ASSIGN(7, BackToMe)
+ASSIGN(8, LeftToMe)
+ASSIGN(9, RightToMe)
+ASSIGN(10, SendTonyMessage)
+ASSIGN(11, ChangeBoxStatus)
+ASSIGN(12, ChangeLocation)
+ASSIGN(13, DisableTony)
+ASSIGN(14, EnableTony)
+ASSIGN(15, WaitForPatternEnd)
+ASSIGN(16, SetLocStartPosition)
+ASSIGN(17, ScrollLocation)
+ASSIGN(18, MoveTonyAndWait)
+ASSIGN(19, ChangeHotspot)
+ASSIGN(20, AddInventory)
+ASSIGN(21, RemoveInventory)
+ASSIGN(22, ChangeInventoryStatus)
+ASSIGN(23, SetTonyPosition)
+ASSIGN(24, SendFullscreenMessage)
+ASSIGN(25, SaveTonyPosition)
+ASSIGN(26, RestoreTonyPosition)
+ASSIGN(27, DisableInput)
+ASSIGN(28, EnableInput)
+ASSIGN(29, StopTony)
+
+ASSIGN(30, TonyTakeUp1)
+ASSIGN(31, TonyTakeMid1)
+ASSIGN(32, TonyTakeDown1)
+ASSIGN(33, TonyTakeUp2)
+ASSIGN(34, TonyTakeMid2)
+ASSIGN(35, TonyTakeDown2)
+
+ASSIGN(72, TonyPutUp1)
+ASSIGN(73, TonyPutMid1)
+ASSIGN(74, TonyPutDown1)
+ASSIGN(75, TonyPutUp2)
+ASSIGN(76, TonyPutMid2)
+ASSIGN(77, TonyPutDown2)
+
+ASSIGN(36, TonyOnTheFloor)
+ASSIGN(37, TonyGetUp)
+ASSIGN(38, TonyShepherdess)
+ASSIGN(39, TonyWhistle)
+
+ASSIGN(40, TonyLaugh)
+ASSIGN(41, TonyHips)
+ASSIGN(42, TonySing)
+ASSIGN(43, TonyIndicate)
+ASSIGN(44, TonyScaredWithHands)
+ASSIGN(49, TonyScaredWithoutHands)
+ASSIGN(45, TonyWithGlasses)
+ASSIGN(46, TonyWithWorm)
+ASSIGN(47, TonyWithHammer)
+ASSIGN(48, TonyWithRope)
+ASSIGN(90, TonyWithRabbitANIM)
+ASSIGN(91, TonyWithRecipeANIM)
+ASSIGN(92, TonyWithCardsANIM)
+ASSIGN(93, TonyWithSnowmanANIM)
+ASSIGN(94, TonyWithSnowmanStart)
+ASSIGN(95, TonyWithSnowmanEnd)
+ASSIGN(96, TonyWithRabbitStart)
+ASSIGN(97, TonyWithRabbitEnd)
+ASSIGN(98, TonyWithRecipeStart)
+ASSIGN(99, TonyWithRecipeEnd)
+ASSIGN(100, TonyWithCardsStart)
+ASSIGN(101, TonyWithCardsEnd)
+ASSIGN(102, TonyWithNotebookStart)
+ASSIGN(103, TonyWithNotebookEnd)
+ASSIGN(104, TonyWithMegaphoneStart)
+ASSIGN(105, TonyWithMegaphoneEnd)
+ASSIGN(106, TonyWithBeardStart)
+ASSIGN(107, TonyWithBeardEnd)
+ASSIGN(108, TonyGiggle)
+ASSIGN(109, TonyDisgusted)
+ASSIGN(110, TonySarcastic)
+ASSIGN(111, TonyMacbeth)
+ASSIGN(112, TonySniffLeft)
+ASSIGN(113, TonySniffRight)
+ASSIGN(114, TonyScaredStart)
+ASSIGN(115, TonyScaredEnd)
+ASSIGN(116, TonyWithSecretary)
+
+ASSIGN(50, CharSetCode)
+ASSIGN(51, CharSetColor)
+ASSIGN(52, CharSetTalkPattern)
+ASSIGN(53, CharSendMessage)
+ASSIGN(54, CharSetStartEndTalkPattern)
+
+ASSIGN(60, MCharSetCode)
+ASSIGN(61, MCharSetColor)
+ASSIGN(62, MCharSetCurrentGroup)
+ASSIGN(63, MCharSetNumTalksInGroup)
+ASSIGN(64, MCharSetNumTexts)
+ASSIGN(65, MCharSendMessage)
+ASSIGN(66, MCharSetPosition)
+ASSIGN(67, MCharSetAlwaysBack)
+ASSIGN(68, MCharResetCode)
+
+ASSIGN(70, StartDialog)
+ASSIGN(71, SendDialogMessage)
+
+ASSIGN(80, TakeOwnership)
+ASSIGN(81, ReleaseOwnership)
+
+ASSIGN(86, PlaySoundEffect)
+ASSIGN(87, PlayJingle)
+ASSIGN(88, FadeInSoundEffect)
+ASSIGN(89, FadeOutSoundEffect)
+ASSIGN(123, FadeInJingle)
+ASSIGN(124, FadeOutJingle)
+ASSIGN(125, MuteSoundEffect)
+ASSIGN(126, DemuteSoundEffect)
+ASSIGN(127, MuteJingle)
+ASSIGN(128, DemuteJingle)
+ASSIGN(84, StopSoundEffect)
+ASSIGN(85, StopJingle)
+ASSIGN(83, PlayItemSfx)
+ASSIGN(129, JingleFadeStart)
+ASSIGN(130, JingleFadeEnd)
+
+ASSIGN(120, ShakeScreen)
+ASSIGN(121, AutoSave)
+ASSIGN(122, AbortGame)
+ASSIGN(131, NoBullsEye)
+ASSIGN(132, SendFullscreenMsgStart)
+ASSIGN(133, SendFullscreenMsgEnd)
+ASSIGN(134, CustEnableGUI)
+ASSIGN(135, CustDisableGUI)
+ASSIGN(136, ClearScreen)
+ASSIGN(137, PatIrqFreeze)
+ASSIGN(138, TonySetPerorate)
+ASSIGN(139, OpenInitLoadMenu)
+ASSIGN(140, OpenInitOptions)
+ASSIGN(141, SyncScrollLocation)
+ASSIGN(142, CloseLocation)
+ASSIGN(143, SetAlwaysDisplay)
+ASSIGN(144, DoCredits)
+
+ASSIGN(200, MustSkipIdleStart);
+ASSIGN(201, MustSkipIdleEnd);
+
+END_CUSTOM_FUNCTION_MAP()
+
+void processKilledCallback(Common::PROCESS *p) {
+ for (uint i = 0; i < 10; i++)
+ if (GLOBALS._mut[i]._ownerPid == p->pid) {
+ // Handle scripts which don't call ReleaseOwnership, such as
+ // the one in loc37's vEnter when Tony is chasing the mouse.
+ debug(DEBUG_BASIC, "Force-releasing mutex %d after process died", i);
+
+ GLOBALS._mut[i]._ownerPid = 0;
+ GLOBALS._mut[i]._lockCount = 0;
+ CoroScheduler.setEvent(GLOBALS._mut[i]._eventId);
+ }
+}
+
+void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input) {
+ GLOBALS._tony = tony;
+ GLOBALS._pointer = ptr;
+ GLOBALS._boxes = box;
+ GLOBALS._loc = loc;
+ GLOBALS._inventory = inv;
+ GLOBALS._input = input;
+
+ GLOBALS.DisableGUI = mainDisableGUI;
+ GLOBALS.EnableGUI = mainEnableGUI;
+
+ GLOBALS._bAlwaysDisplay = false;
+ int i;
+
+ CoroScheduler.setResourceCallback(processKilledCallback);
+ for (i = 0; i < 10; i++)
+ GLOBALS._mut[i]._eventId = CoroScheduler.createEvent(false, true);
+
+ for (i = 0; i < 200; i++)
+ GLOBALS._ambiance[i] = 0;
+
+ GLOBALS._ambiance[6] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[7] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[8] = AMBIANCE_CRICKETSMUFFLED;
+ GLOBALS._ambiance[10] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[12] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[13] = AMBIANCE_CRICKETSMUFFLED;
+ GLOBALS._ambiance[15] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[16] = AMBIANCE_CRICKETSWIND;
+ GLOBALS._ambiance[18] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[19] = AMBIANCE_CRICKETSWIND;
+ GLOBALS._ambiance[20] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[23] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[26] = AMBIANCE_SEAHALFVOLUME;
+ GLOBALS._ambiance[27] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[28] = AMBIANCE_CRICKETSWIND;
+ GLOBALS._ambiance[31] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[33] = AMBIANCE_SEA;
+ GLOBALS._ambiance[35] = AMBIANCE_SEA;
+ GLOBALS._ambiance[36] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[37] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[40] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[41] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[42] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[45] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[51] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[52] = AMBIANCE_CRICKETSWIND1;
+ GLOBALS._ambiance[53] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[54] = AMBIANCE_CRICKETS;
+ GLOBALS._ambiance[57] = AMBIANCE_WIND;
+ GLOBALS._ambiance[58] = AMBIANCE_WIND;
+ GLOBALS._ambiance[60] = AMBIANCE_WIND;
+
+
+
+ // Create an event for the idle skipping
+ GLOBALS._hSkipIdle = CoroScheduler.createEvent(true, false);
+}
+
+} // end of namespace Tony
diff --git a/engines/tony/custom.h b/engines/tony/custom.h
new file mode 100644
index 0000000000..524ab14aab
--- /dev/null
+++ b/engines/tony/custom.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_CUSTOM_H
+#define TONY_CUSTOM_H
+
+#include "common/str.h"
+#include "tony/mpal/mpal.h"
+
+namespace Tony {
+
+using namespace MPAL;
+
+#define INIT_CUSTOM_FUNCTION MapCustomFunctions
+
+#define DECLARE_CUSTOM_FUNCTION(x) void x
+
+#define BEGIN_CUSTOM_FUNCTION_MAP() \
+ static void AssignError(int num) { \
+ error("Custom function %u has been already assigned!", num); \
+ } \
+ void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap) \
+ {
+
+#define END_CUSTOM_FUNCTION_MAP() \
+ }
+
+#define ASSIGN(num, func) \
+ if (lpMap[num] != NULL) \
+ AssignError(num); \
+ lpMap[num] = func; \
+ lpStrMap[num] = #func;
+
+class RMTony;
+class RMPointer;
+class RMGameBoxes;
+class RMLocation;
+class RMInventory;
+class RMInput;
+
+void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap);
+void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input);
+
+#endif
+
+} // end of namespace Tony
diff --git a/engines/tony/debugger.cpp b/engines/tony/debugger.cpp
new file mode 100644
index 0000000000..85d9469519
--- /dev/null
+++ b/engines/tony/debugger.cpp
@@ -0,0 +1,130 @@
+/* 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/coroutines.h"
+#include "tony/debugger.h"
+#include "tony/globals.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+Debugger::Debugger() : GUI::Debugger() {
+ DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
+ DCmd_Register("scene", WRAP_METHOD(Debugger, Cmd_Scene));
+ DCmd_Register("dirty_rects", WRAP_METHOD(Debugger, Cmd_DirtyRects));
+}
+
+static int strToInt(const char *s) {
+ if (!*s)
+ // No string at all
+ return 0;
+ else if (toupper(s[strlen(s) - 1]) != 'H')
+ // Standard decimal string
+ return atoi(s);
+
+ // Hexadecimal string
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
+ return (int)tmp;
+}
+
+/**
+ * Support process for changing the scene
+ */
+struct ChangeSceneDetails {
+ int sceneNumber;
+ int x;
+ int y;
+};
+
+void DebugChangeScene(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 result;
+ const ChangeSceneDetails *details = (const ChangeSceneDetails *)param;
+ RMPoint scenePos(details->x, details->y);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, &result);
+
+ g_vm->getEngine()->loadLocation(details->sceneNumber, scenePos, RMPoint(-1, -1));
+
+ mainEnableGUI();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * This command loads up the specified new scene number
+ */
+bool Debugger::Cmd_Scene(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Usage: %s <scene number> [<x> <y>]\n", argv[0]);
+ return true;
+ }
+
+ int sceneNumber = strToInt(argv[1]);
+ if (sceneNumber >= g_vm->_theBoxes.getLocBoxesCount()) {
+ DebugPrintf("Invalid scene\n");
+ return true;
+ }
+
+ RMPoint scenePos;
+ if (argc >= 4) {
+ scenePos._x = strToInt(argv[2]);
+ scenePos._y = strToInt(argv[3]);
+ } else {
+ // Get the box areas for the scene, and choose one so as to have a default
+ // position for Tony that will be in the walkable areas
+ RMBoxLoc *box = g_vm->_theBoxes.getBoxes(sceneNumber);
+ scenePos.set(box->_boxes[0]._hotspot[0]._hotx, box->_boxes[0]._hotspot[0]._hoty);
+ }
+
+ // Set up a process to change the scene
+ ChangeSceneDetails details;
+ details.sceneNumber = sceneNumber;
+ details.x = scenePos._x;
+ details.y = scenePos._y;
+ CoroScheduler.createProcess(DebugChangeScene, &details, sizeof(ChangeSceneDetails));
+
+ return false;
+}
+
+/**
+ * Turns showing dirty rects on or off
+ */
+bool Debugger::Cmd_DirtyRects(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage; %s [on | off]\n", argv[0]);
+ return true;
+ } else {
+ g_vm->_window.showDirtyRects(strcmp(argv[1], "on") == 0);
+ return false;
+ }
+}
+
+} // End of namespace Tony
diff --git a/engines/agos/installshield_cab.h b/engines/tony/debugger.h
index f7e8bed277..85ba9d75b6 100644
--- a/engines/agos/installshield_cab.h
+++ b/engines/tony/debugger.h
@@ -11,7 +11,7 @@
* 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
+ * 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
@@ -20,22 +20,24 @@
*
*/
-#include "common/archive.h"
-#include "common/str.h"
+#ifndef TONY_DEBUGGER_H
+#define TONY_DEBUGGER_H
-#ifndef AGOS_INSTALLSHIELD_CAB_H
-#define AGOS_INSTALLSHIELD_CAB_H
+#include "common/scummsys.h"
+#include "gui/debugger.h"
-namespace AGOS {
+namespace Tony {
-/**
- * This factory method creates an Archive instance corresponding to the content
- * of the InstallShield compressed file with the given name.
- *
- * May return 0 in case of a failure.
- */
-Common::Archive *makeInstallShieldArchive(const Common::String &name);
+class Debugger : public GUI::Debugger {
+public:
+ Debugger();
+ virtual ~Debugger() {}
+
+protected:
+ bool Cmd_Scene(int argc, const char **argv);
+ bool Cmd_DirtyRects(int argc, const char **argv);
+};
-} // End of namespace AGOS
+} // End of namespace Tony
#endif
diff --git a/engines/tony/detection.cpp b/engines/tony/detection.cpp
new file mode 100644
index 0000000000..8e6d5a64c3
--- /dev/null
+++ b/engines/tony/detection.cpp
@@ -0,0 +1,194 @@
+/* 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 "common/memstream.h"
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+#include "tony/tony.h"
+#include "tony/game.h"
+
+namespace Tony {
+
+enum {
+ GF_COMPRESSED = (1 << 0)
+};
+
+struct TonyGameDescription {
+ ADGameDescription desc;
+};
+
+uint32 TonyEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+Common::Language TonyEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+bool TonyEngine::getIsDemo() const {
+ return _gameDescription->desc.flags & ADGF_DEMO;
+}
+
+bool TonyEngine::isCompressed() const {
+ return _gameDescription->desc.flags & GF_COMPRESSED;
+}
+
+} // End of namespace Tony
+
+static const PlainGameDescriptor tonyGames[] = {
+ {"tony", "Tony Tough and the Night of Roasted Moths"},
+ {0, 0}
+};
+
+#include "tony/detection_tables.h"
+
+class TonyMetaEngine : public AdvancedMetaEngine {
+public:
+ TonyMetaEngine() : AdvancedMetaEngine(Tony::gameDescriptions, sizeof(Tony::TonyGameDescription), tonyGames) {
+ }
+
+ virtual const char *getName() const {
+ return "Tony Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Tony Engine (C) Protonic Interactive";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) 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 TonyMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail);
+}
+
+bool Tony::TonyEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool TonyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Tony::TonyGameDescription *gd = (const Tony::TonyGameDescription *)desc;
+ if (gd) {
+ *engine = new Tony::TonyEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+SaveStateList TonyMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = "tony.0??";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ int slotNum = atoi(file->c_str() + file->size() - 3);
+
+ if (slotNum >= 0 && slotNum <= 999) {
+ byte thumbnailData[160 * 120 * 2];
+ Common::String saveName;
+ byte difficulty;
+
+ if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slotNum, thumbnailData, saveName, difficulty)) {
+ // Add the save name to the savegame list
+ saveList.push_back(SaveStateDescriptor(slotNum, saveName));
+ }
+ }
+ }
+
+ return saveList;
+}
+
+int TonyMetaEngine::getMaximumSaveSlot() const {
+ return 99;
+}
+
+void TonyMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String filename = Tony::TonyEngine::getSaveStateFileName(slot);
+
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor TonyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String saveName;
+ byte difficulty;
+ byte thumbData[160 * 120 * 2];
+
+ if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slot, thumbData, saveName, difficulty)) {
+ // Convert the 565 thumbnail data to the needed overlay format
+ Common::MemoryReadStream thumbStream(thumbData, 160 * 120 * 2);
+ Graphics::PixelFormat destFormat = g_system->getOverlayFormat();
+ Graphics::Surface *to = new Graphics::Surface();
+ to->create(160, 120, destFormat);
+
+ OverlayColor *pixels = (OverlayColor *)to->pixels;
+ for (int y = 0; y < to->h; ++y) {
+ for (int x = 0; x < to->w; ++x) {
+ uint8 r, g, b;
+ Graphics::colorToRGB<Graphics::ColorMasks<555> >(thumbStream.readUint16LE(), r, g, b);
+
+ // converting to current OSystem Color
+ *pixels++ = destFormat.RGBToColor(r, g, b);
+ }
+ }
+
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, saveName);
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+ desc.setThumbnail(to);
+
+ return desc;
+ }
+
+ return SaveStateDescriptor();
+}
+
+
+#if PLUGIN_ENABLED_DYNAMIC(TONY)
+REGISTER_PLUGIN_DYNAMIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine);
+#endif
diff --git a/engines/tony/detection_tables.h b/engines/tony/detection_tables.h
new file mode 100644
index 0000000000..f9881c29f2
--- /dev/null
+++ b/engines/tony/detection_tables.h
@@ -0,0 +1,145 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+namespace Tony {
+
+static const TonyGameDescription gameDescriptions[] = {
+ {
+ // Tony Tough English
+ {
+ "tony",
+ 0,
+ {
+ // TODO: AdvancedDetector seems to have a problem where it thinks data1.cab is unrecognized.
+ // Is it perhaps because the Agos engine also has detection entries for data1.cab?
+ {"data1.cab", 0, "ce82907242166bfb594d97bdb68f96d2", 4350},
+ /*{"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "57c4a3860cf899443c357e0078ea6f49", 366773},*/
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // Tony Tough English Demo
+ {
+ "tony",
+ "Extracted Demo",
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 14972409},
+ {"roasted.mpc", 0, "1e247922ec869712bfd96625bc4d3c7c", 39211},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // Tony Tough English Demo (Compressed)
+ {
+ "tony",
+ "Demo",
+ {
+ {"data1.cab", 0, "7d8b6d308f96aee3968ad7910fb11e6d", 58660608},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO | GF_COMPRESSED,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough French "Collection Aventure" provided by Strangerke
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "e890c6a41238827bdfa9874a65618b69", 374135},
+ AD_LISTEND
+ },
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough German "Shoe Box" provided by Strangerke
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "ccf7ab939a34de1b13df538596431684", 389554},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough Italian provided by Fabio Barzagli
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "1dc896cdb945170d7408598f803411c1", 380001},
+ AD_LISTEND
+ },
+ Common::IT_ITA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ {
+ // Tony Tough Polish provided by Fabio Barzagli
+ {
+ "tony",
+ 0,
+ {
+ {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071},
+ {"roasted.mpc", 0, "89733ea710669acc8e7900b115f4afef", 389625},
+ AD_LISTEND
+ },
+ Common::PL_POL,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace Tony
diff --git a/engines/tony/font.cpp b/engines/tony/font.cpp
new file mode 100644
index 0000000000..927adf9006
--- /dev/null
+++ b/engines/tony/font.cpp
@@ -0,0 +1,1200 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/textconsole.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/font.h"
+#include "tony/input.h"
+#include "tony/inventory.h"
+#include "tony/loc.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* RMFont Methods
+\****************************************************************************/
+
+RMFont::RMFont() {
+ _letter = NULL;
+ _nLetters = _fontDimx = _fontDimy = _dimx = _dimy = 0;
+}
+
+RMFont::~RMFont() {
+ unload();
+}
+
+void RMFont::load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID) {
+ _letter = new RMGfxSourceBuffer8RLEByte[nChars];
+
+ // Initialize the fonts
+ for (int i = 0; i < nChars; i++) {
+ // Initialize the buffer with the letters
+ _letter[i].init(buf + i * (dimx * dimy + 8) + 8, dimx, dimy);
+ _letter[i].loadPaletteWA(palResID);
+ }
+
+ _fontDimx = dimx;
+ _fontDimy = dimy;
+
+ _nLetters = nChars;
+}
+
+void RMFont::load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID) {
+ RMRes res(resID);
+
+ if ((int)res.size() < nChars * (dimy * dimx + 8))
+ nChars = res.size() / (dimy * dimx + 8);
+
+ load(res, nChars, dimx, dimy, palResID);
+}
+
+void RMFont::unload() {
+ if (_letter != NULL) {
+ delete[] _letter;
+ _letter = NULL;
+ }
+}
+
+
+RMGfxPrimitive *RMFont::makeLetterPrimitive(byte bChar, int &nLength) {
+ RMFontPrimitive *prim;
+ int nLett;
+
+ // Convert from character to glyph index
+ nLett = convertToLetter(bChar);
+ assert(nLett < _nLetters);
+
+ // Create primitive font
+ prim = new RMFontPrimitive(this);
+ prim->_nChar = nLett;
+
+ // Get the length of the character in pixels
+ nLength = letterLength(bChar);
+
+ return prim;
+}
+
+void RMFont::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim2) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ RMFontPrimitive *prim = (RMFontPrimitive *)prim2;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Call the draw method of the letter assigned to the primitive
+ if (prim->_nChar != -1)
+ CORO_INVOKE_2(_letter[prim->_nChar].draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+void RMFont::close() {
+ unload();
+}
+
+int RMFont::stringLen(const Common::String &text) {
+ uint len, i;
+
+ if (text.empty())
+ return letterLength('\0');
+
+ len = 0;
+ for (i = 0; i < text.size() - 1; i++)
+ len += letterLength(text[i], text[i + 1]);
+ len += letterLength(text[i]);
+
+ return len;
+}
+
+int RMFont::stringLen(char bChar, char bNext) {
+ return letterLength(bChar, bNext);
+}
+
+/****************************************************************************\
+* RMFontColor Methods
+\****************************************************************************/
+
+RMFontColor::RMFontColor() : RMFont() {
+ _fontR = _fontG = _fontB = 255;
+}
+
+RMFontColor::~RMFontColor() {
+}
+
+void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) {
+ int r = (int)r1 << 16;
+ int g = (int)g1 << 16;
+ int b = (int)b1 << 16;
+
+ int rstep = r / 14;
+ int gstep = g / 14;
+ int bstep = b / 14;
+
+ int i;
+ byte pal[768 * 3];
+
+ // Check if we are already on the right color
+ if (_fontR == r1 && _fontG == g1 && _fontB == b1)
+ return;
+
+ _fontR = r1;
+ _fontG = g1;
+ _fontB = b1;
+
+ // Constructs a new palette for the font
+ for (i = 1; i < 16; i++) {
+ pal[i * 3 + 0] = r >> 16;
+ pal[i * 3 + 1] = g >> 16;
+ pal[i * 3 + 2] = b >> 16;
+
+ r -= rstep;
+ g -= gstep;
+ b -= bstep;
+ }
+
+ pal[15 * 3 + 0] += 8;
+ pal[15 * 3 + 1] += 8;
+ pal[15 * 3 + 2] += 8;
+
+ // Puts in all the letters
+ for (i = 0; i < _nLetters; i++)
+ _letter[i].loadPaletteWA(pal);
+}
+
+/***************************************************************************\
+* RMFontWithTables Methods
+\****************************************************************************/
+int RMFontWithTables::convertToLetter(byte nChar) {
+ return _cTable[nChar];
+}
+
+int RMFontWithTables::letterLength(int nChar, int nNext) {
+ return (nChar != -1 ? _lTable[(byte)nChar] + _l2Table[(byte)nChar][(byte)nNext] : _lDefault);
+}
+
+/***************************************************************************\
+* RMFontDialog Methods
+\****************************************************************************/
+
+void RMFontDialog::init() {
+ int i;
+
+ // bernie: Number of characters in the font
+ int nchars =
+ 112 // base
+ + 18 // polish
+ + 66 // russian
+ + 30 // czech
+ + 8 // french
+ + 5; // deutsch
+
+ load(RES_F_PARL, nchars, 20, 20);
+
+ // Initialize the font table
+ _lDefault = 13;
+ _hDefault = 18;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableDialog[i];
+ _lTable[i] = g_vm->_lTableDialog[i];
+ }
+}
+
+
+/***************************************************************************\
+* RMFontMacc Methods
+\****************************************************************************/
+
+void RMFontMacc::init() {
+ int i;
+
+ // bernie: Number of characters in the font
+ int nchars =
+ 102 // base
+ + 18 // polish
+ + 66 // russian
+ + 30 // czech
+ + 8 // francais
+ + 5; // deutsch
+
+
+ load(RES_F_MACC, nchars, 11, 16);
+
+ // Default
+ _lDefault = 10;
+ _hDefault = 17;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableMacc[i];
+ _lTable[i] = g_vm->_lTableMacc[i];
+ }
+}
+
+/***************************************************************************\
+* RMFontCredits Methods
+\****************************************************************************/
+
+void RMFontCredits::init() {
+ int i;
+
+ // bernie: Number of characters in the font
+ int nchars =
+ 112 // base
+ + 18 // polish
+ + 66 // russian
+ + 30 // czech
+ + 8 // french
+ + 2; // deutsch
+
+
+ load(RES_F_CREDITS, nchars, 27, 28, RES_F_CPAL);
+
+ // Default
+ _lDefault = 10;
+ _hDefault = 28;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableCred[i];
+ _lTable[i] = g_vm->_lTableCred[i];
+ }
+}
+
+
+
+/***************************************************************************\
+* RMFontObj Methods
+\****************************************************************************/
+
+#define TOUPPER(a) ((a) >= 'a' && (a) <= 'z' ? (a) + 'A' - 'a' : (a))
+#define TOLOWER(a) ((a) >= 'A' && (a) <= 'Z' ? (a) + 'a' - 'A' : (a))
+
+void RMFontObj::setBothCase(int nChar, int nNext, signed char spiazz) {
+ _l2Table[TOUPPER(nChar)][TOUPPER(nNext)] = spiazz;
+ _l2Table[TOUPPER(nChar)][TOLOWER(nNext)] = spiazz;
+ _l2Table[TOLOWER(nChar)][TOUPPER(nNext)] = spiazz;
+ _l2Table[TOLOWER(nChar)][TOLOWER(nNext)] = spiazz;
+}
+
+
+void RMFontObj::init() {
+ int i;
+
+ //bernie: Number of characters in the font (solo maiuscolo)
+ int nchars =
+ 85 // base
+ + 9 // polish
+ + 33 // russian
+ + 15 // czech
+ + 0 // francais (no uppercase chars)
+ + 1; // deutsch
+
+
+ load(RES_F_OBJ, nchars, 25, 30);
+
+ // Initialize the font table
+ _lDefault = 26;
+ _hDefault = 30;
+ Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
+
+ for (i = 0; i < 256; i++) {
+ _cTable[i] = g_vm->_cTableObj[i];
+ _lTable[i] = g_vm->_lTableObj[i];
+ }
+
+ // Special case
+ setBothCase('C', 'C', 2);
+ setBothCase('A', 'T', -2);
+ setBothCase('R', 'S', 2);
+ setBothCase('H', 'I', -2);
+ setBothCase('T', 'S', 2);
+ setBothCase('O', 'R', 2);
+ setBothCase('O', 'L', 2);
+ setBothCase('O', 'G', 2);
+ setBothCase('Z', 'A', -1);
+ setBothCase('R', 'R', 1);
+ setBothCase('R', 'U', 3);
+}
+
+
+/****************************************************************************\
+* RMText Methods
+\****************************************************************************/
+
+RMFontColor *RMText::_fonts[4] = { NULL, NULL, NULL, NULL };
+
+void RMText::initStatics() {
+ Common::fill(&_fonts[0], &_fonts[4], (RMFontColor *)NULL);
+}
+
+RMText::RMText() {
+ // Default color: white
+ _textR = _textG = _textB = 255;
+
+ // Default length
+ _maxLineLength = 350;
+
+ _bTrasp0 = true;
+ _aHorType = HCENTER;
+ _aVerType = VTOP;
+ setPriority(150);
+}
+
+RMText::~RMText() {
+}
+
+void RMText::unload() {
+ if (_fonts[0] != NULL) {
+ delete _fonts[0];
+ delete _fonts[1];
+ delete _fonts[2];
+ delete _fonts[3];
+ _fonts[0] = _fonts[1] = _fonts[2] = _fonts[3] = 0;
+ }
+}
+
+void RMText::setMaxLineLength(int max) {
+ _maxLineLength = max;
+}
+
+void RMText::removeThis(CORO_PARAM, bool &result) {
+ // Here we can do checks on the number of frames, time spent, etc.
+ result = true;
+}
+
+void RMText::writeText(const Common::String &text, int nFont, int *time) {
+ // Initializes the font (only once)
+ if (_fonts[0] == NULL) {
+ _fonts[0] = new RMFontDialog;
+ _fonts[0]->init();
+ _fonts[1] = new RMFontObj;
+ _fonts[1]->init();
+ _fonts[2] = new RMFontMacc;
+ _fonts[2]->init();
+ _fonts[3] = new RMFontCredits;
+ _fonts[3]->init();
+ }
+
+ writeText(text, _fonts[nFont], time);
+}
+
+void RMText::writeText(Common::String text, RMFontColor *font, int *time) {
+ RMGfxPrimitive *prim;
+ uint p, old_p;
+ int j, x, y;
+ int len;
+ int numchar;
+ int width, height;
+
+ // Set the base color
+ font->setBaseColor(_textR, _textG, _textB);
+
+ // Destroy the buffer before starting
+ destroy();
+
+ // If the string is empty, do nothing
+ if (text.empty())
+ return;
+
+ // Divide the words into lines. In this cycle, X contains the maximum length reached by a line,
+ // and the number of lines
+ Common::Array<Common::String> lines;
+ p = 0;
+ j = x = 0;
+ while (p < text.size()) {
+ j += font->stringLen(text[p]);
+ if (j > (((_aHorType == HLEFTPAR) && (lines.size() > 0)) ? _maxLineLength - 25 : _maxLineLength)) {
+ j -= font->stringLen(text[p], (p + 1 == text.size()) ? '\0' : text[p + 1]);
+ if (j > x)
+ x = j;
+
+ // Back to the first usable space
+ //
+ // BERNIE: In the original, sentences containing words that exceed the
+ // width of a line caused discontinuation of the whole sentence.
+ // This workaround has the partial word broken up so it will still display
+ //
+ old_p = p;
+ while (text[p] != ' ' && text[p] != '-' && p > 0)
+ p--;
+
+ if (p == 0)
+ p = old_p;
+
+ // Check if there are any blanks to end
+ while ((text[p] == ' ' || text[p] == '-') && p + 1 < text.size())
+ p++;
+ if (p == text.size())
+ break;
+ lines.push_back(Common::String(text.c_str(), p));
+ if (text[p] == ' ')
+ p++;
+ text = text.c_str() + p;
+ p = 0;
+ j = 0;
+ continue;
+ }
+ p++;
+ }
+
+ if (j > x)
+ x = j;
+
+ // Add the last line of text.
+ lines.push_back(text);
+
+ x += 8;
+
+ // Starting position for the surface: X1, Y
+ width = x;
+ height = (lines.size() - 1) * font->letterHeight() + font->_fontDimy;
+
+ // Create the surface
+ create(width, height);
+ Common::fill(_buf, _buf + width * height * 2, 0);
+
+ p = 0;
+
+ y = 0;
+ numchar = 0;
+ for (uint i = 0; i < lines.size(); ++i) {
+ const Common::String &line = lines[i];
+
+ // Measure the length of the line
+ x = 0;
+ j = font->stringLen(line);
+
+ switch (_aHorType) {
+ case HLEFT:
+ x = 0;
+ break;
+
+ case HLEFTPAR:
+ if (i == 0)
+ x = 0;
+ else
+ x = 25;
+ break;
+
+ case HCENTER:
+ x = width / 2 - j / 2;
+ break;
+
+ case HRIGHT:
+ x = width - j - 1;
+ break;
+ }
+
+ p = 0;
+ while (p < line.size()) {
+ if (line[p] == ' ') {
+ x += font->stringLen(line[p]);
+ p++;
+ continue;
+ }
+
+ prim = font->makeLetterPrimitive(line[p], len);
+ prim->getDst()._x1 = x;
+ prim->getDst()._y1 = y;
+ addPrim(prim);
+
+ numchar++;
+
+ x += font->stringLen(line[p], (p + 1 == line.size()) ? '\0' : line[p + 1]);
+ p++;
+ }
+ p++;
+ y += font->letterHeight();
+ }
+
+ if (time != NULL)
+ *time = 1000 + numchar * (11 - GLOBALS._nCfgTextSpeed) * 14;
+}
+
+void RMText::clipOnScreen(RMGfxPrimitive *prim) {
+ // Don't let it go outside the screen
+ if (prim->getDst()._x1 < 5)
+ prim->getDst()._x1 = 5;
+ if (prim->getDst()._y1 < 5)
+ prim->getDst()._y1 = 5;
+ if (prim->getDst()._x1 + _dimx > 635)
+ prim->getDst()._x1 = 635 - _dimx;
+ if (prim->getDst()._y1 + _dimy > 475)
+ prim->getDst()._y1 = 475 - _dimy;
+}
+
+void RMText::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+ // Horizontally
+ if (_aHorType == HCENTER)
+ prim->getDst().topLeft() -= RMPoint(_dimx / 2, 0);
+ else if (_aHorType == HRIGHT)
+ prim->getDst().topLeft() -= RMPoint(_dimx, 0);
+
+
+ // Vertically
+ if (_aVerType == VTOP) {
+
+ } else if (_aVerType == VCENTER) {
+ prim->getDst()._y1 -= _dimy / 2;
+
+ } else if (_aVerType == VBOTTOM) {
+ prim->getDst()._y1 -= _dimy;
+ }
+
+ clipOnScreen(prim);
+
+ CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Set the alignment type
+ */
+void RMText::setAlignType(HorAlign aHor, VerAlign aVer) {
+ _aHorType = aHor;
+ _aVerType = aVer;
+}
+
+/**
+ * Set the base color
+ */
+void RMText::setColor(byte r, byte g, byte b) {
+ _textR = r;
+ _textG = g;
+ _textB = b;
+}
+
+/****************************************************************************\
+* RMTextDialog Methods
+\****************************************************************************/
+
+RMTextDialog::RMTextDialog() : RMText() {
+ _time = _startTime = 0;
+ _dst = RMPoint(0, 0);
+
+ _bSkipStatus = true;
+ _bShowed = true;
+ _bForceTime = false;
+ _bForceNoTime = false;
+ _bAlwaysDisplay = false;
+ _bNoTab = false;
+ _hCustomSkip = CORO_INVALID_PID_VALUE;
+ _hCustomSkip2 = CORO_INVALID_PID_VALUE;
+ _input = NULL;
+
+ // Create the event for displaying the end
+ _hEndDisplay = CoroScheduler.createEvent(false, false);
+}
+
+RMTextDialog::~RMTextDialog() {
+ CoroScheduler.closeEvent(_hEndDisplay);
+}
+
+void RMTextDialog::show() {
+ _bShowed = true;
+}
+
+void RMTextDialog::hide(CORO_PARAM) {
+ _bShowed = false;
+}
+
+void RMTextDialog::writeText(const Common::String &text, int font, int *time) {
+ RMText::writeText(text, font, &_time);
+
+ if (time != NULL)
+ *time = _time;
+}
+
+void RMTextDialog::writeText(const Common::String &text, RMFontColor *font, int *time) {
+ RMText::writeText(text, font, &_time);
+
+ if (time != NULL)
+ *time = _time;
+}
+
+
+void RMTextDialog::setSkipStatus(bool bEnabled) {
+ _bSkipStatus = bEnabled;
+}
+
+void RMTextDialog::forceTime() {
+ _bForceTime = true;
+}
+
+void RMTextDialog::forceNoTime() {
+ _bForceNoTime = true;
+}
+
+void RMTextDialog::setNoTab() {
+ _bNoTab = true;
+}
+
+void RMTextDialog::setForcedTime(uint32 dwTime) {
+ _time = dwTime;
+}
+
+void RMTextDialog::setAlwaysDisplay() {
+ _bAlwaysDisplay = true;
+}
+
+void RMTextDialog::removeThis(CORO_PARAM, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ bool expired;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Presume successful result
+ result = true;
+
+ // Don't erase the background
+ if (_bSkipStatus) {
+ if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) {
+ if (GLOBALS._bCfgTimerizedText) {
+ if (!_bForceNoTime) {
+ if (g_vm->getTime() > (uint32)_time + _startTime)
+ return;
+ }
+ }
+ }
+
+ if (!_bNoTab) {
+ if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB))
+ return;
+ }
+
+ if (!_bNoTab) {
+ if (_input) {
+ if (_input->mouseLeftClicked() || _input->mouseRightClicked())
+ return;
+ }
+ }
+ }
+ // Erase the background
+ else if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) {
+ if (!_bForceNoTime) {
+ if (g_vm->getTime() > (uint32)_time + _startTime)
+ return;
+ }
+ }
+
+ // If time is forced
+ if (_bForceTime) {
+ if (g_vm->getTime() > (uint32)_time + _startTime)
+ return;
+ }
+
+ if (_hCustomSkip != CORO_INVALID_PID_VALUE) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip, 0, &_ctx->expired);
+ // == WAIT_OBJECT_0
+ if (!_ctx->expired)
+ return;
+ }
+
+ if (GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip2, 0, &_ctx->expired);
+ // == WAIT_OBJECT_0
+ if (!_ctx->expired)
+ return;
+ }
+
+ result = false;
+
+ CORO_END_CODE;
+}
+
+void RMTextDialog::Unregister() {
+ RMGfxTask::Unregister();
+ assert(_nInList == 0);
+ CoroScheduler.setEvent(_hEndDisplay);
+}
+
+void RMTextDialog::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_startTime == 0)
+ _startTime = g_vm->getTime();
+
+ if (_bShowed) {
+ if (GLOBALS._bShowSubtitles || _bAlwaysDisplay) {
+ prim->getDst().topLeft() = _dst;
+ CORO_INVOKE_2(RMText::draw, bigBuf, prim);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMTextDialog::setCustomSkipHandle(uint32 hCustom) {
+ _hCustomSkip = hCustom;
+}
+
+void RMTextDialog::setCustomSkipHandle2(uint32 hCustom) {
+ _hCustomSkip2 = hCustom;
+}
+
+void RMTextDialog::waitForEndDisplay(CORO_PARAM) {
+ CoroScheduler.waitForSingleObject(coroParam, _hEndDisplay, CORO_INFINITE);
+}
+
+void RMTextDialog::setInput(RMInput *input) {
+ _input = input;
+}
+
+/**
+ * Set the position
+ */
+void RMTextDialog::setPosition(const RMPoint &pt) {
+ _dst = pt;
+}
+
+/****************************************************************************\
+* RMTextDialogScrolling Methods
+\****************************************************************************/
+
+RMTextDialogScrolling::RMTextDialogScrolling() {
+ _curLoc = NULL;
+}
+
+RMTextDialogScrolling::RMTextDialogScrolling(RMLocation *loc) {
+ _curLoc = loc;
+ _startScroll = loc->scrollPosition();
+}
+
+RMTextDialogScrolling::~RMTextDialogScrolling() {
+}
+
+void RMTextDialogScrolling::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint curDst;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curDst = _dst;
+
+ if (_curLoc != NULL)
+ _dst -= _curLoc->scrollPosition() - _startScroll;
+
+ CORO_INVOKE_2(RMTextDialog::draw, bigBuf, prim);
+
+ _dst = _ctx->curDst;
+
+ CORO_END_CODE;
+}
+
+void RMTextDialogScrolling::clipOnScreen(RMGfxPrimitive *prim) {
+ // We must not do anything!
+}
+
+
+/****************************************************************************\
+* RMTextItemName Methods
+\****************************************************************************/
+
+RMTextItemName::RMTextItemName() : RMText() {
+ _item = NULL;
+ setPriority(220);
+}
+
+RMTextItemName::~RMTextItemName() {
+}
+
+void RMTextItemName::doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv) {
+ CORO_BEGIN_CONTEXT;
+ RMItem *lastItem;
+ uint32 hThread;
+ CORO_END_CONTEXT(_ctx);
+
+ Common::String itemName;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->lastItem = _item;
+
+ // Adds to the list if there is need
+ if (!_nInList)
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ // Update the scrolling co-ordinates
+ _curscroll = loc.scrollPosition();
+
+ // Check if we are on the inventory
+ if (inv.itemInFocus(_mpos))
+ _item = inv.whichItemIsIn(_mpos);
+ else
+ _item = loc.whichItemIsIn(_mpos);
+
+ // If there an item, get its name
+ if (_item != NULL)
+ _item->getName(itemName);
+
+ // Write it
+ writeText(itemName, 1);
+
+ // Handle the change If the selected item is different from the previous one
+ if (_ctx->lastItem != _item) {
+ if (_item == NULL)
+ ptr.setSpecialPointer(RMPointer::PTR_NONE);
+ else {
+ _ctx->hThread = mpalQueryDoAction(20, _item->mpalCode(), 0);
+ if (_ctx->hThread == CORO_INVALID_PID_VALUE)
+ ptr.setSpecialPointer(RMPointer::PTR_NONE);
+ else
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->hThread, CORO_INFINITE);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+
+void RMTextItemName::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // If there is no text, it's pointless to continue
+ if (_buf == NULL)
+ return;
+
+ // Set the destination coordinates of the mouse
+ prim->getDst().topLeft() = _mpos - RMPoint(0, 30);
+
+ CORO_INVOKE_2(RMText::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+RMPoint RMTextItemName::getHotspot() {
+ if (_item == NULL)
+ return _mpos + _curscroll;
+ else
+ return _item->hotspot();
+}
+
+RMItem *RMTextItemName::getSelectedItem() {
+ return _item;
+}
+
+bool RMTextItemName::isItemSelected() {
+ return _item != NULL;
+}
+
+void RMTextItemName::setMouseCoord(const RMPoint &m) {
+ _mpos = m;
+}
+
+void RMTextItemName::removeThis(CORO_PARAM, bool &result) {
+ result = true;
+}
+
+/****************************************************************************\
+* RMDialogChoice Methods
+\****************************************************************************/
+
+RMDialogChoice::RMDialogChoice() {
+ RMResRaw dlg1(RES_I_DLGTEXT);
+ RMResRaw dlg2(RES_I_DLGTEXTLINE);
+ RMRes dlgpal(RES_I_DLGTEXTPAL);
+
+ _dlgText.init(dlg1, dlg1.width(), dlg1.height());
+ _dlgTextLine.init(dlg2, dlg2.width(), dlg2.height());
+
+ _dlgText.loadPaletteWA(dlgpal);
+ _dlgTextLine.loadPaletteWA(dlgpal);
+
+ _hUnreg = CoroScheduler.createEvent(false, false);
+ _bRemoveFromOT = false;
+
+ _curAdded = 0;
+ _bShow = false;
+}
+
+RMDialogChoice::~RMDialogChoice() {
+ CoroScheduler.closeEvent(_hUnreg);
+}
+
+void RMDialogChoice::Unregister() {
+ RMGfxWoodyBuffer::Unregister();
+ assert(!_nInList);
+ CoroScheduler.pulseEvent(_hUnreg);
+
+ _bRemoveFromOT = false;
+}
+
+void RMDialogChoice::init() {
+ _numChoices = 0;
+ _drawedStrings = NULL;
+ _ptDrawStrings = NULL;
+ _curSelection = -1;
+
+ create(640, 477);
+ setPriority(140);
+}
+
+
+void RMDialogChoice::close() {
+ if (_drawedStrings != NULL) {
+ delete[] _drawedStrings;
+ _drawedStrings = NULL;
+ }
+
+ if (_ptDrawStrings != NULL) {
+ delete[] _ptDrawStrings;
+ _ptDrawStrings = NULL;
+ }
+
+ destroy();
+}
+
+void RMDialogChoice::setNumChoices(int num) {
+ int i;
+
+ _numChoices = num;
+ _curAdded = 0;
+
+ // Allocate space for drawn strings
+ _drawedStrings = new RMText[num];
+ _ptDrawStrings = new RMPoint[num];
+
+ // Initialization
+ for (i = 0; i < _numChoices; i++) {
+ _drawedStrings[i].setColor(0, 255, 0);
+ _drawedStrings[i].setAlignType(RMText::HLEFTPAR, RMText::VTOP);
+ _drawedStrings[i].setMaxLineLength(600);
+ _drawedStrings[i].setPriority(10);
+ }
+}
+
+void RMDialogChoice::addChoice(const Common::String &string) {
+ // Draw the string
+ assert(_curAdded < _numChoices);
+ _drawedStrings[_curAdded++].writeText(string, 0);
+}
+
+void RMDialogChoice::prepare(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ RMPoint ptPos;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ addPrim(new RMGfxPrimitive(&_dlgText, RMPoint(0, 0)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83)));
+ addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83 + 83)));
+
+ _ctx->ptPos.set(20, 90);
+
+ for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
+ addPrim(new RMGfxPrimitive(&_drawedStrings[_ctx->i], _ctx->ptPos));
+ _ptDrawStrings[_ctx->i] = _ctx->ptPos;
+ _ctx->ptPos.offset(0, _drawedStrings[_ctx->i].getDimy() + 15);
+ }
+
+ CORO_INVOKE_0(drawOT);
+ clearOT();
+
+ _ptDrawPos.set(0, 480 - _ctx->ptPos._y);
+
+ CORO_END_CODE;
+}
+
+void RMDialogChoice::setSelected(CORO_PARAM, int pos) {
+ CORO_BEGIN_CONTEXT;
+ RMGfxBox box;
+ RMRect rc;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (pos == _curSelection)
+ return;
+
+ _ctx->box.setPriority(5);
+
+ if (_curSelection != -1) {
+ _ctx->box.setColor(0xCC, 0xCC, 0xFF);
+ _ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[_curSelection]._y);
+ _ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[_curSelection].getDimy());
+ addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
+
+ addPrim(new RMGfxPrimitive(&_drawedStrings[_curSelection], _ptDrawStrings[_curSelection]));
+ CORO_INVOKE_0(drawOT);
+ clearOT();
+ }
+
+ if (pos != -1) {
+ _ctx->box.setColor(100, 100, 100);
+ _ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[pos]._y);
+ _ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[pos].getDimy());
+ addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
+ addPrim(new RMGfxPrimitive(&_drawedStrings[pos], _ptDrawStrings[pos]));
+ }
+
+ CORO_INVOKE_0(drawOT);
+ clearOT();
+
+ _curSelection = pos;
+
+ CORO_END_CODE;
+}
+
+void RMDialogChoice::show(CORO_PARAM, RMGfxTargetBuffer *bigBuf) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint destpt;
+ int deltay;
+ int starttime;
+ int elaps;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_0(prepare);
+ _bShow = false;
+
+ if (!_nInList && bigBuf != NULL)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+
+ if (0) {
+ _bShow = true;
+ } else {
+ _ctx->starttime = g_vm->getTime();
+ _ctx->deltay = 480 - _ptDrawPos._y;
+ _ctx->destpt = _ptDrawPos;
+ _ptDrawPos.set(0, 480);
+
+ if (!_nInList && bigBuf != NULL)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+ _bShow = true;
+
+ _ctx->elaps = 0;
+ while (_ctx->elaps < 700) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ _ctx->elaps = g_vm->getTime() - _ctx->starttime;
+ _ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * _ctx->elaps) / 100;
+ }
+
+ _ptDrawPos._y = _ctx->destpt._y;
+ }
+
+ CORO_END_CODE;
+}
+
+void RMDialogChoice::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bShow == false)
+ return;
+
+ prim->setDst(_ptDrawPos);
+ CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+
+void RMDialogChoice::hide(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int deltay;
+ int starttime;
+ int elaps;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (1) {
+ _ctx->starttime = g_vm->getTime();
+
+ _ctx->deltay = 480 - _ptDrawPos._y;
+ _ctx->elaps = 0;
+ while (_ctx->elaps < 700) {
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ _ctx->elaps = g_vm->getTime() - _ctx->starttime;
+ _ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * (700 - _ctx->elaps)) / 100;
+ }
+ }
+
+ _bShow = false;
+ _bRemoveFromOT = true;
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hUnreg, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+
+void RMDialogChoice::removeThis(CORO_PARAM, bool &result) {
+ result = _bRemoveFromOT;
+}
+
+void RMDialogChoice::doFrame(CORO_PARAM, RMPoint ptMousePos) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (ptMousePos._y > _ptDrawPos._y) {
+ for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
+ if ((ptMousePos._y >= _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y) && (ptMousePos._y < _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y + _drawedStrings[_ctx->i].getDimy())) {
+ CORO_INVOKE_1(setSelected, _ctx->i);
+ break;
+ }
+ }
+
+ if (_ctx->i == _numChoices)
+ CORO_INVOKE_1(setSelected, -1);
+ }
+
+ CORO_END_CODE;
+}
+
+int RMDialogChoice::getSelection() {
+ return _curSelection;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/font.h b/engines/tony/font.h
new file mode 100644
index 0000000000..99b20571b1
--- /dev/null
+++ b/engines/tony/font.h
@@ -0,0 +1,379 @@
+/* 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.
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_FONT_H
+#define TONY_FONT_H
+
+#include "common/system.h"
+#include "common/coroutines.h"
+#include "tony/gfxcore.h"
+#include "tony/resid.h"
+
+namespace Tony {
+
+class RMInput;
+class RMInventory;
+class RMItem;
+class RMLoc;
+class RMLocation;
+class RMPointer;
+
+/**
+ * Manages a font, in which there is a different surface for each letter
+ */
+class RMFont : public RMGfxTaskSetPrior {
+protected:
+ int _nLetters;
+ RMGfxSourceBuffer8RLEByte *_letter;
+public:
+ int _fontDimx, _fontDimy;
+
+private:
+ int _dimx, _dimy;
+
+ class RMFontPrimitive : public RMGfxPrimitive {
+ public:
+ RMFontPrimitive() : RMGfxPrimitive() { _nChar = 0; }
+ RMFontPrimitive(RMGfxTask *task) : RMGfxPrimitive(task) { _nChar = 0; }
+ virtual ~RMFontPrimitive() { }
+ virtual RMGfxPrimitive *duplicate() {
+ return new RMFontPrimitive(*this);
+ }
+
+ int _nChar;
+ };
+
+protected:
+ // Loads the font
+ void load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL);
+ void load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL);
+
+ // Remove the font
+ void unload();
+
+protected:
+ // Conversion form character to font index
+ virtual int convertToLetter(byte nChar) = 0;
+
+ // Character width
+ virtual int letterLength(int nChar, int nNext = 0) = 0;
+
+public:
+ virtual int letterHeight() = 0;
+
+public:
+ RMFont();
+ virtual ~RMFont();
+
+ // Initialization and closing
+ virtual void init() = 0;
+ virtual void close();
+
+ // Drawing
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBug, RMGfxPrimitive *prim);
+
+ // Create a primitive for a letter
+ RMGfxPrimitive *makeLetterPrimitive(byte bChar, int &nLength);
+
+ // Length in pixels of a string with the current font
+ int stringLen(const Common::String &text);
+ int stringLen(char bChar, char bNext = 0);
+};
+
+
+class RMFontColor : public virtual RMFont {
+private:
+ byte _fontR, _fontG, _fontB;
+
+public:
+ RMFontColor();
+ virtual ~RMFontColor();
+ virtual void setBaseColor(byte r, byte g, byte b);
+};
+
+
+class RMFontWithTables : public virtual RMFont {
+protected:
+ int _cTable[256];
+ int _lTable[256];
+ int _lDefault;
+ int _hDefault;
+ signed char _l2Table[256][256];
+
+protected:
+ // Overloaded methods
+ int convertToLetter(byte nChar);
+ int letterLength(int nChar, int nNext = 0);
+
+public:
+ int letterHeight() {
+ return _hDefault;
+ }
+ virtual ~RMFontWithTables() {}
+};
+
+
+class RMFontDialog : public RMFontColor, public RMFontWithTables {
+public:
+ void init();
+ virtual ~RMFontDialog() {}
+};
+
+class RMFontObj : public RMFontColor, public RMFontWithTables {
+private:
+ void setBothCase(int nChar, int nNext, signed char spiazz);
+
+public:
+ void init();
+ virtual ~RMFontObj() {}
+};
+
+class RMFontMacc : public RMFontColor, public RMFontWithTables {
+public:
+ void init();
+ virtual ~RMFontMacc() {}
+};
+
+class RMFontCredits : public RMFontColor, public RMFontWithTables {
+public:
+ void init();
+ virtual ~RMFontCredits() {}
+ virtual void setBaseColor(byte r, byte g, byte b) {}
+};
+
+/**
+ * Manages writing text onto9 the screen
+ */
+class RMText : public RMGfxWoodyBuffer {
+private:
+ static RMFontColor *_fonts[4];
+ int _maxLineLength;
+
+public:
+ enum HorAlign {
+ HLEFT,
+ HLEFTPAR,
+ HCENTER,
+ HRIGHT
+ };
+
+ enum VerAlign {
+ VTOP,
+ VCENTER,
+ VBOTTOM
+ };
+
+private:
+ HorAlign _aHorType;
+ VerAlign _aVerType;
+ byte _textR, _textG, _textB;
+
+protected:
+ virtual void clipOnScreen(RMGfxPrimitive *prim);
+
+public:
+ RMText();
+ virtual ~RMText();
+ static void initStatics();
+ static void unload();
+
+ // Set the alignment type
+ void setAlignType(HorAlign aHor, VerAlign aVer);
+
+ // Sets the maximum length of a line in pixels (used to format the text)
+ void setMaxLineLength(int max);
+
+ // Write the text
+ void writeText(const Common::String &text, int font, int *time = NULL);
+ void writeText(Common::String text, RMFontColor *font, int *time = NULL);
+
+ // Overloaded function to decide when you delete the object from the OT list
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Overloading of the Draw to center the text, if necessary
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Set the base color
+ void setColor(byte r, byte g, byte b);
+};
+
+/**
+ * Manages text in a dialog
+ */
+class RMTextDialog : public RMText {
+protected:
+ int _startTime;
+ int _time;
+ bool _bSkipStatus;
+ RMPoint _dst;
+ uint32 _hEndDisplay;
+ bool _bShowed;
+ bool _bForceTime;
+ bool _bForceNoTime;
+ uint32 _hCustomSkip;
+ uint32 _hCustomSkip2;
+ RMInput *_input;
+ bool _bAlwaysDisplay;
+ bool _bNoTab;
+
+public:
+ RMTextDialog();
+ virtual ~RMTextDialog();
+
+ // Write the text
+ void writeText(const Common::String &text, int font, int *time = NULL);
+ void writeText(const Common::String &text, RMFontColor *font, int *time = NULL);
+
+ // Overloaded function to decide when you delete the object from the OT list
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Overloaded de-registration
+ virtual void Unregister();
+
+ // Overloading of the Draw to center the text, if necessary
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Set the position
+ void setPosition(const RMPoint &pt);
+
+ // Waiting
+ void waitForEndDisplay(CORO_PARAM);
+ void setCustomSkipHandle(uint32 hCustomSkip);
+ void setCustomSkipHandle2(uint32 hCustomSkip);
+ void setSkipStatus(bool bEnabled);
+ void setForcedTime(uint32 dwTime);
+ void setNoTab();
+ void forceTime();
+ void forceNoTime();
+ void setAlwaysDisplay();
+
+ // Set the input device, to allow skip from mouse
+ void setInput(RMInput *input);
+
+ void show();
+ void hide(CORO_PARAM);
+};
+
+class RMTextDialogScrolling : public RMTextDialog {
+protected:
+ RMLocation *_curLoc;
+ RMPoint _startScroll;
+
+ virtual void clipOnScreen(RMGfxPrimitive *prim);
+
+public:
+ RMTextDialogScrolling();
+ RMTextDialogScrolling(RMLocation *loc);
+ virtual ~RMTextDialogScrolling();
+
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Manages the name of a selected item on the screen
+ */
+class RMTextItemName : protected RMText {
+protected:
+ RMPoint _mpos;
+ RMPoint _curscroll;
+ RMItem *_item;
+
+public:
+ RMTextItemName();
+ virtual ~RMTextItemName();
+
+ void setMouseCoord(const RMPoint &m);
+
+ void doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ RMPoint getHotspot();
+ RMItem *getSelectedItem();
+ bool isItemSelected();
+
+ virtual void removeThis(CORO_PARAM, bool &result);
+};
+
+
+/**
+ * Manages the selection of screen items in a box
+ */
+class RMDialogChoice : public RMGfxWoodyBuffer {
+private:
+ int _curSelection;
+ int _numChoices;
+ RMText *_drawedStrings;
+ RMPoint *_ptDrawStrings;
+ int _curAdded;
+ bool _bShow;
+ RMGfxSourceBuffer8 _dlgText;
+ RMGfxSourceBuffer8 _dlgTextLine;
+ RMPoint _ptDrawPos;
+ uint32 _hUnreg;
+ bool _bRemoveFromOT;
+
+protected:
+ void prepare(CORO_PARAM);
+ void setSelected(CORO_PARAM, int pos);
+
+public:
+ virtual void removeThis(CORO_PARAM, bool &result);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void Unregister();
+
+public:
+ // Initialization
+ RMDialogChoice();
+ virtual ~RMDialogChoice();
+
+ // Initialization and closure
+ void init();
+ void close();
+
+ // Sets the number of possible sentences, which then be added with AddChoice()
+ void setNumChoices(int num);
+
+ // Adds a string with the choice
+ void addChoice(const Common::String &string);
+
+ // Show and hide the selection, with possible animations.
+ // NOTE: If no parameter is passed to Show(), it is the obligation of
+ // caller to ensure that the class is inserted into OT list
+ void show(CORO_PARAM, RMGfxTargetBuffer *bigBuf = NULL);
+ void hide(CORO_PARAM);
+
+ // Polling Update
+ void doFrame(CORO_PARAM, RMPoint ptMousePos);
+
+ // Returns the currently selected item, or -1 if none is selected
+ int getSelection();
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/game.cpp b/engines/tony/game.cpp
new file mode 100644
index 0000000000..2bcfdc7fc2
--- /dev/null
+++ b/engines/tony/game.cpp
@@ -0,0 +1,1617 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+#include "graphics/cursorman.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/custom.h"
+#include "tony/game.h"
+#include "tony/gfxengine.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+using namespace MPAL;
+
+// Global functions
+void mainEnableGUI() {
+ g_vm->getEngine()->_bGUIInterface = true;
+ g_vm->getEngine()->_bGUIInventory = true;
+ g_vm->getEngine()->_bGUIOption = true;
+}
+
+void mainDisableGUI() {
+ g_vm->getEngine()->_bGUIInterface = false;
+ g_vm->getEngine()->_bGUIInventory = false;
+ g_vm->getEngine()->_bGUIOption = false;
+}
+
+/****************************************************************************\
+* RMOptionButton Methods
+\****************************************************************************/
+
+RMOptionButton::RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState) {
+ RMResRaw raw(dwRes);
+ assert(raw.isValid());
+ _buf = new RMGfxSourceBuffer16(false);
+ _buf->init(raw, raw.width(), raw.height());
+
+ _rect.setRect(pt._x, pt._y, pt._x + raw.width() - 1, pt._y + raw.height() - 1);
+ _bActive = false;
+ _bHasGfx = true;
+ _bDoubleState = bDoubleState;
+}
+
+RMOptionButton::RMOptionButton(const RMRect &pt) {
+ _rect = pt;
+ _bActive = false;
+ _bHasGfx = false;
+ _bDoubleState = false;
+ _buf = NULL;
+}
+
+RMOptionButton::~RMOptionButton() {
+ if (_bHasGfx)
+ delete _buf;
+}
+
+bool RMOptionButton::doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick) {
+ if (!_bDoubleState) {
+ if (_rect.ptInRect(mousePos)) {
+ if (!_bActive) {
+ _bActive = true;
+ return true;
+ }
+ } else {
+ if (_bActive) {
+ _bActive = false;
+ return true;
+ }
+ }
+ } else {
+ if (bLeftClick && _rect.ptInRect(mousePos)) {
+ _bActive = !_bActive;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void RMOptionButton::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!_bActive)
+ return;
+
+ if (_bHasGfx)
+ CORO_INVOKE_2(_buf->draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+void RMOptionButton::addToList(RMGfxTargetBuffer &bigBuf) {
+ if (_bHasGfx)
+ bigBuf.addPrim(new RMGfxPrimitive(this, _rect));
+}
+
+bool RMOptionButton::isActive() {
+ return _bActive;
+}
+
+void RMOptionButton::setActiveState(bool bState) {
+ _bActive = bState;
+}
+
+/****************************************************************************\
+* RMOptionSlide Methods
+\****************************************************************************/
+
+RMOptionSlide::RMOptionSlide(const RMPoint &pt, int nRange, int nStartValue, int slideSize) {
+ RMResRaw *raw;
+
+ _pos = pt;
+ _nSlideSize = slideSize;
+ _nMax = nRange;
+ _nStep = 100 / _nMax;
+ _nValue = nStartValue;
+
+ _sliderCenter = NULL;
+ _sliderLeft = NULL;
+ _sliderRight = NULL;
+ _sliderSingle = NULL;
+
+ // Sliders
+ INIT_GFX16_FROMRAW(20029, _sliderCenter);
+ INIT_GFX16_FROMRAW(20030, _sliderLeft);
+ INIT_GFX16_FROMRAW(20031, _sliderRight);
+ INIT_GFX16_FROMRAW(20032, _sliderSingle);
+
+ // Buttons
+ _pushLeft = new RMOptionButton(RMRect(pt._x - 23, pt._y, pt._x - 23 + 22, pt._y + 26));
+ _pushRight = new RMOptionButton(RMRect(pt._x + _nSlideSize, pt._y, pt._x + _nSlideSize + 5 + 22, pt._y + 26));
+}
+
+
+RMOptionSlide::~RMOptionSlide() {
+ delete _sliderCenter;
+ _sliderCenter = NULL;
+ delete _sliderLeft;
+ _sliderLeft = NULL;
+ delete _sliderRight;
+ _sliderRight = NULL;
+ delete _sliderSingle;
+ _sliderSingle = NULL;
+
+ delete _pushLeft;
+ _pushLeft = NULL;
+ delete _pushRight;
+ _pushRight = NULL;
+}
+
+bool RMOptionSlide::doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick) {
+ bool bRefresh = false;
+
+ // Do the button DoFrame's
+ _pushLeft->doFrame(mousePos, bLeftClick, bRightClick);
+ _pushRight->doFrame(mousePos, bLeftClick, bRightClick);
+
+ if (_pushLeft->isActive()) {
+ if (bLeftClick) {
+ bRefresh = true;
+ _nValue--;
+ } else if (bRightClick) {
+ bRefresh = true;
+ _nValue -= 3;
+ }
+ if (_nValue < 1)
+ _nValue = 1;
+ } else if (_pushRight->isActive()) {
+ bRefresh = true;
+
+ if (bLeftClick) {
+ bRefresh = true;
+ _nValue++;
+ } else if (bRightClick) {
+ bRefresh = true;
+ _nValue += 3;
+ }
+ if (_nValue > _nMax)
+ _nValue = _nMax;
+ }
+
+ return bRefresh;
+}
+
+void RMOptionSlide::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ int val;
+ RMPoint pos;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->pos = _pos;
+ _ctx->pos._x += 4;
+ _ctx->pos._y += 4;
+
+ _ctx->val = _nValue * _nStep;
+ if (_ctx->val < 1)
+ _ctx->val = 1;
+ else if (_ctx->val > 100)
+ _ctx->val = 100;
+
+ if (_ctx->val == 1) {
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderSingle->draw, bigBuf, prim);
+ } else {
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderLeft->draw, bigBuf, prim);
+ _ctx->pos._x += 3;
+
+ for (_ctx->i = 1; _ctx->i < _ctx->val - 1; _ctx->i++) {
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderCenter->draw, bigBuf, prim);
+ _ctx->pos._x += 3;
+ }
+
+ prim->setDst(_ctx->pos);
+ CORO_INVOKE_2(_sliderRight->draw, bigBuf, prim);
+ _ctx->pos._x += 3;
+ }
+
+ CORO_END_CODE;
+}
+
+void RMOptionSlide::addToList(RMGfxTargetBuffer &bigBuf) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+}
+
+int RMOptionSlide::getValue() {
+ return _nValue;
+}
+
+/****************************************************************************\
+* RMOptionScreen Methods
+\****************************************************************************/
+
+RMOptionScreen::RMOptionScreen() {
+ _nState = MENUNONE;
+ _menu = NULL;
+ _hideLoadSave = NULL;
+ _quitConfirm = NULL;
+ _bQuitConfirm = false;
+
+ create(RM_SX, RM_SY);
+
+ _buttonExit = NULL;
+ _buttonLoad = NULL;
+ _buttonSave = NULL;
+ _buttonGameMenu = NULL;
+ _buttonGfxMenu = NULL;
+ _buttonSoundMenu = NULL;
+ _buttonSave_ArrowLeft = NULL;
+ _buttonSave_ArrowRight = NULL;
+ _bEditSaveName = false;
+
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ _curThumb[i] = NULL;
+ _buttonSave_States[i] = NULL;
+ }
+
+ _statePos = 0;
+ _buttonQuitYes = NULL;
+ _buttonQuitNo = NULL;
+ _buttonQuit = NULL;
+ _saveEasy = NULL;
+ _saveHard = NULL;
+ _buttonGfx_Tips = NULL;
+ _buttonSound_DubbingOn = NULL;
+ _buttonSound_MusicOn = NULL;
+ _buttonSound_SFXOn = NULL;
+ _slideTonySpeed = NULL;
+ _slideTextSpeed = NULL;
+ _buttonGame_Lock = NULL;
+ _buttonGfx_Anni30 = NULL;
+ _sliderSound_Music = NULL;
+ _buttonGame_TimerizedText = NULL;
+ _buttonGfx_AntiAlias = NULL;
+ _sliderSound_SFX = NULL;
+ _buttonGame_Scrolling = NULL;
+ _buttonGfx_Sottotitoli = NULL;
+ _sliderSound_Dubbing = NULL;
+ _buttonGame_InterUp = NULL;
+ _buttonGfx_Trans = NULL;
+
+ _fadeStep = 0;
+ _fadeY = 0;
+ _fadeTime = 0;
+ _nEditPos = 0;
+ _nLastState = MENUGAME;
+}
+
+RMOptionScreen::~RMOptionScreen() {
+ closeState();
+}
+
+void RMOptionScreen::refreshAll(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ RMGfxSourceBuffer16 *thumb;
+ RMText *title;
+ RMText *num[6];
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+ clearOT();
+
+ addPrim(new RMGfxPrimitive(_menu));
+
+ if (_bNoLoadSave)
+ addPrim(new RMGfxPrimitive(_hideLoadSave, RMPoint(0, 401)));
+
+ if (_bQuitConfirm) {
+ addPrim(new RMGfxPrimitive(_quitConfirm, RMPoint(270, 200)));
+ _buttonQuitYes->addToList(*this);
+ _buttonQuitNo->addToList(*this);
+ }
+
+ _buttonExit->addToList(*this);
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ _buttonQuit->addToList(*this);
+ _buttonLoad->addToList(*this);
+ _buttonSave->addToList(*this);
+ }
+
+ if (_nState == MENUGAME) {
+ _buttonGame_Lock->addToList(*this);
+ _buttonGame_TimerizedText->addToList(*this);
+ _buttonGame_Scrolling->addToList(*this);
+ _buttonGame_InterUp->addToList(*this);
+ _slideTextSpeed->addToList(*this);
+ _slideTonySpeed->addToList(*this);
+ } else if (_nState == MENUGFX) {
+ _buttonGfx_Anni30->addToList(*this);
+ _buttonGfx_AntiAlias->addToList(*this);
+ _buttonGfx_Sottotitoli->addToList(*this);
+ _buttonGfx_Trans->addToList(*this);
+ _buttonGfx_Tips->addToList(*this);
+ } else if (_nState == MENUSOUND) {
+ _sliderSound_Dubbing->addToList(*this);
+ _sliderSound_Music->addToList(*this);
+ _sliderSound_SFX->addToList(*this);
+ _buttonSound_DubbingOn->addToList(*this);
+ _buttonSound_MusicOn->addToList(*this);
+ _buttonSound_SFXOn->addToList(*this);
+ }
+
+ _ctx->thumb = NULL;
+ _ctx->title = NULL;
+ Common::fill(&_ctx->num[0], &_ctx->num[6], (RMText *)NULL);
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ _ctx->title = new RMText;
+ if (_nState == MENULOAD) {
+ RMMessage msg(10);
+ _ctx->title->writeText(msg[0], 1);
+ } else {
+ RMMessage msg(11);
+ _ctx->title->writeText(msg[0], 1);
+ }
+
+ addPrim(new RMGfxPrimitive(_ctx->title, RMPoint(320, 10)));
+
+ if (_curThumbDiff[0] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(48, 57)));
+ else if (_curThumbDiff[0] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(48, 57)));
+ if (_curThumbDiff[1] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(240, 57)));
+ else if (_curThumbDiff[1] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(240, 57)));
+ if (_curThumbDiff[2] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(432, 57)));
+ else if (_curThumbDiff[2] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(432, 57)));
+ if (_curThumbDiff[3] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(48, 239)));
+ else if (_curThumbDiff[3] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(48, 239)));
+ if (_curThumbDiff[4] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(240, 239)));
+ else if (_curThumbDiff[4] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(240, 239)));
+ if (_curThumbDiff[5] == 0)
+ addPrim(new RMGfxPrimitive(_saveHard, RMPoint(432, 239)));
+ else if (_curThumbDiff[5] == 1)
+ addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(432, 239)));
+
+ if (_curThumb[0] && !(_bEditSaveName && _nEditPos == 0))
+ addPrim(new RMGfxPrimitive(_curThumb[0], RMPoint(48, 57)));
+ if (_curThumb[1] && !(_bEditSaveName && _nEditPos == 1))
+ addPrim(new RMGfxPrimitive(_curThumb[1], RMPoint(240, 57)));
+ if (_curThumb[2] && !(_bEditSaveName && _nEditPos == 2))
+ addPrim(new RMGfxPrimitive(_curThumb[2], RMPoint(432, 57)));
+ if (_curThumb[3] && !(_bEditSaveName && _nEditPos == 3))
+ addPrim(new RMGfxPrimitive(_curThumb[3], RMPoint(48, 239)));
+ if (_curThumb[4] && !(_bEditSaveName && _nEditPos == 4))
+ addPrim(new RMGfxPrimitive(_curThumb[4], RMPoint(240, 239)));
+ if (_curThumb[5] && !(_bEditSaveName && _nEditPos == 5))
+ addPrim(new RMGfxPrimitive(_curThumb[5], RMPoint(432, 239)));
+
+ if (_bEditSaveName) {
+ _ctx->thumb = new RMGfxSourceBuffer16;
+ _ctx->thumb->init((byte *)g_vm->getThumbnail(), 640 / 4, 480 / 4);
+
+ if (_nEditPos == 0)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(48, 57)));
+ else if (_nEditPos == 1)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(240, 57)));
+ else if (_nEditPos == 2)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(432, 57)));
+ else if (_nEditPos == 3)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(48, 239)));
+ else if (_nEditPos == 4)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(240, 239)));
+ else if (_nEditPos == 5)
+ addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(432, 239)));
+ }
+
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) {
+ Common::String s;
+
+ if (_bEditSaveName && _nEditPos == _ctx->i)
+ s = Common::String::format("%02d)%s*", _statePos + _ctx->i, _editName);
+ else {
+ if (_statePos == 0 && _ctx->i == 0)
+ s = "Autosave";
+ else
+ s = Common::String::format("%02d)%s", _statePos + _ctx->i, _curThumbName[_ctx->i].c_str());
+ }
+
+ _ctx->num[_ctx->i] = new RMText;
+ _ctx->num[_ctx->i]->setAlignType(RMText::HLEFT, RMText::VTOP);
+ _ctx->num[_ctx->i]->writeText(s, 2);
+ }
+
+ addPrim(new RMGfxPrimitive(_ctx->num[0], RMPoint(55 - 3, 180 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[1], RMPoint(247 - 3, 180 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[2], RMPoint(439 - 3, 180 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[3], RMPoint(55 - 3, 362 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[4], RMPoint(247 - 3, 362 + 14)));
+ addPrim(new RMGfxPrimitive(_ctx->num[5], RMPoint(439 - 3, 362 + 14)));
+
+ _buttonSave_ArrowLeft->addToList(*this);
+ _buttonSave_ArrowRight->addToList(*this);
+ }
+
+ CORO_INVOKE_0(drawOT);
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_ctx->thumb)
+ delete _ctx->thumb;
+
+ if (_ctx->title)
+ delete _ctx->title;
+
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) {
+ if (_ctx->num[_ctx->i])
+ delete _ctx->num[_ctx->i];
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::refreshThumbnails() {
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ if (_curThumb[i])
+ delete _curThumb[i];
+
+ _curThumb[i] = new RMGfxSourceBuffer16;
+ _curThumb[i]->create(640 / 4, 480 / 4);
+ if (!loadThumbnailFromSaveState(_statePos + i, *_curThumb[i], _curThumbName[i], _curThumbDiff[i])) {
+ delete _curThumb[i];
+ _curThumb[i] = NULL;
+ _curThumbName[i].clear();
+ _curThumbDiff[i] = 11;
+ }
+ }
+}
+
+void RMOptionScreen::initState(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ RMResRaw *raw;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND)
+ _ctx->raw = new RMResRaw(20000 + _nState);
+ else if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_bAlterGfx)
+ _ctx->raw = new RMResRaw(20024);
+ else
+ _ctx->raw = new RMResRaw(20003);
+ } else {
+ error("Invalid state");
+ }
+
+ assert(_ctx->raw->isValid());
+ assert(_menu == NULL);
+ _menu = new RMGfxSourceBuffer16(false);
+ _menu->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height());
+ delete _ctx->raw;
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_bAlterGfx) {
+ assert(_buttonExit == NULL);
+ _buttonExit = new RMOptionButton(20025, RMPoint(561, 406));
+ } else {
+ assert(_buttonExit == NULL);
+ _buttonExit = new RMOptionButton(20012, RMPoint(560, 404));
+ }
+
+ INIT_GFX8_FROMRAW(_ctx->raw, 20036, _saveEasy);
+ INIT_GFX8_FROMRAW(_ctx->raw, 20037, _saveHard);
+
+ refreshThumbnails();
+
+ assert(_buttonSave_States[0] == NULL);
+ _buttonSave_States[0] = new RMOptionButton(RMRect(48, 57, 48 + 160, 57 + 120));
+ assert(_buttonSave_States[1] == NULL);
+ _buttonSave_States[1] = new RMOptionButton(RMRect(240, 57, 240 + 160, 57 + 120));
+ assert(_buttonSave_States[2] == NULL);
+ _buttonSave_States[2] = new RMOptionButton(RMRect(432, 57, 432 + 160, 57 + 120));
+ assert(_buttonSave_States[3] == NULL);
+ _buttonSave_States[3] = new RMOptionButton(RMRect(48, 239, 48 + 160, 239 + 120));
+ assert(_buttonSave_States[4] == NULL);
+ _buttonSave_States[4] = new RMOptionButton(RMRect(240, 239, 240 + 160, 239 + 120));
+ assert(_buttonSave_States[5] == NULL);
+ _buttonSave_States[5] = new RMOptionButton(RMRect(432, 239, 432 + 160, 239 + 120));
+
+ if (_bAlterGfx) {
+ assert(_buttonSave_ArrowLeft == NULL);
+ _buttonSave_ArrowLeft = new RMOptionButton(20026, RMPoint(3, 196));
+ assert(_buttonSave_ArrowRight == NULL);
+ _buttonSave_ArrowRight = new RMOptionButton(20027, RMPoint(601, 197));
+ } else {
+ assert(_buttonSave_ArrowLeft == NULL);
+ _buttonSave_ArrowLeft = new RMOptionButton(20013, RMPoint(0, 197));
+ assert(_buttonSave_ArrowRight == NULL);
+ _buttonSave_ArrowRight = new RMOptionButton(20014, RMPoint(601, 197));
+ }
+ } else if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ assert(_buttonExit == NULL);
+ _buttonExit = new RMOptionButton(20005, RMPoint(560, 405));
+ assert(_buttonQuit == NULL);
+ _buttonQuit = new RMOptionButton(20020, RMPoint(7, 408));
+ assert(_buttonLoad == NULL);
+ _buttonLoad = new RMOptionButton(20006, RMPoint(231, 401));
+ assert(_buttonSave == NULL);
+ _buttonSave = new RMOptionButton(20007, RMPoint(325, 401));
+
+ assert(_buttonGameMenu == NULL);
+ _buttonGameMenu = new RMOptionButton(RMRect(24, 32, 118, 64));
+ assert(_buttonGfxMenu == NULL);
+ _buttonGfxMenu = new RMOptionButton(RMRect(118, 32, 212, 64));
+ assert(_buttonSoundMenu == NULL);
+ _buttonSoundMenu = new RMOptionButton(RMRect(212, 32, 306, 64));
+
+ _ctx->raw = new RMResRaw(20021);
+ assert(_ctx->raw->isValid());
+ assert(_quitConfirm == NULL);
+ _quitConfirm = new RMGfxSourceBuffer16(false);
+ _quitConfirm->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height());
+ delete _ctx->raw;
+
+ assert(_buttonQuitYes == NULL);
+ _buttonQuitYes = new RMOptionButton(20022, RMPoint(281, 265));
+ _buttonQuitYes->setPriority(30);
+ assert(_buttonQuitNo == NULL);
+ _buttonQuitNo = new RMOptionButton(20023, RMPoint(337, 264));
+ _buttonQuitNo->setPriority(30);
+
+ if (_bNoLoadSave) {
+ _ctx->raw = new RMResRaw(20028);
+ assert(_ctx->raw->isValid());
+ assert(_hideLoadSave == NULL);
+ _hideLoadSave = new RMGfxSourceBuffer16(false);
+ _hideLoadSave->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height());
+ delete _ctx->raw;
+ }
+
+ // Menu GAME
+ if (_nState == MENUGAME) {
+ assert(_buttonGame_Lock == NULL);
+ _buttonGame_Lock = new RMOptionButton(20008, RMPoint(176, 262), true);
+ _buttonGame_Lock->setActiveState(GLOBALS._bCfgInvLocked);
+ assert(_buttonGame_TimerizedText == NULL);
+ _buttonGame_TimerizedText = new RMOptionButton(20009, RMPoint(463, 273), true);
+ _buttonGame_TimerizedText->setActiveState(!GLOBALS._bCfgTimerizedText);
+ assert(_buttonGame_Scrolling == NULL);
+ _buttonGame_Scrolling = new RMOptionButton(20010, RMPoint(315, 263), true);
+ _buttonGame_Scrolling->setActiveState(GLOBALS._bCfgInvNoScroll);
+ assert(_buttonGame_InterUp == NULL);
+ _buttonGame_InterUp = new RMOptionButton(20011, RMPoint(36, 258), true);
+ _buttonGame_InterUp->setActiveState(GLOBALS._bCfgInvUp);
+
+ assert(_slideTextSpeed == NULL);
+ _slideTextSpeed = new RMOptionSlide(RMPoint(165, 122), 10, GLOBALS._nCfgTextSpeed);
+ assert(_slideTonySpeed == NULL);
+ _slideTonySpeed = new RMOptionSlide(RMPoint(165, 226), 5, GLOBALS._nCfgTonySpeed);
+ }
+ // Menu Graphics
+ else if (_nState == MENUGFX) {
+ assert(_buttonGfx_Anni30 == NULL);
+ _buttonGfx_Anni30 = new RMOptionButton(20015, RMPoint(247, 178), true);
+ _buttonGfx_Anni30->setActiveState(GLOBALS._bCfgAnni30);
+ assert(_buttonGfx_AntiAlias == NULL);
+ _buttonGfx_AntiAlias = new RMOptionButton(20016, RMPoint(430, 83), true);
+ _buttonGfx_AntiAlias->setActiveState(!GLOBALS._bCfgAntiAlias);
+ assert(_buttonGfx_Sottotitoli == NULL);
+ _buttonGfx_Sottotitoli = new RMOptionButton(20017, RMPoint(98, 82), true);
+ _buttonGfx_Sottotitoli->setActiveState(!GLOBALS._bShowSubtitles);
+ assert(_buttonGfx_Tips == NULL);
+ _buttonGfx_Tips = new RMOptionButton(20018, RMPoint(431, 246), true);
+ _buttonGfx_Tips->setActiveState(GLOBALS._bCfgInterTips);
+ assert(_buttonGfx_Trans == NULL);
+ _buttonGfx_Trans = new RMOptionButton(20019, RMPoint(126, 271), true);
+ _buttonGfx_Trans->setActiveState(!GLOBALS._bCfgTransparence);
+
+ } else if (_nState == MENUSOUND) {
+ assert(_sliderSound_Dubbing == NULL);
+ _sliderSound_Dubbing = new RMOptionSlide(RMPoint(165, 122), 10, GLOBALS._nCfgDubbingVolume);
+ assert(_sliderSound_Music == NULL);
+ _sliderSound_Music = new RMOptionSlide(RMPoint(165, 226), 10, GLOBALS._nCfgMusicVolume);
+ assert(_sliderSound_SFX == NULL);
+ _sliderSound_SFX = new RMOptionSlide(RMPoint(165, 330), 10, GLOBALS._nCfgSFXVolume);
+
+ assert(_buttonSound_DubbingOn == NULL);
+ _buttonSound_DubbingOn = new RMOptionButton(20033, RMPoint(339, 75), true);
+ _buttonSound_DubbingOn->setActiveState(GLOBALS._bCfgDubbing);
+ assert(_buttonSound_MusicOn == NULL);
+ _buttonSound_MusicOn = new RMOptionButton(20034, RMPoint(338, 179), true);
+ _buttonSound_MusicOn->setActiveState(GLOBALS._bCfgMusic);
+ assert(_buttonSound_SFXOn == NULL);
+ _buttonSound_SFXOn = new RMOptionButton(20035, RMPoint(338, 283), true);
+ _buttonSound_SFXOn->setActiveState(GLOBALS._bCfgSFX);
+ }
+ }
+
+ CORO_INVOKE_0(refreshAll);
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::closeState() {
+ delete _menu;
+ _menu = NULL;
+
+ delete _buttonExit;
+ _buttonExit = NULL;
+
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ if (_curThumb[i] != NULL) {
+ delete _curThumb[i];
+ _curThumb[i] = NULL;
+ }
+
+ delete _buttonSave_States[i];
+ _buttonSave_States[i] = NULL;
+ }
+
+ delete _buttonSave_ArrowLeft;
+ _buttonSave_ArrowLeft = NULL;
+ delete _buttonSave_ArrowRight;
+ _buttonSave_ArrowRight = NULL;
+
+ delete _saveEasy;
+ _saveEasy = NULL;
+ delete _saveHard;
+ _saveHard = NULL;
+ }
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ delete _buttonQuit;
+ _buttonQuit = NULL;
+ delete _buttonLoad;
+ _buttonLoad = NULL;
+ delete _buttonSave;
+ _buttonSave = NULL;
+ delete _buttonGameMenu;
+ _buttonGameMenu = NULL;
+ delete _buttonGfxMenu;
+ _buttonGfxMenu = NULL;
+ delete _buttonSoundMenu;
+ _buttonSoundMenu = NULL;
+ delete _quitConfirm;
+ _quitConfirm = NULL;
+ delete _buttonQuitYes;
+ _buttonQuitYes = NULL;
+ delete _buttonQuitNo;
+ _buttonQuitNo = NULL;
+
+ if (_bNoLoadSave) {
+ delete _hideLoadSave;
+ _hideLoadSave = NULL;
+ }
+
+ if (_nState == MENUGAME) {
+ GLOBALS._bCfgInvLocked = _buttonGame_Lock->isActive();
+ delete _buttonGame_Lock;
+ _buttonGame_Lock = NULL;
+
+ GLOBALS._bCfgTimerizedText = !_buttonGame_TimerizedText->isActive();
+ delete _buttonGame_TimerizedText;
+ _buttonGame_TimerizedText = NULL;
+
+ GLOBALS._bCfgInvNoScroll = _buttonGame_Scrolling->isActive();
+ delete _buttonGame_Scrolling;
+ _buttonGame_Scrolling = NULL;
+
+ GLOBALS._bCfgInvUp = _buttonGame_InterUp->isActive();
+ delete _buttonGame_InterUp;
+ _buttonGame_InterUp = NULL;
+
+ GLOBALS._nCfgTextSpeed = _slideTextSpeed->getValue();
+ delete _slideTextSpeed;
+ _slideTextSpeed = NULL;
+
+ GLOBALS._nCfgTonySpeed = _slideTonySpeed->getValue();
+ delete _slideTonySpeed;
+ _slideTonySpeed = NULL;
+ } else if (_nState == MENUGFX) {
+ GLOBALS._bCfgAnni30 = _buttonGfx_Anni30->isActive();
+ delete _buttonGfx_Anni30;
+ _buttonGfx_Anni30 = NULL;
+
+ GLOBALS._bCfgAntiAlias = !_buttonGfx_AntiAlias->isActive();
+ delete _buttonGfx_AntiAlias;
+ _buttonGfx_AntiAlias = NULL;
+
+ GLOBALS._bShowSubtitles = !_buttonGfx_Sottotitoli->isActive();
+ delete _buttonGfx_Sottotitoli;
+ _buttonGfx_Sottotitoli = NULL;
+
+ GLOBALS._bCfgInterTips = _buttonGfx_Tips->isActive();
+ delete _buttonGfx_Tips;
+ _buttonGfx_Tips = NULL;
+
+ GLOBALS._bCfgTransparence = !_buttonGfx_Trans->isActive();
+ delete _buttonGfx_Trans;
+ _buttonGfx_Trans = NULL;
+ } else if (_nState == MENUSOUND) {
+ GLOBALS._nCfgDubbingVolume = _sliderSound_Dubbing->getValue();
+ delete _sliderSound_Dubbing;
+ _sliderSound_Dubbing = NULL;
+
+ GLOBALS._nCfgMusicVolume = _sliderSound_Music->getValue();
+ delete _sliderSound_Music;
+ _sliderSound_Music = NULL;
+
+ GLOBALS._nCfgSFXVolume = _sliderSound_SFX->getValue();
+ delete _sliderSound_SFX;
+ _sliderSound_SFX = NULL;
+
+ GLOBALS._bCfgDubbing = _buttonSound_DubbingOn->isActive();
+ delete _buttonSound_DubbingOn;
+ _buttonSound_DubbingOn = NULL;
+
+ GLOBALS._bCfgMusic = _buttonSound_MusicOn->isActive();
+ delete _buttonSound_MusicOn;
+ _buttonSound_MusicOn = NULL;
+
+ GLOBALS._bCfgSFX = _buttonSound_SFXOn->isActive();
+ delete _buttonSound_SFXOn;
+ _buttonSound_SFXOn = NULL;
+ }
+
+ // Save the new settings to ScummVM
+ g_vm->saveSoundSettings();
+ }
+
+ _nState = MENUNONE;
+}
+
+void RMOptionScreen::reInit(RMGfxTargetBuffer &bigBuf) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+}
+
+void RMOptionScreen::init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = false;
+ _bNoLoadSave = false;
+ _bAlterGfx = false;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ if (_nState == MENULOAD || _nState == MENUSAVE || _nState == MENUNONE)
+ _nState = MENUGAME;
+
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = true;
+ _bNoLoadSave = false;
+ _bAlterGfx = bAlternateGfx;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _nState = MENULOAD;
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = true;
+ _bNoLoadSave = false;
+ _bAlterGfx = bAlternateGfx;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _nState = MENUSAVE;
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_fadeStep != 0) {
+ result = false;
+ return;
+ }
+
+ _fadeStep = 1;
+ _fadeY = -20;
+ _fadeTime = -1;
+ _bExit = false;
+ _bLoadMenuOnly = false;
+ _bNoLoadSave = true;
+
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _nState = MENUGAME;
+ CORO_INVOKE_0(initState);
+
+ result = true;
+
+ CORO_END_CODE;
+}
+
+bool RMOptionScreen::close() {
+ if (_fadeStep != 6)
+ return false;
+
+ // Start fade out
+ _fadeStep++;
+ _fadeTime = g_vm->getTime();
+ return true;
+}
+
+bool RMOptionScreen::isClosing() {
+ return _bExit;
+}
+
+int RMOptionScreen::priority() {
+ // Just below the mouse
+ return 190;
+}
+
+void RMOptionScreen::changeState(CORO_PARAM, OptionScreenState newState) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _nLastState = _nState;
+ closeState();
+ _nState = newState;
+ CORO_INVOKE_0(initState);
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::doFrame(CORO_PARAM, RMInput *input) {
+ CORO_BEGIN_CONTEXT;
+ bool bLeftClick, bRightClick;
+ RMPoint mousePos;
+ bool bRefresh;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+
+ // If it is fully open, do nothing
+ if (_fadeStep != 6)
+ return;
+
+ // Reads input
+ _ctx->mousePos = input->mousePos();
+ _ctx->bLeftClick = input->mouseLeftClicked();
+ _ctx->bRightClick = input->mouseRightClicked();
+
+ _ctx->bRefresh = false;
+
+ if (_bQuitConfirm) {
+ _ctx->bRefresh |= _buttonQuitYes->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonQuitNo->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ } else {
+ _ctx->bRefresh |= _buttonExit->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ // Check if you have clicked on the output
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ // Buttons without graphics...
+ _buttonGameMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _buttonGfxMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _buttonSoundMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ // Buttons with graphics
+ if (!_bNoLoadSave) {
+ if (!g_vm->getIsDemo()) {
+ _ctx->bRefresh |= _buttonLoad->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSave->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ }
+
+ _ctx->bRefresh |= _buttonQuit->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ }
+ }
+
+ if (_nState == MENUGAME) {
+ _ctx->bRefresh |= _buttonGame_Lock->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGame_TimerizedText->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGame_Scrolling->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGame_InterUp->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _slideTextSpeed->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _slideTonySpeed->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ } else if (_nState == MENUGFX) {
+ _ctx->bRefresh |= _buttonGfx_Anni30->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_AntiAlias->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_Sottotitoli->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_Tips->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonGfx_Trans->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ } else if (_nState == MENUSOUND) {
+ _ctx->bRefresh |= _sliderSound_Dubbing->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _sliderSound_Music->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _sliderSound_SFX->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSound_DubbingOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSound_MusicOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ _ctx->bRefresh |= _buttonSound_SFXOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ } else if (_nState == MENULOAD || _nState == MENUSAVE) {
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++)
+ _buttonSave_States[_ctx->i]->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+
+ if (_statePos > 0)
+ _ctx->bRefresh |= _buttonSave_ArrowLeft->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ if (_statePos < 90)
+ _ctx->bRefresh |= _buttonSave_ArrowRight->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick);
+ }
+ }
+
+#define KEYPRESS(c) (g_vm->getEngine()->getInput().getAsyncKeyState(c))
+#define PROCESS_CHAR(cod, c) if (KEYPRESS(cod)) { \
+ _editName[strlen(_editName) + 1] = '\0'; _editName[strlen(_editName)] = c; _ctx->bRefresh = true; }
+
+ // State Buttons
+ if (_bEditSaveName) {
+ if (KEYPRESS(Common::KEYCODE_BACKSPACE)) {
+ if (_editName[0] != '\0') {
+ _editName[strlen(_editName) - 1] = '\0';
+ _ctx->bRefresh = true;
+ }
+ }
+
+ for (_ctx->i = 0; _ctx->i < 26 && strlen(_editName) < 12; _ctx->i++) {
+ if (KEYPRESS(Common::KEYCODE_LSHIFT) ||
+ KEYPRESS(Common::KEYCODE_RSHIFT)) {
+ PROCESS_CHAR((Common::KeyCode)((int)'a' + _ctx->i), _ctx->i + 'A');
+ } else {
+ PROCESS_CHAR((Common::KeyCode)((int)'a' + _ctx->i), _ctx->i + 'a');
+ }
+ }
+
+ for (_ctx->i = 0; _ctx->i < 10 && strlen(_editName) < 12; _ctx->i++)
+ PROCESS_CHAR((Common::KeyCode)((int)'0' + _ctx->i), _ctx->i + '0');
+
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_SPACE, ' ');
+
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP0, '0');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP1, '1');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP2, '2');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP3, '3');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP4, '4');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP5, '5');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP6, '6');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP7, '7');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP8, '8');
+ if (strlen(_editName) < 12)
+ PROCESS_CHAR(Common::KEYCODE_KP9, '9');
+
+ // Cancel
+ if (KEYPRESS(Common::KEYCODE_ESCAPE)) {
+ _bEditSaveName = false;
+ _ctx->bRefresh = true;
+ }
+
+ // OK
+ if (KEYPRESS(Common::KEYCODE_RETURN)) {
+ _bEditSaveName = false;
+ g_vm->saveState(_statePos + _nEditPos, _editName);
+ close();
+ }
+
+ } else if (_ctx->bLeftClick) {
+ if (_nState == MENULOAD || _nState == MENUSAVE) {
+ if (_buttonExit->isActive()) {
+ if (_bLoadMenuOnly) {
+ // If only the loading menu, close
+ close();
+ } else {
+ CORO_INVOKE_1(changeState, _nLastState);
+ _ctx->bRefresh = true;
+ }
+ } else if (_buttonSave_ArrowLeft->isActive()) {
+ if (_statePos > 0) {
+ _statePos -= 6;
+ if (_statePos < 0)
+ _statePos = 0;
+ _buttonSave_ArrowLeft->setActiveState(false);
+ _ctx->bRefresh = true;
+ refreshThumbnails();
+ }
+ } else if (_buttonSave_ArrowRight->isActive()) {
+ if (_statePos < 90) {
+ _statePos += 6;
+ if (_statePos > 90)
+ _statePos = 90;
+ _buttonSave_ArrowRight->setActiveState(false);
+ _ctx->bRefresh = true;
+ refreshThumbnails();
+ }
+ } else {
+ for (_ctx->i = 0; _ctx->i < 6; _ctx->i++)
+ if (_buttonSave_States[_ctx->i]->isActive()) {
+ // There by saving or loading!!!
+ if (_nState == MENULOAD && _curThumb[_ctx->i] != NULL) {
+ // Loading
+ CORO_INVOKE_1(g_vm->loadState, _statePos + _ctx->i);
+ close();
+ } else if (_nState == MENUSAVE && (_statePos != 0 || _ctx->i != 0)) {
+ // Turn on edit mode
+ _bEditSaveName = true;
+ _nEditPos = _ctx->i;
+ strcpy(_editName, _curThumbName[_ctx->i].c_str());
+ _ctx->bRefresh = true;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ if (_bQuitConfirm) {
+ if (_buttonQuitNo->isActive()) {
+ _bQuitConfirm = false;
+ _ctx->bRefresh = true;
+ } else if (_buttonQuitYes->isActive()) {
+ _bQuitConfirm = false;
+ _ctx->bRefresh = true;
+
+ g_vm->quitGame();
+ }
+ } else {
+ if (_buttonQuit->isActive()) {
+ _bQuitConfirm = true;
+ _buttonQuitNo->setActiveState(false);
+ _buttonQuitYes->setActiveState(false);
+ _ctx->bRefresh = true;
+ } else if (_buttonExit->isActive())
+ close();
+ else if (_buttonLoad->isActive()) {
+ CORO_INVOKE_1(changeState, MENULOAD);
+ _ctx->bRefresh = true;
+ } else if (_buttonSave->isActive()) {
+ CORO_INVOKE_1(changeState, MENUSAVE);
+ _ctx->bRefresh = true;
+ } else if (_buttonGameMenu->isActive() && _nState != MENUGAME) {
+ CORO_INVOKE_1(changeState, MENUGAME);
+ _ctx->bRefresh = true;
+ } else if (_buttonGfxMenu->isActive() && _nState != MENUGFX) {
+ CORO_INVOKE_1(changeState, MENUGFX);
+ _ctx->bRefresh = true;
+ } else if (_buttonSoundMenu->isActive() && _nState != MENUSOUND) {
+ CORO_INVOKE_1(changeState, MENUSOUND);
+ _ctx->bRefresh = true;
+ }
+
+ if (_nState == MENUGFX) {
+ // These options take effect immediately
+ if (_buttonGfx_Anni30->isActive())
+ GLOBALS._bCfgAnni30 = true;
+ else
+ GLOBALS._bCfgAnni30 = false;
+
+ if (_buttonGfx_AntiAlias->isActive())
+ GLOBALS._bCfgAntiAlias = false;
+ else
+ GLOBALS._bCfgAntiAlias = true;
+
+ if (_buttonGfx_Trans->isActive())
+ GLOBALS._bCfgTransparence = false;
+ else
+ GLOBALS._bCfgTransparence = true;
+ }
+ }
+ }
+ }
+
+ if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) {
+ if (!_bQuitConfirm && KEYPRESS(Common::KEYCODE_ESCAPE))
+ close();
+ }
+
+ if (_ctx->bRefresh)
+ CORO_INVOKE_0(refreshAll);
+
+ CORO_END_CODE;
+}
+
+
+void RMOptionScreen::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int curTime;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->curTime = g_vm->getTime();
+
+#define FADE_SPEED 20
+#define SYNC (_ctx->curTime - _fadeTime) / 25
+
+ if (_bExit)
+ return;
+
+ if (_fadeStep == 1) {
+ // Downhill fast
+ if (_fadeTime == -1)
+ _fadeY += FADE_SPEED;
+ else
+ _fadeY += FADE_SPEED * SYNC;
+ if (_fadeY > 480) {
+ _fadeY = 480;
+ _fadeStep++;
+ }
+
+ // Set the part to draw the scrolling
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 2) {
+ // Bounce 1
+ _fadeY -= FADE_SPEED / 2 * SYNC;
+ if (_fadeY < 400) {
+ _fadeY = 400;
+ _fadeStep++;
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 3) {
+ _fadeY -= FADE_SPEED / 4 * SYNC;
+ if (_fadeY < 380) {
+ _fadeY = 380;
+ _fadeStep++;
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 4) {
+ // Bounce 1 - 2
+ _fadeY += FADE_SPEED / 3 * SYNC;
+ if (_fadeY > 420) {
+ _fadeY = 420;
+ _fadeStep++;
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 5) {
+ _fadeY += FADE_SPEED / 2 * SYNC;
+ if (_fadeY > 480) {
+ _fadeY = 480;
+ _fadeStep++;
+ g_vm->hideLocation();
+ }
+
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 6) {
+ // Menu ON
+
+ } else if (_fadeStep == 7) {
+ // Menu OFF
+ g_vm->showLocation();
+ _fadeStep++;
+
+ } else if (_fadeStep == 8) {
+ _fadeY -= FADE_SPEED * SYNC;
+ if (_fadeY < 0) {
+ _fadeY = 0;
+ _fadeStep++;
+ }
+ prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480));
+
+ } else if (_fadeStep == 9) {
+ // Hello hello!
+ _bExit = true;
+ _fadeStep = 0;
+
+ // Free memory
+ closeState();
+ return;
+
+ } else {
+ _fadeStep = 0;
+ }
+
+ _fadeTime = _ctx->curTime;
+
+ CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+void RMOptionScreen::removeThis(CORO_PARAM, bool &result) {
+ if (_bExit)
+ result = true;
+ else
+ result = false;
+}
+
+
+bool RMOptionScreen::loadThumbnailFromSaveState(int nState, byte *lpDestBuf, Common::String &name, byte &diff) {
+ Common::String buf;
+ char namebuf[256];
+ int i;
+ Common::InSaveFile *f;
+ char id[4];
+
+ // Cleans the destination
+ Common::fill(lpDestBuf, lpDestBuf + 160 * 120 * 2, 0);
+ name = "No name";
+ diff = 10;
+
+ // Get the savegame filename for the given slot
+ buf = g_vm->getSaveStateFileName(nState);
+
+ // Try and open the savegame
+ f = g_system->getSavefileManager()->openForLoading(buf);
+ if (f == NULL)
+ return false;
+
+ // Check to see if the file has a valid header
+ f->read(id, 4);
+ if (id[0] != 'R' || id[1] != 'M' || id[2] != 'S') {
+ delete f;
+ return false;
+ }
+
+ if (id[3] < 0x3) {
+ // Very old version that doesn't have screenshots
+ delete f;
+ return true;
+ }
+
+ // Load the screenshot
+ if ((id[3] >= 0x5) && (id[3] < 0x8)) {
+ // Read it as an LZO compressed data block
+ byte *cmpbuf;
+ uint32 cmpsize, size;
+
+ cmpbuf = new byte[160 * 120 * 4];
+
+ // Read in the compressed data
+ cmpsize = f->readUint32LE();
+ f->read(cmpbuf, cmpsize);
+
+ lzo1x_decompress(cmpbuf, cmpsize, lpDestBuf, &size);
+
+ delete[] cmpbuf;
+ } else {
+ // Read in the screenshot as an uncompressed data block
+ if (id[3] >= 8)
+ // Recent versions use hardcoded 160x120 uncomrpessed data, so size can be skipped
+ f->skip(4);
+
+ f->read(lpDestBuf, 160 * 120 * 2);
+ }
+
+ if (id[3] >= 0x5) {
+ // Read in the difficulty level
+ diff = f->readByte();
+ }
+
+ if (id[3] < 0x4) {
+ // Savegame version doesn't have a stored name
+ delete f;
+ return true;
+ }
+
+ i = f->readByte();
+ f->read(namebuf, i);
+ namebuf[i] = '\0';
+ name = namebuf;
+
+ delete f;
+ return true;
+}
+
+/****************************************************************************\
+* RMPointer Methods
+\****************************************************************************/
+
+RMPointer::RMPointer() {
+ Common::fill(_pointer, _pointer + 16, (RMGfxSourceBuffer8 *)NULL);
+ Common::fill(_specialPointer, _specialPointer + 16, (RMItem *)NULL);
+
+ _nCurPointer = _nCurSpecialPointer = 0;
+ _nCurCustomPointer = NULL;
+}
+
+RMPointer::~RMPointer() {
+ close();
+}
+
+void RMPointer::init() {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ RMResRaw res(RES_P_GO + i);
+
+ _pointer[i] = new RMGfxSourceBuffer8RLEByteAA;
+ _pointer[i]->init(res, res.width(), res.height(), false);
+ _pointer[i]->loadPaletteWA(RES_P_PAL);
+ }
+
+ for (i = 0; i < 5; i++) {
+ RMRes res(RES_P_PAP1 + i);
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _specialPointer[i] = new RMItem;
+ _specialPointer[i]->readFromStream(*ds);
+ delete ds;
+ }
+
+ //m_hotspot[0].set(19,5);
+ _hotspot[0].set(5, 1);
+ _hotspot[1].set(32, 28);
+ _hotspot[2].set(45, 23);
+ _hotspot[3].set(35, 25);
+ _hotspot[4].set(32, 28);
+
+ // Default=GO
+ _nCurPointer = 0;
+ _nCurSpecialPointer = 0;
+}
+
+void RMPointer::close() {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ if (_pointer[i] != NULL) {
+ delete _pointer[i];
+ _pointer[i] = NULL;
+ }
+
+ if (_specialPointer[i] != NULL) {
+ delete _specialPointer[i];
+ _specialPointer[i] = NULL;
+ }
+ }
+}
+
+void RMPointer::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int n;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Check the pointer
+ _ctx->n = _nCurPointer;
+ if (_ctx->n == TA_COMBINE)
+ _ctx->n = TA_USE;
+
+ _cursorHotspot = _hotspot[_ctx->n];
+
+ // Call the Draw method of the pointer
+ if (_nCurSpecialPointer == 0) {
+ // WORKAROUND: updateCursor gets called too early sometimes (for example, when
+ // the cursor is released over the TA_PERORATE option), via setAction.
+ if (_ctx->n > 4)
+ _ctx->n = 0;
+
+ CORO_INVOKE_2(_pointer[_ctx->n]->draw, bigBuf, prim);
+ } else {
+ if (_nCurSpecialPointer == PTR_CUSTOM)
+ CORO_INVOKE_2(_nCurCustomPointer->draw, bigBuf, prim);
+ else
+ // Call the draw on the special pointer
+ CORO_INVOKE_2(_specialPointer[_nCurSpecialPointer - 1]->draw, bigBuf, prim);
+ }
+
+ CORO_END_CODE;
+}
+
+int RMPointer::curAction() {
+ if (_nCurSpecialPointer != 0)
+ return 0;
+
+ return _nCurPointer;
+}
+
+/**
+ * Show the cursor
+ */
+void RMPointer::showCursor() {
+ if (!CursorMan.isVisible()) {
+ CursorMan.showMouse(true);
+
+ updateCursor();
+ }
+}
+
+/**
+ * Hide the cursor
+ */
+void RMPointer::hideCursor() {
+ if (CursorMan.isVisible()) {
+ CursorMan.showMouse(false);
+ }
+}
+
+void RMPointer::doFrame() {
+ // Update the cursor animation if needed.
+ if (_nCurSpecialPointer == 0 || _nCurSpecialPointer == PTR_CUSTOM)
+ return;
+
+ RMGfxTargetBuffer buf;
+ if (_specialPointer[_nCurSpecialPointer - 1]->doFrame(&buf, false))
+ updateCursor();
+}
+
+void RMPointer::updateCursor() {
+ // Create an intermediate buffer and draw the cursor onto it
+ RMGfxTargetBuffer buf;
+ buf.create(64, 64, 16);
+ RMGfxPrimitive prim;
+
+ draw(Common::nullContext, buf, &prim);
+
+ // Get a pointer to the cursor data
+ byte *cursorData = buf;
+
+ // If in black & white mode, convert the cursor
+ if (GLOBALS._bCfgAnni30) {
+ if (!RMGfxTargetBuffer::_precalcTable) {
+ RMGfxTargetBuffer::createBWPrecalcTable();
+ }
+ uint16 *src = (uint16 *)cursorData;
+ for (int i = 0; i < 64; i++) {
+ uint16 *lineP = src;
+ for (int j = 0; j < 64; j++) {
+ lineP[j] = RMGfxTargetBuffer::_precalcTable[lineP[j] & 0x7FFF];
+ }
+ src += 64;
+ }
+ }
+
+ // Get the raw pixel data and set the cursor to it
+ Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ CursorMan.replaceCursor(cursorData, 64, 64, _cursorHotspot._x, _cursorHotspot._y, 0, 1, &pixelFormat);
+}
+
+/**
+ * Sets a new action as current
+ */
+void RMPointer::setAction(RMTonyAction action) {
+ _nCurPointer = action;
+ updateCursor();
+}
+
+/**
+ * Sets a new pointer
+ */
+void RMPointer::setSpecialPointer(PointerType ptr) {
+ _nCurSpecialPointer = ptr;
+ if (_nCurSpecialPointer && _nCurSpecialPointer != PTR_CUSTOM)
+ _specialPointer[ptr - 1]->setPattern(1);
+
+ updateCursor();
+}
+
+RMPointer::PointerType RMPointer::getSpecialPointer() {
+ return (PointerType)_nCurSpecialPointer;
+}
+
+/**
+ * Set the new custom pointer
+ */
+void RMPointer::setCustomPointer(RMGfxSourceBuffer8 *ptr) {
+ _nCurCustomPointer = ptr;
+ updateCursor();
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/game.h b/engines/tony/game.h
new file mode 100644
index 0000000000..83a1ddaea1
--- /dev/null
+++ b/engines/tony/game.h
@@ -0,0 +1,340 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_GAME_H
+#define TONY_GAME_H
+
+#include "tony/gfxcore.h"
+#include "tony/input.h"
+#include "tony/loc.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+#define INIT_GFX16_FROMRAW(dwRes, buf16) \
+ raw = new RMResRaw(dwRes); \
+ assert(raw->isValid()); \
+ assert((buf16) == NULL); \
+ (buf16) = new RMGfxSourceBuffer16(false); \
+ (buf16)->init(*raw, raw->width(), raw->height()); \
+ delete raw;
+
+#define INIT_GFX8_FROMRAW(raw, dwRes, buf8) \
+ raw = new RMResRaw(dwRes); \
+ assert(raw->isValid()); \
+ assert((buf8) == NULL); \
+ (buf8) = new RMGfxSourceBuffer8RLEByte(); \
+ (buf8)->init(*raw, raw->width(), raw->height(), true); \
+ delete raw;
+
+// X & Y dimensions of the adventure
+#define RM_SX 640
+#define RM_SY 480
+
+// X & Y dimensions of bigbuf
+#define RM_BBX (RM_SX)
+#define RM_BBY (RM_SY)
+
+// Skipping X & Y
+#define RM_SKIPY ((RM_BBY - RM_SY) / 2)
+#define RM_SKIPX 0
+
+// Tony's actions
+enum RMTonyAction {
+ TA_GOTO = 0,
+ TA_TAKE,
+ TA_USE,
+ TA_EXAMINE,
+ TA_TALK,
+ TA_PERORATE,
+
+ TA_COMBINE = 10,
+ TA_RECEIVECOMBINE,
+ TA_COMBINEGIVE,
+ TA_RECEIVECOMBINEGIVE
+};
+
+// Global Functions
+void mainEnableGUI();
+void mainDisableGUI();
+
+// Classes
+class RMPointer {
+public:
+ enum PointerType {
+ PTR_NONE = 0,
+ PTR_ARROWUP,
+ PTR_ARROWDOWN,
+ PTR_ARROWLEFT,
+ PTR_ARROWRIGHT,
+ PTR_ARROWMAP,
+ PTR_CUSTOM
+ };
+
+private:
+ RMGfxSourceBuffer8 *_pointer[16];
+ RMPoint _hotspot[16];
+ RMPoint _cursorHotspot;
+
+ RMItem *_specialPointer[16];
+
+ int _nCurPointer;
+ int _nCurSpecialPointer;
+
+ RMGfxSourceBuffer8 *_nCurCustomPointer;
+
+public:
+ /**
+ * Constructor & destructor
+ */
+ RMPointer();
+ virtual ~RMPointer();
+
+ /**
+ * Initialization
+ */
+ void init();
+
+ /**
+ * Deinitialization
+ */
+ void close();
+
+ /**
+ * Process a frame
+ */
+ void doFrame();
+
+ /**
+ * draw method
+ */
+ void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ /**
+ * Sets a new action as current
+ */
+ void setAction(RMTonyAction action);
+
+ /**
+ * Sets a new pointer
+ */
+ void setSpecialPointer(PointerType ptr);
+
+ PointerType getSpecialPointer();
+
+ /**
+ * Set the new custom pointer
+ */
+ void setCustomPointer(RMGfxSourceBuffer8 *ptr);
+
+ /**
+ * Return the current action to be applied according to the pointer
+ */
+ int curAction();
+
+ /**
+ * Update the cursor
+ */
+ void updateCursor();
+
+ /**
+ * Show the cursor
+ */
+ void showCursor();
+
+ /**
+ * Hide the cursor
+ */
+ void hideCursor();
+};
+
+class RMOptionButton: public RMGfxTaskSetPrior {
+public:
+ RMRect _rect;
+ RMGfxSourceBuffer16 *_buf;
+ bool _bActive;
+ bool _bHasGfx;
+ bool _bDoubleState;
+
+public:
+ RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState = false);
+ RMOptionButton(const RMRect &pt);
+ virtual ~RMOptionButton();
+
+ bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void addToList(RMGfxTargetBuffer &bigBuf);
+ bool isActive();
+ void setActiveState(bool bState);
+};
+
+class RMOptionSlide : public RMGfxTaskSetPrior {
+private:
+ RMOptionButton *_pushLeft;
+ RMOptionButton *_pushRight;
+ RMGfxSourceBuffer16 *_sliderCenter;
+ RMGfxSourceBuffer16 *_sliderLeft;
+ RMGfxSourceBuffer16 *_sliderRight;
+ RMGfxSourceBuffer16 *_sliderSingle;
+ int _nSlideSize;
+ RMPoint _pos;
+ int _nValue;
+ int _nMax;
+ int _nStep;
+
+public:
+ RMOptionSlide(const RMPoint &pt, int m_nRange = 100, int m_nStartValue = 0, int slideSize = 300);
+ virtual ~RMOptionSlide();
+
+ bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void addToList(RMGfxTargetBuffer &bigBuf);
+
+ int getValue();
+};
+
+class RMOptionScreen : public RMGfxWoodyBuffer {
+private:
+ RMGfxSourceBuffer16 *_menu;
+ RMGfxSourceBuffer16 *_quitConfirm;
+ RMGfxSourceBuffer16 *_hideLoadSave;
+ RMOptionButton *_buttonQuitYes;
+ RMOptionButton *_buttonQuitNo;
+ RMOptionButton *_buttonExit;
+ RMOptionButton *_buttonQuit;
+ RMOptionButton *_buttonLoad;
+ RMOptionButton *_buttonSave;
+ RMOptionButton *_buttonGameMenu;
+ RMOptionButton *_buttonGfxMenu;
+ RMOptionButton *_buttonSoundMenu;
+ RMGfxSourceBuffer8 *_saveEasy;
+ RMGfxSourceBuffer8 *_saveHard;
+ RMGfxSourceBuffer16 *_curThumb[6];
+ Common::String _curThumbName[6];
+ byte _curThumbDiff[6];
+ RMOptionButton *_buttonSave_States[6];
+ RMOptionButton *_buttonSave_ArrowLeft;
+ RMOptionButton *_buttonSave_ArrowRight;
+ RMOptionButton *_buttonGfx_Tips;
+
+ RMOptionButton *_buttonSound_DubbingOn;
+ RMOptionButton *_buttonSound_MusicOn;
+ RMOptionButton *_buttonSound_SFXOn;
+
+ RMOptionSlide *_slideTonySpeed;
+ RMOptionSlide *_slideTextSpeed;
+
+
+ int _statePos;
+ bool _bEditSaveName;
+ int _nEditPos;
+ char _editName[256];
+
+ union {
+ RMOptionButton *_buttonGame_Lock;
+ RMOptionButton *_buttonGfx_Anni30;
+ RMOptionSlide *_sliderSound_Music;
+ };
+ union {
+ RMOptionButton *_buttonGame_TimerizedText;
+ RMOptionButton *_buttonGfx_AntiAlias;
+ RMOptionSlide *_sliderSound_SFX;
+ };
+ union {
+ RMOptionButton *_buttonGame_Scrolling;
+ RMOptionButton *_buttonGfx_Sottotitoli;
+ RMOptionSlide *_sliderSound_Dubbing;
+ };
+ union {
+ RMOptionButton *_buttonGame_InterUp;
+ RMOptionButton *_buttonGfx_Trans;
+ };
+
+ int _fadeStep;
+ bool _bExit;
+ bool _bQuitConfirm;
+ int _fadeY;
+ int _fadeTime;
+ bool _bLoadMenuOnly;
+ bool _bNoLoadSave;
+ bool _bAlterGfx;
+
+ enum OptionScreenState {
+ MENUGAME,
+ MENUGFX,
+ MENUSOUND,
+ MENULOAD,
+ MENUSAVE,
+ MENUNONE
+ };
+
+ OptionScreenState _nState;
+ OptionScreenState _nLastState;
+
+public:
+ RMOptionScreen();
+ virtual ~RMOptionScreen();
+
+ void init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result);
+ void initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result);
+ void initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result);
+ void initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result);
+ void reInit(RMGfxTargetBuffer &bigBuf);
+ bool close();
+ bool isClosing();
+
+ // Overloaded methods
+ virtual int priority();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ /**
+ * Polling for the option screen
+ */
+ void doFrame(CORO_PARAM, RMInput *m_input);
+
+ /**
+ * Retrieves a savegame's thumbnail, description, and difficulty level
+ */
+ static bool loadThumbnailFromSaveState(int numState, byte *lpDestBuf, Common::String &name, byte &diff);
+
+protected:
+
+ // Initialization and state change
+ void initState(CORO_PARAM);
+ void closeState();
+ void changeState(CORO_PARAM, OptionScreenState newState);
+
+ // Repaint the options menu
+ void refreshAll(CORO_PARAM);
+ void refreshThumbnails();
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp
new file mode 100644
index 0000000000..71bf31396c
--- /dev/null
+++ b/engines/tony/gfxcore.cpp
@@ -0,0 +1,2212 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/gfxengine.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* RMGfxTask Methods
+\****************************************************************************/
+
+RMGfxTask::RMGfxTask() {
+ _nPrior = 0;
+ _nInList = 0;
+}
+
+int RMGfxTask::priority() {
+ return _nPrior;
+}
+
+void RMGfxTask::removeThis(CORO_PARAM, bool &result) {
+ result = true;
+}
+
+/**
+ * Registration
+ */
+void RMGfxTask::Register() {
+ _nInList++;
+}
+
+void RMGfxTask::Unregister() {
+ _nInList--;
+ assert(_nInList >= 0);
+}
+
+/****************************************************************************\
+* RMGfxTaskSetPrior Methods
+\****************************************************************************/
+
+void RMGfxTaskSetPrior::setPriority(int nPrior) {
+ _nPrior = nPrior;
+}
+
+
+/****************************************************************************\
+* RMGfxBuffer Methods
+\****************************************************************************/
+
+RMGfxBuffer::RMGfxBuffer() {
+ _dimx = _dimy = 0;
+ _origBuf = _buf = NULL;
+}
+
+RMGfxBuffer::~RMGfxBuffer() {
+ destroy();
+}
+
+void RMGfxBuffer::create(int dimx, int dimy, int nBpp) {
+ // Destroy the buffer it is already exists
+ if (_buf != NULL)
+ destroy();
+
+ // Copy the parameters in the private members
+ _dimx = dimx;
+ _dimy = dimy;
+
+ // Allocate a buffer
+ _origBuf = _buf = new byte[_dimx * _dimy * nBpp / 8];
+ assert(_buf != NULL);
+ Common::fill(_origBuf, _origBuf + _dimx * _dimy * nBpp / 8, 0);
+}
+
+void RMGfxBuffer::destroy() {
+ if (_origBuf != NULL && _origBuf == _buf) {
+ delete[] _origBuf;
+ _origBuf = _buf = NULL;
+ }
+}
+
+void RMGfxBuffer::offsetY(int nLines, int nBpp) {
+ _buf += nLines * getDimx() * nBpp / 8;
+}
+
+
+RMGfxBuffer::operator byte *() {
+ return _buf;
+}
+
+RMGfxBuffer::operator void *() {
+ return (void *)_buf;
+}
+
+RMGfxBuffer::RMGfxBuffer(int dimx, int dimy, int nBpp) {
+ create(dimx, dimy, nBpp);
+}
+
+int RMGfxBuffer::getDimx() {
+ return _dimx;
+}
+
+int RMGfxBuffer::getDimy() {
+ return _dimy;
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer Methods
+\****************************************************************************/
+
+int RMGfxSourceBuffer::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ create(dimx, dimy, getBpp());
+ memcpy(_buf, buf, dimx * dimy * getBpp() / 8);
+
+ // Invokes the method for preparing the surface (inherited)
+ prepareImage();
+
+ return dimx * dimy * getBpp() / 8;
+}
+
+void RMGfxSourceBuffer::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ create(dimx, dimy, getBpp());
+ ds.read(_buf, dimx * dimy * getBpp() / 8);
+
+ // Invokes the method for preparing the surface (inherited)
+ prepareImage();
+}
+
+RMGfxSourceBuffer::~RMGfxSourceBuffer() {
+}
+
+void RMGfxSourceBuffer::prepareImage() {
+ // Do nothing. Can be overloaded if necessary
+}
+
+bool RMGfxSourceBuffer::clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf) {
+ int destw, desth;
+
+ destw = buf->getDimx();
+ desth = buf->getDimy();
+
+ if (!bUseSrc) {
+ u = v = 0;
+ width = _dimx;
+ height = _dimy;
+ }
+
+ if (x1 > destw - 1)
+ return false;
+
+ if (y1 > desth - 1)
+ return false;
+
+ if (x1 < 0) {
+ width += x1;
+ if (width < 0)
+ return false;
+ u -= x1;
+ x1 = 0;
+ }
+
+ if (y1 < 0) {
+ height += y1;
+ if (height < 0)
+ return false;
+ v -= y1;
+ y1 = 0;
+ }
+
+ if (x1 + width - 1 > destw - 1)
+ width = destw - x1;
+
+ if (y1 + height - 1 > desth - 1)
+ height = desth - y1;
+
+ return (width > 1 && height > 1);
+}
+
+/**
+ * Initializes a surface by resource Id
+ *
+ * @param resID Resource ID
+ * @param dimx Buffer X dimension
+ * @param dimy Buffer Y dimension
+ */
+int RMGfxSourceBuffer::init(uint32 resID, int dimx, int dimy, bool bLoadPalette) {
+ return init(RMRes(resID), dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer::offsetY(int nLines) {
+ RMGfxBuffer::offsetY(nLines, getBpp());
+}
+
+/****************************************************************************\
+* RMGfxWoodyBuffer Methods
+\****************************************************************************/
+
+RMGfxWoodyBuffer::~RMGfxWoodyBuffer() {
+
+}
+
+void RMGfxWoodyBuffer::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Draw the OT list
+ CORO_INVOKE_0(drawOT);
+
+ // Draw itself into the target buffer
+ CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+RMGfxWoodyBuffer::RMGfxWoodyBuffer() {
+
+}
+
+RMGfxWoodyBuffer::RMGfxWoodyBuffer(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 16) {
+}
+
+/****************************************************************************\
+* RMGfxTargetBuffer Methods
+\****************************************************************************/
+
+RMGfxTargetBuffer::RMGfxTargetBuffer() {
+ _otlist = NULL;
+ _otSize = 0;
+ _trackDirtyRects = false;
+}
+
+RMGfxTargetBuffer::~RMGfxTargetBuffer() {
+ clearOT();
+}
+
+void RMGfxTargetBuffer::clearOT() {
+ OTList *cur, *n;
+
+ cur = _otlist;
+
+ while (cur != NULL) {
+ cur->_prim->_task->Unregister();
+ delete cur->_prim;
+ n = cur->_next;
+ delete cur;
+ cur = n;
+ }
+
+ _otlist = NULL;
+}
+
+void RMGfxTargetBuffer::drawOT(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ OTList *cur;
+ OTList *prev;
+ OTList *next;
+ RMGfxPrimitive *myprim;
+ bool result;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->prev = NULL;
+ _ctx->cur = _otlist;
+
+ while (_ctx->cur != NULL) {
+ // Call the task Draw method, passing it a copy of the original
+ _ctx->myprim = _ctx->cur->_prim->duplicate();
+ CORO_INVOKE_2(_ctx->cur->_prim->_task->draw, *this, _ctx->myprim);
+ delete _ctx->myprim;
+
+ // Check if it's time to remove the task from the OT list
+ CORO_INVOKE_1(_ctx->cur->_prim->_task->removeThis, _ctx->result);
+ if (_ctx->result) {
+ // De-register the task
+ _ctx->cur->_prim->_task->Unregister();
+
+ // Delete task, freeing the memory
+ delete _ctx->cur->_prim;
+ _ctx->next = _ctx->cur->_next;
+ delete _ctx->cur;
+
+ // If it was the first item, update the list head
+ if (_ctx->prev == NULL)
+ _otlist = _ctx->next;
+ // Otherwise update the next pinter of the previous item
+ else
+ _ctx->prev->_next = _ctx->next;
+
+ _ctx->cur = _ctx->next;
+ } else {
+ // Update the pointer to the previous item, and the current to the next
+ _ctx->prev = _ctx->cur;
+ _ctx->cur = _ctx->cur->_next;
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMGfxTargetBuffer::addPrim(RMGfxPrimitive *prim) {
+ int nPrior;
+ OTList *cur, *n;
+
+ // Warn of the OT listing
+ prim->_task->Register();
+
+ // Check the priority
+ nPrior = prim->_task->priority();
+ n = new OTList(prim);
+
+ // Empty list
+ if (_otlist == NULL) {
+ _otlist = n;
+ _otlist->_next = NULL;
+ }
+ // Inclusion in the head
+ else if (nPrior < _otlist->_prim->_task->priority()) {
+ n->_next = _otlist;
+ _otlist = n;
+ } else {
+ cur = _otlist;
+ while (cur->_next != NULL && nPrior > cur->_next->_prim->_task->priority())
+ cur = cur->_next;
+
+ n->_next = cur->_next;
+ cur->_next = n;
+ }
+}
+
+void RMGfxTargetBuffer::addDirtyRect(const Common::Rect &r) {
+ assert(r.isValidRect());
+ if (_trackDirtyRects && r.width() > 0 && r.height() > 0)
+ _currentDirtyRects.push_back(r);
+}
+
+Common::List<Common::Rect> &RMGfxTargetBuffer::getDirtyRects() {
+ // Copy rects from both the current and previous frame into the output dirty rects list
+ Common::List<Common::Rect>::iterator i;
+ _dirtyRects.clear();
+ for (i = _previousDirtyRects.begin(); i != _previousDirtyRects.end(); ++i)
+ _dirtyRects.push_back(*i);
+ for (i = _currentDirtyRects.begin(); i != _currentDirtyRects.end(); ++i)
+ _dirtyRects.push_back(*i);
+
+ mergeDirtyRects();
+ return _dirtyRects;
+}
+
+/**
+ * Move the set of dirty rects from the finished current frame into the previous frame list.
+ */
+void RMGfxTargetBuffer::clearDirtyRects() {
+ Common::List<Common::Rect>::iterator i;
+ _previousDirtyRects.clear();
+ for (i = _currentDirtyRects.begin(); i != _currentDirtyRects.end(); ++i)
+ _previousDirtyRects.push_back(*i);
+
+ _currentDirtyRects.clear();
+}
+
+/**
+ * Merges any clipping rectangles that overlap to try and reduce
+ * the total number of clip rectangles.
+ */
+void RMGfxTargetBuffer::mergeDirtyRects() {
+ if (_dirtyRects.size() <= 1)
+ return;
+
+ Common::List<Common::Rect>::iterator rOuter, rInner;
+
+ for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) {
+ rInner = rOuter;
+ while (++rInner != _dirtyRects.end()) {
+
+ if ((*rOuter).intersects(*rInner)) {
+ // these two rectangles overlap or
+ // are next to each other - merge them
+
+ (*rOuter).extend(*rInner);
+
+ // remove the inner rect from the list
+ _dirtyRects.erase(rInner);
+
+ // move back to beginning of list
+ rInner = rOuter;
+ }
+ }
+ }
+}
+
+uint16 *RMGfxTargetBuffer::_precalcTable = NULL;
+
+/**
+ * Set up the black & white precalculated mapping table. This is only
+ * called if the user selects the black & white option.
+ */
+void RMGfxTargetBuffer::createBWPrecalcTable() {
+ _precalcTable = new uint16[0x8000];
+
+ for (int i = 0; i < 0x8000; i++) {
+ int r = (i >> 10) & 0x1F;
+ int g = (i >> 5) & 0x1F;
+ int b = i & 0x1F;
+
+ int min = MIN(r, MIN(g, b));
+ int max = MAX(r, MAX(g, b));
+
+ min = (min + max) / 2;
+
+ r = CLIP(min + 8 - 8, 0, 31);
+ g = CLIP(min + 5 - 8, 0, 31);
+ b = CLIP(min + 0 - 8, 0, 31);
+
+ _precalcTable[i] = (r << 10) | (g << 5) | b;
+ }
+}
+
+/**
+ * Frees the black & white precalculated mapping table.
+ */
+void RMGfxTargetBuffer::freeBWPrecalcTable() {
+ delete[] _precalcTable;
+ _precalcTable = NULL;
+}
+
+RMGfxTargetBuffer::operator byte *() {
+ return _buf;
+}
+
+RMGfxTargetBuffer::operator void *() {
+ return (void *)_buf;
+}
+
+RMGfxTargetBuffer::operator uint16 *() {
+ // FIXME: This may not be endian safe
+ return (uint16 *)_buf;
+}
+
+/**
+ * Offseting buffer
+ */
+void RMGfxTargetBuffer::offsetY(int nLines) {
+ RMGfxBuffer::offsetY(nLines, 16);
+}
+
+void RMGfxTargetBuffer::setTrackDirtyRects(bool v) {
+ _trackDirtyRects = v;
+}
+
+bool RMGfxTargetBuffer::getTrackDirtyRects() const {
+ return _trackDirtyRects;
+}
+
+/****************************************************************************\
+* RMGfxSourceBufferPal Methods
+\****************************************************************************/
+
+RMGfxSourceBufferPal::~RMGfxSourceBufferPal() {
+
+}
+
+int RMGfxSourceBufferPal::loadPaletteWA(const byte *buf, bool bSwapped) {
+ if (bSwapped) {
+ for (int i = 0; i < (1 << getBpp()); i++) {
+ _pal[i * 3 + 0] = buf[i * 3 + 2];
+ _pal[i * 3 + 1] = buf[i * 3 + 1];
+ _pal[i * 3 + 2] = buf[i * 3 + 0];
+ }
+ } else {
+ memcpy(_pal, buf, (1 << getBpp()) * 3);
+ }
+
+ preparePalette();
+
+ return (1 << getBpp()) * 3;
+}
+
+int RMGfxSourceBufferPal::loadPalette(const byte *buf) {
+ int i;
+
+ for (i = 0; i < 256; i++)
+ memcpy(_pal + i * 3, buf + i * 4, 3);
+
+ preparePalette();
+
+ return (1 << getBpp()) * 4;
+}
+
+void RMGfxSourceBufferPal::preparePalette() {
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ _palFinal[i] = (((int)_pal[i * 3 + 0] >> 3) << 10) |
+ (((int)_pal[i * 3 + 1] >> 3) << 5) |
+ (((int)_pal[i * 3 + 2] >> 3) << 0);
+ }
+}
+
+int RMGfxSourceBufferPal::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ int read;
+
+ // Load the RAW image
+ read = RMGfxSourceBuffer::init(buf, dimx, dimy);
+
+ // Load the palette if necessary
+ if (bLoadPalette)
+ read += loadPaletteWA(&buf[read]);
+
+ return read;
+}
+
+void RMGfxSourceBufferPal::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ // Load the RAW image
+ RMGfxSourceBuffer::init(ds, dimx, dimy);
+
+ // Load the palette if necessary
+ if (bLoadPalette) {
+ byte *suxpal = new byte[256 * 3];
+ ds.read(suxpal, 256 * 3);
+ loadPaletteWA(suxpal);
+ delete[] suxpal;
+ }
+}
+
+int RMGfxSourceBufferPal::loadPalette(uint32 resID) {
+ return loadPalette(RMRes(resID));
+}
+
+int RMGfxSourceBufferPal::loadPaletteWA(uint32 resID, bool bSwapped) {
+ return loadPaletteWA(RMRes(resID), bSwapped);
+}
+
+/****************************************************************************\
+* RMGfxSourceBuffer4 Methods
+\****************************************************************************/
+
+void RMGfxSourceBuffer4::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+}
+
+RMGfxSourceBuffer4::RMGfxSourceBuffer4(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 4) {
+ setPriority(0);
+}
+
+
+/**
+ * Returns the number of bits per pixel of the surface
+ *
+ * @returns Bit per pixel
+ */
+int RMGfxSourceBuffer4::getBpp() {
+ return 4;
+}
+
+void RMGfxSourceBuffer4::create(int dimx, int dimy) {
+ RMGfxBuffer::create(dimx, dimy, 4);
+}
+
+/****************************************************************************\
+* RMGfxSourceBuffer8 Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8::~RMGfxSourceBuffer8() {
+
+}
+
+void RMGfxSourceBuffer8::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int x, y, width, height, u, v;
+ int bufx = bigBuf.getDimx();
+ uint16 *buf = bigBuf;
+ byte *raw = _buf;
+
+ // Destination buffer
+ RMRect dst;
+ if (prim->haveDst())
+ dst = prim->getDst();
+
+ // Clipping
+ if (prim->haveSrc()) {
+ u = prim->getSrc()._x1;
+ v = prim->getSrc()._y1;
+
+ width = prim->getSrc().width();
+ height = prim->getSrc().height();
+ }
+
+ if (!clip2D(dst._x1, dst._y1, u, v, width, height, prim->haveSrc(), &bigBuf))
+ return;
+
+ // Starting offset into the buffer
+ buf += dst._y1 * bufx + dst._x1;
+
+ // Normal step
+ if (_bTrasp0) {
+ for (y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (x = 0; x < width; x++) {
+ if (*raw)
+ *buf = _palFinal[*raw];
+ buf++;
+ raw++;
+ }
+
+ buf += bufx - width;
+ }
+ } else {
+ for (y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (x = 0; x < width; x += 2) {
+ buf[0] = _palFinal[raw[0]];
+ buf[1] = _palFinal[raw[1]];
+
+ buf += 2;
+ raw += 2;
+ }
+
+ buf += bufx - width;
+ }
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(dst._x1, dst._y1, dst._x1 + width, dst._y1 + height));
+}
+
+RMGfxSourceBuffer8::RMGfxSourceBuffer8(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 8) {
+ setPriority(0);
+ _bTrasp0 = false;
+}
+
+RMGfxSourceBuffer8::RMGfxSourceBuffer8(bool bTrasp0) {
+ _bTrasp0 = bTrasp0;
+}
+
+
+/**
+ * Returns the number of bits per pixel of the surface
+ *
+ * @returns Bit per pixel
+ */
+int RMGfxSourceBuffer8::getBpp() {
+ return 8;
+}
+
+void RMGfxSourceBuffer8::create(int dimx, int dimy) {
+ RMGfxBuffer::create(dimx, dimy, 8);
+}
+
+#define GETRED(x) (((x) >> 10) & 0x1F)
+#define GETGREEN(x) (((x) >> 5) & 0x1F)
+#define GETBLUE(x) ((x) & 0x1F)
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8AB Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8AB::~RMGfxSourceBuffer8AB() {
+
+}
+
+int RMGfxSourceBuffer8AB::calcTrasp(int fore, int back) {
+ int r, g, b;
+
+ r = (GETRED(fore) >> 2) + (GETRED(back) >> 1);
+ g = (GETGREEN(fore) >> 2) + (GETGREEN(back) >> 1);
+ b = (GETBLUE(fore) >> 2) + (GETBLUE(back) >> 1);
+
+ if (r > 0x1F)
+ r = 0x1F;
+
+ if (g > 0x1F)
+ g = 0x1F;
+
+ if (b > 0x1F)
+ b = 0x1F;
+
+ return (r << 10) | (g << 5) | b;
+}
+
+
+void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int x, y, width, height, u, v;
+ int bufx = bigBuf.getDimx();
+ uint16 *buf = bigBuf;
+ byte *raw = _buf;
+
+ // Destination buffer
+ RMRect dst;
+ if (prim->haveDst())
+ dst = prim->getDst();
+
+ // Clipping
+ if (prim->haveSrc()) {
+ u = prim->getSrc()._x1;
+ v = prim->getSrc()._y1;
+
+ width = prim->getSrc().width();
+ height = prim->getSrc().height();
+ }
+
+ if (!clip2D(dst._x1, dst._y1, u, v, width, height, prim->haveSrc(), &bigBuf))
+ return;
+
+ // Starting offset into the buffer
+ buf += dst._y1 * bufx + dst._x1;
+
+ // Passaggio normale
+ if (_bTrasp0) {
+ for (y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (x = 0; x < width; x++) {
+ if (*raw)
+ *buf = calcTrasp(_palFinal[*raw], *buf);
+
+ buf++;
+ raw++;
+ }
+
+ buf += bufx - width;
+ }
+ } else {
+ for (y = 0; y < height; y++) {
+ raw = _buf + (y + v) * _dimx + u;
+
+ for (x = 0; x < width; x += 2) {
+ buf[0] = calcTrasp(_palFinal[raw[0]], buf[0]);
+ buf[1] = calcTrasp(_palFinal[raw[1]], buf[1]);
+
+ buf += 2;
+ raw += 2;
+ }
+
+ buf += bufx - width;
+ }
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(dst._x1, dst._y1, dst._x1 + width, dst._y1 + height));
+}
+
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLE Methods
+\****************************************************************************/
+
+byte RMGfxSourceBuffer8RLE::_megaRLEBuf[512 * 1024];
+
+void RMGfxSourceBuffer8RLE::setAlphaBlendColor(int color) {
+ _alphaBlendColor = color;
+}
+
+RMGfxSourceBuffer8RLE::RMGfxSourceBuffer8RLE() {
+ _alphaBlendColor = -1;
+ _bNeedRLECompress = true;
+ _buf = NULL;
+
+ _alphaR = _alphaG = _alphaB = 0;
+}
+
+RMGfxSourceBuffer8RLE::~RMGfxSourceBuffer8RLE() {
+ if (_buf != NULL) {
+ delete[] _buf;
+ _buf = NULL;
+ }
+}
+
+
+int RMGfxSourceBuffer8RLE::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ return RMGfxSourceBufferPal::init(buf, dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer8RLE::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ if (_bNeedRLECompress) {
+ RMGfxSourceBufferPal::init(ds, dimx, dimy, bLoadPalette);
+ } else {
+ int size;
+
+ size = ds.readSint32LE();
+ _buf = new byte[size];
+ ds.read(_buf, size);
+
+ _dimx = dimx;
+ _dimy = dimy;
+ }
+}
+
+void RMGfxSourceBuffer8RLE::preparePalette() {
+ // Invoke the parent method
+ RMGfxSourceBuffer8::preparePalette();
+
+ // Handle RGB alpha blending
+ if (_alphaBlendColor != -1) {
+ _alphaR = (_palFinal[_alphaBlendColor] >> 10) & 0x1F;
+ _alphaG = (_palFinal[_alphaBlendColor] >> 5) & 0x1F;
+ _alphaB = (_palFinal[_alphaBlendColor]) & 0x1F;
+ }
+}
+
+void RMGfxSourceBuffer8RLE::prepareImage() {
+ // Invoke the parent method
+ RMGfxSourceBuffer::prepareImage();
+
+ // Compress
+ compressRLE();
+}
+
+void RMGfxSourceBuffer8RLE::setAlreadyCompressed() {
+ _bNeedRLECompress = false;
+}
+
+void RMGfxSourceBuffer8RLE::compressRLE() {
+ int x, y;
+ byte *startline;
+ byte *cur;
+ byte curdata;
+ byte *src;
+ byte *startsrc;
+ int rep;
+
+ // Perform RLE compression for lines
+ cur = _megaRLEBuf;
+ src = _buf;
+ for (y = 0; y < _dimy; y++) {
+ // Save the beginning of the line
+ startline = cur;
+
+ // Leave space for the length of the line
+ cur += 2;
+
+ // It starts from the empty space
+ curdata = 0;
+ rep = 0;
+ startsrc = src;
+ for (x = 0; x < _dimx;) {
+ if ((curdata == 0 && *src == 0) || (curdata == 1 && *src == _alphaBlendColor)
+ || (curdata == 2 && (*src != _alphaBlendColor && *src != 0))) {
+ src++;
+ rep++;
+ x++;
+ } else {
+ if (curdata == 0) {
+ RLEWriteTrasp(cur, rep);
+ curdata++;
+ } else if (curdata == 1) {
+ RLEWriteAlphaBlend(cur, rep);
+ curdata++;
+ } else {
+ RLEWriteData(cur, rep, startsrc);
+ curdata = 0;
+ }
+
+ rep = 0;
+ startsrc = src;
+ }
+ }
+
+ // Pending data?
+ if (curdata == 1) {
+ RLEWriteAlphaBlend(cur, rep);
+ RLEWriteData(cur, 0, NULL);
+ }
+
+ if (curdata == 2) {
+ RLEWriteData(cur, rep, startsrc);
+ }
+
+ // End of line
+ RLEWriteEOL(cur);
+
+ // Write the length of the line
+ WRITE_LE_UINT16(startline, (uint16)(cur - startline));
+ }
+
+ // Delete the original image
+ delete[] _buf;
+
+ // Copy the compressed image
+ x = cur - _megaRLEBuf;
+ _buf = new byte[x];
+ Common::copy(_megaRLEBuf, _megaRLEBuf + x, _buf);
+}
+
+void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int y;
+ byte *src;
+ uint16 *buf = bigBuf;
+ int x1, y1, u, v, width, height;
+
+ // Clipping
+ x1 = prim->getDst()._x1;
+ y1 = prim->getDst()._y1;
+ if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf))
+ return;
+
+ // Go forward through the RLE lines
+ src = _buf;
+ for (y = 0; y < v; y++)
+ src += READ_LE_UINT16(src);
+
+ // Calculate the position in the destination buffer
+ buf += y1 * bigBuf.getDimx();
+
+ // Loop
+ if (prim->isFlipped()) {
+// Eliminate horizontal clipping
+// width = m_dimx;
+// x1=prim->Dst().x1;
+
+ // Clipping
+ u = _dimx - (width + u);
+ x1 = (prim->getDst()._x1 + _dimx - 1) - u;
+
+ if (width > x1)
+ width = x1;
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height));
+
+ for (y = 0; y < height; y++) {
+ // Decompression
+ RLEDecompressLineFlipped(buf + x1, src + 2, u, width);
+
+ // Next line
+ src += READ_LE_UINT16(src);
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+ } else {
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height));
+
+ for (y = 0; y < height; y++) {
+ // Decompression
+ RLEDecompressLine(buf + x1, src + 2, u, width);
+
+ // Next line
+ src += READ_LE_UINT16(src);
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+ }
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLEByte Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEByte::~RMGfxSourceBuffer8RLEByte() {
+
+}
+
+void RMGfxSourceBuffer8RLEByte::RLEWriteTrasp(byte *&cur, int rep) {
+ assert(rep < 255);
+ *cur ++ = rep;
+}
+
+void RMGfxSourceBuffer8RLEByte::RLEWriteAlphaBlend(byte *&cur, int rep) {
+ assert(rep < 255);
+ *cur ++ = rep;
+}
+
+void RMGfxSourceBuffer8RLEByte::RLEWriteData(byte *&cur, int rep, byte *src) {
+ assert(rep < 256);
+
+ *cur ++ = rep;
+ if (rep > 0) {
+ memcpy(cur, src, rep);
+ cur += rep;
+ src += rep;
+ }
+
+ return;
+}
+
+void RMGfxSourceBuffer8RLEByte::RLEWriteEOL(byte *&cur) {
+ *cur ++ = 0xFF;
+}
+
+void RMGfxSourceBuffer8RLEByte::RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int i, n;
+ int r, g, b;
+
+ if (nStartSkip == 0)
+ goto RLEByteDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = *src++;
+ if (n == 0xFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst += n - nStartSkip;
+ nLength -= n - nStartSkip;
+ if (nLength > 0)
+ goto RLEByteDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = *src++;
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEByteDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // DATA
+ n = *src++;
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEByteDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEByteDoTrasp:
+ // Get the trasp of s**t
+ n = *src++;
+
+ // EOL?
+ if (n == 0xFF)
+ return;
+
+ dst += n;
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEByteDoAlpha:
+ // Alpha
+ n = *src++;
+
+RLEByteDoAlpha2:
+ if (n > nLength)
+ n = nLength;
+ for (i = 0; i < n; i++) {
+ r = (*dst >> 10) & 0x1F;
+ g = (*dst >> 5) & 0x1F;
+ b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst ++ = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+
+//RLEByteDoCopy:
+ // Copy the stuff
+ n = *src++;
+
+RLEByteDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++)
+ *dst ++ = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+ }
+}
+
+void RMGfxSourceBuffer8RLEByte::RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int i, n;
+ int r, g, b;
+
+ if (nStartSkip == 0)
+ goto RLEByteFlippedDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = *src++;
+ if (n == 0xFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst -= n - nStartSkip;
+ nLength -= n - nStartSkip;
+ if (nLength > 0)
+ goto RLEByteFlippedDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = *src++;
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEByteFlippedDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // DATA
+ n = *src++;
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEByteFlippedDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEByteFlippedDoTrasp:
+ // Get the trasp of s**t
+ n = *src++;
+
+ // EOL?
+ if (n == 0xFF)
+ return;
+
+ dst -= n;
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEByteFlippedDoAlpha:
+ // Alpha
+ n = *src++;
+
+RLEByteFlippedDoAlpha2:
+ if (n > nLength)
+ n = nLength;
+ for (i = 0; i < n; i++) {
+ r = (*dst >> 10) & 0x1F;
+ g = (*dst >> 5) & 0x1F;
+ b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst-- = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+
+//RLEByteFlippedDoCopy:
+ // Copy the data
+ n = *src++;
+
+RLEByteFlippedDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++)
+ *dst-- = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+ assert(nLength > 0);
+ }
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLEWord Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEWord::~RMGfxSourceBuffer8RLEWord() {
+
+}
+
+void RMGfxSourceBuffer8RLEWord::RLEWriteTrasp(byte *&cur, int rep) {
+ WRITE_LE_UINT16(cur, rep);
+ cur += 2;
+}
+
+void RMGfxSourceBuffer8RLEWord::RLEWriteAlphaBlend(byte *&cur, int rep) {
+ WRITE_LE_UINT16(cur, rep);
+ cur += 2;
+}
+
+void RMGfxSourceBuffer8RLEWord::RLEWriteData(byte *&cur, int rep, byte *src) {
+ WRITE_LE_UINT16(cur, rep);
+ cur += 2;
+
+ if (rep > 0) {
+ memcpy(cur, src, rep);
+ cur += rep;
+ src += rep;
+ }
+}
+
+void RMGfxSourceBuffer8RLEWord::RLEWriteEOL(byte *&cur) {
+ *cur ++ = 0xFF;
+ *cur ++ = 0xFF;
+}
+
+void RMGfxSourceBuffer8RLEWord::RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int i, n;
+ int r, g, b;
+
+ if (nStartSkip == 0)
+ goto RLEWordDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n == 0xFFFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst += n - nStartSkip;
+ nLength -= n - nStartSkip;
+
+ if (nLength > 0)
+ goto RLEWordDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEWordDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ // DATA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEWordDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEWordDoTrasp:
+ // Get the trasp of s**t
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ // EOL?
+ if (n == 0xFFFF)
+ return;
+
+ dst += n;
+
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEWordDoAlpha:
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoAlpha2:
+
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++) {
+ r = (*dst >> 10) & 0x1F;
+ g = (*dst >> 5) & 0x1F;
+ b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst++ = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+//RLEWordDoCopy:
+ // Copy the data
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++)
+ *dst++ = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+ }
+}
+
+void RMGfxSourceBuffer8RLEWord::RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int i, n;
+ int r, g, b;
+
+ if (nStartSkip == 0)
+ goto RLEWordFlippedDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n == 0xFFFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst -= n - nStartSkip;
+ nLength -= n - nStartSkip;
+
+ if (nLength > 0)
+ goto RLEWordFlippedDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEWordFlippedDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ // DATA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEWordFlippedDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEWordFlippedDoTrasp:
+ // Get the trasp of s**t
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ // EOL?
+ if (n == 0xFFFF)
+ return;
+
+ dst -= n;
+
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEWordFlippedDoAlpha:
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordFlippedDoAlpha2:
+
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++) {
+ r = (*dst >> 10) & 0x1F;
+ g = (*dst >> 5) & 0x1F;
+ b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst-- = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+//RLEWordFlippedDoCopy:
+ // Copy the data
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordFlippedDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++)
+ *dst-- = _palFinal[*src++];
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+ }
+}
+
+/****************************************************************************\
+* Methods for RMGfxSourceBuffer8RLEWord
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEWordAB::~RMGfxSourceBuffer8RLEWordAB() {
+
+}
+
+void RMGfxSourceBuffer8RLEWordAB::RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) {
+ int i, n;
+ int r, g, b, r2, g2, b2;
+
+ if (!GLOBALS._bCfgTransparence) {
+ RMGfxSourceBuffer8RLEWord::RLEDecompressLine(dst, src, nStartSkip, nLength);
+ return;
+ }
+
+ if (nStartSkip == 0)
+ goto RLEWordDoTrasp;
+
+ while (1) {
+ assert(nStartSkip > 0);
+
+ // TRASP
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n == 0xFFFF)
+ return;
+
+ if (n >= nStartSkip) {
+ dst += n - nStartSkip;
+ nLength -= n - nStartSkip;
+
+ if (nLength > 0)
+ goto RLEWordDoAlpha;
+ else
+ return;
+ }
+ nStartSkip -= n;
+
+ assert(nStartSkip > 0);
+
+ // ALPHA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ n -= nStartSkip;
+ goto RLEWordDoAlpha2;
+ }
+ nStartSkip -= n;
+
+ // DATA
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ if (n >= nStartSkip) {
+ src += nStartSkip;
+ n -= nStartSkip;
+ goto RLEWordDoCopy2;
+ }
+ nStartSkip -= n;
+ src += n;
+ }
+
+
+ while (1) {
+RLEWordDoTrasp:
+ // Get the trasp of s**t
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+ // EOL?
+ if (n == 0xFFFF)
+ return;
+
+ dst += n;
+
+ nLength -= n;
+ if (nLength <= 0)
+ return;
+
+RLEWordDoAlpha:
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoAlpha2:
+
+ if (n > nLength)
+ n = nLength;
+
+ // @@@ SHOULD NOT BE THERE !!!!!
+ for (i = 0; i < n; i++) {
+ r = (*dst >> 10) & 0x1F;
+ g = (*dst >> 5) & 0x1F;
+ b = *dst & 0x1F;
+
+ r = (r >> 2) + (_alphaR >> 1);
+ g = (g >> 2) + (_alphaG >> 1);
+ b = (b >> 2) + (_alphaB >> 1);
+
+ *dst++ = (r << 10) | (g << 5) | b;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+
+//RLEWordDoCopy:
+ // Copy the data
+ n = READ_LE_UINT16(src);
+ src += 2;
+
+RLEWordDoCopy2:
+ if (n > nLength)
+ n = nLength;
+
+ for (i = 0; i < n; i++) {
+ r = (*dst >> 10) & 0x1F;
+ g = (*dst >> 5) & 0x1F;
+ b = *dst & 0x1F;
+
+ r2 = (_palFinal[*src] >> 10) & 0x1F;
+ g2 = (_palFinal[*src] >> 5) & 0x1F;
+ b2 = _palFinal[*src] & 0x1F;
+
+ r = (r >> 1) + (r2 >> 1);
+ g = (g >> 1) + (g2 >> 1);
+ b = (b >> 1) + (b2 >> 1);
+
+ *dst ++ = (r << 10) | (g << 5) | b;
+ src++;
+ }
+
+ nLength -= n;
+ if (!nLength)
+ return;
+
+ assert(nLength > 0);
+ }
+}
+
+/****************************************************************************\
+* Methods for RMGfxSourceBuffer8AA
+\****************************************************************************/
+
+byte RMGfxSourceBuffer8AA::_megaAABuf[256 * 1024];
+byte RMGfxSourceBuffer8AA::_megaAABuf2[64 * 1024];
+
+void RMGfxSourceBuffer8AA::prepareImage() {
+ // Invoke the parent method
+ RMGfxSourceBuffer::prepareImage();
+
+ // Prepare the buffer for anti-aliasing
+ calculateAA();
+}
+
+void RMGfxSourceBuffer8AA::calculateAA() {
+ int x, y;
+ byte *src, *srcaa;
+
+ // First pass: fill the edges
+ Common::fill(_megaAABuf, _megaAABuf + _dimx * _dimy, 0);
+
+ src = _buf;
+ srcaa = _megaAABuf;
+ for (y = 0; y < _dimy; y++) {
+ for (x = 0; x < _dimx; x++) {
+ if (*src == 0) {
+ if ((y > 0 && src[-_dimx] != 0) ||
+ (y < _dimy - 1 && src[_dimx] != 0) ||
+ (x > 0 && src[-1] != 0) ||
+ (x < _dimx - 1 && src[1] != 0))
+ *srcaa = 1;
+ }
+
+ src++;
+ srcaa++;
+ }
+ }
+
+ src = _buf;
+ srcaa = _megaAABuf;
+ for (y = 0; y < _dimy; y++) {
+ for (x = 0; x < _dimx; x++) {
+ if (*src != 0) {
+ if ((y > 0 && srcaa[-_dimx] == 1) ||
+ (y < _dimy - 1 && srcaa[_dimx] == 1) ||
+ (x > 0 && srcaa[-1] == 1) ||
+ (x < _dimx - 1 && srcaa[1] == 1))
+ *srcaa = 2;
+ }
+
+ src++;
+ srcaa++;
+ }
+ }
+
+ if (_aabuf != NULL)
+ delete[] _aabuf;
+
+ _aabuf = new byte[_dimx * _dimy];
+ memcpy(_aabuf, _megaAABuf, _dimx * _dimy);
+}
+
+RMGfxSourceBuffer8AA::RMGfxSourceBuffer8AA() : RMGfxSourceBuffer8() {
+ _aabuf = NULL;
+}
+
+RMGfxSourceBuffer8AA::~RMGfxSourceBuffer8AA() {
+ if (_aabuf != NULL)
+ delete[] _aabuf;
+}
+
+void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int x, y;
+ byte *src;
+ uint16 *mybuf;
+ uint16 *buf;
+ int x1, y1, u, v, width, height;
+ int r, g, b;
+ int step;
+
+ // Clip the sprite
+ x1 = prim->getDst()._x1;
+ y1 = prim->getDst()._y1;
+ if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf))
+ return;
+
+ // Go forward through the RLE lines
+ src = _buf;
+ for (y = 0; y < v; y++)
+ src += READ_LE_UINT16(src);
+
+ // Eliminate horizontal clipping
+
+ if (prim->isFlipped()) {
+ u = _dimx - (width + u);
+ x1 = (prim->getDst()._x1 + _dimx - 1) - u;
+
+ if (width > x1)
+ width = x1;
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height));
+ } else {
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height));
+ }
+
+// width = _dimx;
+// x1 = prim->Dst().x1;
+
+
+ // Position into the destination buffer
+ buf = bigBuf;
+ buf += y1 * bigBuf.getDimx();
+
+ if (prim->isFlipped())
+ step = -1;
+ else
+ step = 1;
+
+ // Loop
+ buf += bigBuf.getDimx(); // Skip the first line
+ for (y = 1; y < height - 1; y++) {
+ // if (prim->IsFlipped())
+ // mybuf=&buf[x1+m_dimx-1];
+ // else
+ mybuf = &buf[x1];
+
+ for (x = 0; x < width; x++, mybuf += step) {
+ if (_aabuf[(y + v) * _dimx + x + u] == 2 && x != 0 && x != width - 1) {
+ r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]);
+ g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]);
+ b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]);
+
+ r += GETRED(mybuf[0]);
+ g += GETGREEN(mybuf[0]);
+ b += GETBLUE(mybuf[0]);
+
+ r /= 5;
+ g /= 5;
+ b /= 5;
+
+ if (r > 31) r = 31;
+ if (g > 31) g = 31;
+ if (b > 31) b = 31;
+
+ mybuf[0] = (r << 10) | (g << 5) | b;
+ }
+ }
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+
+ // Position into the destination buffer
+ buf = bigBuf;
+ buf += y1 * bigBuf.getDimx();
+
+ // Looppone
+ buf += bigBuf.getDimx();
+ for (y = 1; y < height - 1; y++) {
+ // if (prim->IsFlipped())
+ // mybuf=&buf[x1+m_dimx-1];
+ // else
+ mybuf = &buf[x1];
+
+ for (x = 0; x < width; x++, mybuf += step) {
+ if (_aabuf[(y + v) * _dimx + x + u] == 1 && x != 0 && x != width - 1) {
+ r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]);
+ g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]);
+ b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]);
+
+ r += GETRED(mybuf[0]) * 2;
+ g += GETGREEN(mybuf[0]) * 2;
+ b += GETBLUE(mybuf[0]) * 2;
+
+ r /= 6;
+ g /= 6;
+ b /= 6;
+
+ if (r > 31) r = 31;
+ if (g > 31) g = 31;
+ if (b > 31) b = 31;
+
+ mybuf[0] = (r << 10) | (g << 5) | b;
+ }
+ }
+
+ // Skip to the next line
+ buf += bigBuf.getDimx();
+ }
+}
+
+
+
+void RMGfxSourceBuffer8AA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(RMGfxSourceBuffer8::draw, bigBuf, prim);
+ drawAA(bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+/****************************************************************************\
+* RMGfxSourceBuffer8RLEAA Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer8RLEByteAA::~RMGfxSourceBuffer8RLEByteAA() {
+}
+
+void RMGfxSourceBuffer8RLEByteAA::prepareImage() {
+ RMGfxSourceBuffer::prepareImage();
+ calculateAA();
+ compressRLE();
+}
+
+void RMGfxSourceBuffer8RLEByteAA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(RMGfxSourceBuffer8RLE::draw, bigBuf, prim);
+ if (GLOBALS._bCfgAntiAlias)
+ drawAA(bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+int RMGfxSourceBuffer8RLEByteAA::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ return RMGfxSourceBuffer8RLE::init(buf, dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer8RLEByteAA::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ RMGfxSourceBuffer8RLE::init(ds, dimx, dimy, bLoadPalette);
+
+ if (!_bNeedRLECompress) {
+ // Load the anti-aliasing mask
+ _aabuf = new byte[dimx * dimy];
+ ds.read(_aabuf, dimx * dimy);
+ }
+}
+
+RMGfxSourceBuffer8RLEWordAA::~RMGfxSourceBuffer8RLEWordAA() {
+}
+
+void RMGfxSourceBuffer8RLEWordAA::prepareImage() {
+ RMGfxSourceBuffer::prepareImage();
+ calculateAA();
+ compressRLE();
+}
+
+void RMGfxSourceBuffer8RLEWordAA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(RMGfxSourceBuffer8RLE::draw, bigBuf, prim);
+ if (GLOBALS._bCfgAntiAlias)
+ drawAA(bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+int RMGfxSourceBuffer8RLEWordAA::init(byte *buf, int dimx, int dimy, bool bLoadPalette) {
+ return RMGfxSourceBuffer8RLE::init(buf, dimx, dimy, bLoadPalette);
+}
+
+void RMGfxSourceBuffer8RLEWordAA::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) {
+ RMGfxSourceBuffer8RLE::init(ds, dimx, dimy, bLoadPalette);
+
+ if (!_bNeedRLECompress) {
+ // Load the anti-aliasing mask
+ _aabuf = new byte[dimx * dimy];
+ ds.read(_aabuf, dimx * dimy);
+ }
+}
+
+
+/****************************************************************************\
+* RMGfxSourceBuffer16 Methods
+\****************************************************************************/
+
+RMGfxSourceBuffer16::RMGfxSourceBuffer16(bool bTrasp0) {
+ _bTrasp0 = bTrasp0;
+}
+
+RMGfxSourceBuffer16::~RMGfxSourceBuffer16() {
+}
+
+void RMGfxSourceBuffer16::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ uint16 *buf = bigBuf;
+ uint16 *raw = (uint16 *)_buf;
+ int dimx, dimy;
+ int u, v;
+ int x1, y1;
+
+ dimx = _dimx;
+ dimy = _dimy;
+ u = 0;
+ v = 0;
+ x1 = 0;
+ y1 = 0;
+
+ if (prim->haveSrc()) {
+ u = prim->getSrc()._x1;
+ v = prim->getSrc()._y1;
+ dimx = prim->getSrc().width();
+ dimy = prim->getSrc().height();
+ }
+
+ if (prim->haveDst()) {
+ x1 = prim->getDst()._x1;
+ y1 = prim->getDst()._y1;
+ }
+
+ if (!clip2D(x1, y1, u, v, dimx, dimy, true, &bigBuf))
+ return;
+
+ raw += v * _dimx + u;
+ buf += y1 * bigBuf.getDimx() + x1;
+
+ if (_bTrasp0) {
+ for (int y = 0; y < dimy; y++) {
+ for (int x = 0; x < dimx;) {
+ while (x < dimx && raw[x] == 0)
+ x++;
+
+ while (x < dimx && raw[x] != 0) {
+ buf[x] = raw[x];
+ x++;
+ }
+ }
+
+ raw += _dimx;
+ buf += bigBuf.getDimx();
+ }
+ } else {
+ for (int y = 0; y < dimy; y++) {
+ Common::copy(raw, raw + dimx, buf);
+ buf += bigBuf.getDimx();
+ raw += _dimx;
+ }
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + dimx, y1 + dimy));
+}
+
+void RMGfxSourceBuffer16::prepareImage() {
+ // Color space conversion if necessary!
+ int i;
+ uint16 *buf = (uint16 *)_buf;
+
+ for (i = 0; i < _dimx * _dimy; i++)
+ WRITE_LE_UINT16(&buf[i], FROM_LE_16(buf[i]) & 0x7FFF);
+}
+
+RMGfxSourceBuffer16::RMGfxSourceBuffer16(int dimx, int dimy)
+ : RMGfxBuffer(dimx, dimy, 16) {
+ setPriority(0);
+ _bTrasp0 = false;
+}
+
+/**
+ * Returns the number of bits per pixel of the surface
+ *
+ * @returns Bit per pixel
+ */
+int RMGfxSourceBuffer16::getBpp() {
+ return 16;
+}
+
+void RMGfxSourceBuffer16::create(int dimx, int dimy) {
+ RMGfxBuffer::create(dimx, dimy, 16);
+}
+
+/****************************************************************************\
+* RMGfxBox Methods
+\****************************************************************************/
+
+void RMGfxBox::removeThis(CORO_PARAM, bool &result) {
+ result = true;
+}
+
+void RMGfxBox::setColor(byte r, byte g, byte b) {
+ r >>= 3;
+ g >>= 3;
+ b >>= 3;
+ _wFillColor = (r << 10) | (g << 5) | b;
+}
+
+void RMGfxBox::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ int i, j;
+ uint16 *buf = bigBuf;
+ RMRect rcDst;
+
+ // It takes the destination rectangle
+ rcDst = prim->getDst();
+ buf += rcDst._y1 * bigBuf.getDimx() + rcDst._x1;
+
+ // Loop through the pixels
+ for (j = 0; j < rcDst.height(); j++) {
+ for (i = 0; i < rcDst.width(); i++)
+ *buf++ = _wFillColor;
+
+ buf += bigBuf.getDimx() - rcDst.width();
+ }
+
+ // Specify the drawn area
+ bigBuf.addDirtyRect(rcDst);
+}
+
+
+/****************************************************************************\
+* RMGfxClearTask Methods
+\****************************************************************************/
+
+int RMGfxClearTask::priority() {
+ // Maximum priority (must be done first)
+ return 1;
+}
+
+void RMGfxClearTask::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *) {
+ // Clean the target buffer
+ Common::fill((byte *)bigBuf, (byte *)bigBuf + (bigBuf.getDimx() * bigBuf.getDimy() * 2), 0x0);
+ bigBuf.addDirtyRect(Common::Rect(bigBuf.getDimx(), bigBuf.getDimy()));
+}
+
+void RMGfxClearTask::removeThis(CORO_PARAM, bool &result) {
+ // The task is fine to be removed
+ result = true;
+}
+
+/****************************************************************************\
+* RMGfxPrimitive Methods
+\****************************************************************************/
+
+RMGfxPrimitive::RMGfxPrimitive() {
+ _bFlag = 0;
+ _task = NULL;
+ _src.setEmpty();
+ _dst.setEmpty();
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task) {
+ _task = task;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst) {
+ _task = task;
+ _src = src;
+ _dst = dst;
+ _bFlag = 0;
+ _bStretch = (src.width() != dst.width() || src.height() != dst.height());
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst) {
+ _task = task;
+ _src.topLeft() = src;
+ _dst = dst;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst) {
+ _task = task;
+ _src.topLeft() = src;
+ _dst.topLeft() = dst;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst) {
+ _task = task;
+ _src = src;
+ _dst.topLeft() = dst;
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &dst) {
+ _task = task;
+ _dst = dst;
+ _src.setEmpty();
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst) {
+ _task = task;
+ _dst.topLeft() = dst;
+ _src.setEmpty();
+ _bFlag = 0;
+ _bStretch = false;
+}
+
+RMGfxPrimitive::~RMGfxPrimitive() {
+}
+
+void RMGfxPrimitive::setFlag(byte bFlag) {
+ _bFlag = bFlag;
+}
+
+void RMGfxPrimitive::setTask(RMGfxTask *task) {
+ _task = task;
+}
+
+void RMGfxPrimitive::setSrc(const RMRect &src) {
+ _src = src;
+}
+
+void RMGfxPrimitive::setSrc(const RMPoint &src) {
+ _src.topLeft() = src;
+}
+
+void RMGfxPrimitive::setDst(const RMRect &dst) {
+ _dst = dst;
+}
+
+void RMGfxPrimitive::setDst(const RMPoint &dst) {
+ _dst.topLeft() = dst;
+}
+
+void RMGfxPrimitive::setStretch(bool bStretch) {
+ _bStretch = bStretch;
+}
+
+bool RMGfxPrimitive::haveDst() {
+ return !_dst.isEmpty();
+}
+
+RMRect &RMGfxPrimitive::getDst() {
+ return _dst;
+}
+
+bool RMGfxPrimitive::haveSrc() {
+ return !_src.isEmpty();
+}
+
+RMRect &RMGfxPrimitive::getSrc() {
+ return _src;
+}
+
+/**
+ * Flags
+ */
+bool RMGfxPrimitive::isFlipped() {
+ return _bFlag & 1;
+}
+
+/**
+ * Duplicate
+ */
+RMGfxPrimitive *RMGfxPrimitive::duplicate() {
+ return new RMGfxPrimitive(*this);
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/gfxcore.h b/engines/tony/gfxcore.h
new file mode 100644
index 0000000000..ac4eee05e4
--- /dev/null
+++ b/engines/tony/gfxcore.h
@@ -0,0 +1,516 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_GFXCORE_H
+#define TONY_GFXCORE_H
+
+#include "common/system.h"
+#include "common/coroutines.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* Class prototype
+\****************************************************************************/
+
+// Class Name Family Treee Abstract?
+class RMGfxTask; // Yes
+class RMGfxTaskSetPrior; // Task Yes
+class RMGfxBuffer; //
+class RMGfxSourceBuffer; // TaskP+[Buffer] Yes
+class RMGfxTargetBuffer; // [Buffer]
+class RMGfxSourceBufferPal; // Source Yes
+class RMGfxSourceBuffer4; // SourcePal
+class RMGfxSourceBuffer8; // SourcePal
+class RMGfxSourceBuffer16; // Source
+class RMGfxWoodyBuffer; // Source16+Target
+class RMGfxClearTask; // Task
+
+
+/**
+ * Graphics buffer
+ */
+class RMGfxBuffer {
+protected:
+ int _dimx, _dimy;
+ byte *_buf;
+ byte *_origBuf;
+
+public:
+ RMGfxBuffer();
+ RMGfxBuffer(int dimx, int dimy, int nBpp);
+ virtual ~RMGfxBuffer();
+
+ // Attributes
+ int getDimx();
+ int getDimy();
+
+ // Creation
+ virtual void create(int dimx, int dimy, int nBpp);
+ virtual void destroy();
+
+ // These are valid only if the buffer is locked
+ operator byte *();
+ operator void *();
+
+ // Getting the offset for a given Y position
+ void offsetY(int nLines, int nBpp);
+};
+
+/**
+ * Graphics primitive
+ */
+class RMGfxPrimitive {
+public:
+ RMGfxTask *_task;
+
+protected:
+ RMRect _src;
+ RMRect _dst;
+
+ bool _bStretch;
+ byte _bFlag;
+
+public:
+ RMGfxPrimitive();
+ RMGfxPrimitive(RMGfxTask *task);
+ RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMRect &dst);
+ RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst);
+ virtual ~RMGfxPrimitive();
+ void setFlag(byte bFlag);
+ void setTask(RMGfxTask *task);
+ void setSrc(const RMRect &src);
+ void setSrc(const RMPoint &src);
+ void setDst(const RMRect &dst);
+ void setDst(const RMPoint &dst);
+ void setStretch(bool bStretch);
+ bool haveDst();
+ RMRect &getDst();
+ bool haveSrc();
+ RMRect &getSrc();
+
+ // Flags
+ bool isFlipped();
+
+ // Duplicate
+ virtual RMGfxPrimitive *duplicate();
+};
+
+
+/**
+ * Graphic drawing task
+ */
+class RMGfxTask {
+protected:
+ int _nPrior;
+ int _nInList;
+
+public:
+ // Standard constructor
+ RMGfxTask();
+ virtual ~RMGfxTask() { }
+
+ virtual int priority();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) = 0;
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Registration
+ virtual void Register();
+ virtual void Unregister();
+};
+
+
+/**
+ * Graphic drawing with priority
+ */
+class RMGfxTaskSetPrior : public RMGfxTask {
+public:
+ virtual ~RMGfxTaskSetPrior() { }
+ void setPriority(int nPrior);
+};
+
+
+/**
+ * Task that cleans the destination buffer
+ */
+class RMGfxClearTask : public RMGfxTask {
+public:
+ virtual ~RMGfxClearTask() { }
+
+ int priority();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ virtual void removeThis(CORO_PARAM, bool &result);
+};
+
+
+/**
+ * Task that draws a colored box
+ */
+class RMGfxBox : public RMGfxTaskSetPrior {
+protected:
+ uint16 _wFillColor;
+
+public:
+ virtual ~RMGfxBox() { }
+
+ void setColor(byte r, byte g, byte b);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ virtual void removeThis(CORO_PARAM, bool &result);
+};
+
+
+/**
+ * Buffer source for the design, which is a task. This is an abstract base.
+ */
+class RMGfxSourceBuffer : public virtual RMGfxBuffer, public RMGfxTaskSetPrior {
+public:
+ // Load the data for the surface
+ virtual int init(uint32 resID, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+
+ virtual ~RMGfxSourceBuffer();
+
+protected:
+ virtual void prepareImage();
+ bool clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf);
+ void offsetY(int nLines);
+
+public:
+ virtual int getBpp() = 0;
+};
+
+
+/**
+ * 16-bit color source
+ */
+class RMGfxSourceBuffer16 : public RMGfxSourceBuffer {
+protected:
+ virtual void prepareImage();
+ bool _bTrasp0;
+
+public:
+ RMGfxSourceBuffer16(bool bUseTrasp = false);
+ RMGfxSourceBuffer16(int dimx, int dimy);
+ virtual ~RMGfxSourceBuffer16();
+
+ // Initialization
+ void create(int dimx, int dimy);
+
+ int getBpp();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Buffer source with palette
+ */
+class RMGfxSourceBufferPal : public RMGfxSourceBuffer {
+protected:
+ // The size of the palette is (1 << Bpp()) * 4
+ byte _pal[256 * 3];
+ uint16 _palFinal[256];
+
+ // Post process to prepare the palette for drawing
+ virtual void preparePalette();
+
+public:
+ virtual ~RMGfxSourceBufferPal();
+
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+
+ int loadPaletteWA(uint32 resID, bool bSwapped = false);
+ int loadPaletteWA(const byte *buf, bool bSwapped = false);
+ int loadPalette(uint32 resID);
+ int loadPalette(const byte *buf);
+};
+
+
+/**
+ * Buffer source with a 256 color palette
+ */
+class RMGfxSourceBuffer8 : public RMGfxSourceBufferPal {
+protected:
+ bool _bTrasp0;
+
+public:
+ RMGfxSourceBuffer8(bool bTrasp0 = true);
+ RMGfxSourceBuffer8(int dimx, int dimy);
+ virtual ~RMGfxSourceBuffer8();
+
+ // Initialization
+ void create(int dimx, int dimy);
+
+ int getBpp();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Buffer source with a 256 color palette, and alpha blending
+ */
+class RMGfxSourceBuffer8AB : public RMGfxSourceBuffer8 {
+protected:
+ int calcTrasp(int f, int b);
+
+public:
+ virtual ~RMGfxSourceBuffer8AB();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Buffer source with a 256 color palette, RLE compressed
+ */
+
+class RMGfxSourceBuffer8RLE : public virtual RMGfxSourceBuffer8 {
+protected:
+ int _alphaBlendColor;
+ int _alphaR, _alphaB, _alphaG;
+ bool _bNeedRLECompress;
+
+protected:
+ static byte _megaRLEBuf[];
+
+ virtual void RLEWriteTrasp(byte *&cur, int rep) = 0;
+ virtual void RLEWriteData(byte *&cur, int rep, byte *src) = 0;
+ virtual void RLEWriteEOL(byte *&cur) = 0;
+ virtual void RLEWriteAlphaBlend(byte *&cur, int rep) = 0;
+ virtual void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0;
+ virtual void RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0;
+
+ // Perform image compression in RLE
+ void compressRLE();
+
+protected:
+ // Overriding initialization methods
+ virtual void prepareImage();
+ virtual void preparePalette();
+
+public:
+ RMGfxSourceBuffer8RLE();
+ virtual ~RMGfxSourceBuffer8RLE();
+
+ // Overload of the initialization method
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+
+ // Draw image with RLE decompression
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Sets the color that will be alpha blended
+ void setAlphaBlendColor(int color);
+
+ // Warn if the data is already compressed
+ void setAlreadyCompressed();
+};
+
+class RMGfxSourceBuffer8RLEByte : public RMGfxSourceBuffer8RLE {
+protected:
+ void RLEWriteTrasp(byte * &cur, int rep);
+ void RLEWriteAlphaBlend(byte * &cur, int rep);
+ void RLEWriteData(byte * &cur, int rep, byte *src);
+ void RLEWriteEOL(byte * &cur);
+ void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength);
+ void RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength);
+
+public:
+ virtual ~RMGfxSourceBuffer8RLEByte();
+};
+
+class RMGfxSourceBuffer8RLEWord : public RMGfxSourceBuffer8RLE {
+protected:
+ void RLEWriteTrasp(byte * &cur, int rep);
+ void RLEWriteAlphaBlend(byte * &cur, int rep);
+ void RLEWriteData(byte * &cur, int rep, byte *src);
+ void RLEWriteEOL(byte * &cur);
+ virtual void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength);
+ virtual void RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength);
+
+public:
+ virtual ~RMGfxSourceBuffer8RLEWord();
+};
+
+class RMGfxSourceBuffer8RLEWordAB : public RMGfxSourceBuffer8RLEWord {
+protected:
+ virtual void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength);
+
+public:
+ virtual ~RMGfxSourceBuffer8RLEWordAB();
+};
+
+
+/**
+ * Buffer source with a 256 color palette, with anti-aliasing
+ */
+class RMGfxSourceBuffer8AA : public virtual RMGfxSourceBuffer8 {
+protected:
+ static byte _megaAABuf[];
+ static byte _megaAABuf2[];
+ byte *_aabuf;
+
+ // Calculate the buffer for the anti-aliasing
+ void calculateAA();
+
+ // Draw the AA
+ void drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+protected:
+ void prepareImage();
+
+public:
+ RMGfxSourceBuffer8AA();
+ virtual ~RMGfxSourceBuffer8AA();
+
+ // Draw with anti-aliasing
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+class RMGfxSourceBuffer8RLEByteAA : public RMGfxSourceBuffer8RLEByte, public RMGfxSourceBuffer8AA {
+protected:
+ void prepareImage();
+
+public:
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Overloaded initialization methods
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+
+ virtual ~RMGfxSourceBuffer8RLEByteAA();
+};
+
+class RMGfxSourceBuffer8RLEWordAA : public RMGfxSourceBuffer8RLEWord, public RMGfxSourceBuffer8AA {
+protected:
+ void prepareImage();
+
+public:
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Overloaded initialization methods
+ virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false);
+ virtual int init(byte *buf, int dimx, int dimy, bool bLoadPalette = false);
+
+ virtual ~RMGfxSourceBuffer8RLEWordAA();
+};
+
+
+/**
+ * Source buffer with 16 colors
+ */
+class RMGfxSourceBuffer4 : public RMGfxSourceBufferPal {
+public:
+ RMGfxSourceBuffer4();
+ RMGfxSourceBuffer4(int dimx, int dimy);
+
+ // Initialization
+ void create(int dimx, int dimy);
+
+ int getBpp();
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+
+/**
+ * Destination buffer which manages its own internal list of tasks
+ */
+class RMGfxTargetBuffer : public virtual RMGfxBuffer {
+private:
+ struct OTList {
+ RMGfxPrimitive *_prim;
+ OTList *_next;
+
+ OTList();
+ OTList(RMGfxPrimitive *pr) {
+ _prim = pr;
+ }
+ };
+
+ bool _trackDirtyRects;
+ Common::List<Common::Rect> _currentDirtyRects, _previousDirtyRects, _dirtyRects;
+
+ void mergeDirtyRects();
+
+private:
+// OSystem::MutexRef csModifyingOT;
+
+protected:
+ OTList *_otlist;
+ int _otSize;
+
+public:
+ RMGfxTargetBuffer();
+ virtual ~RMGfxTargetBuffer();
+
+ static uint16 *_precalcTable;
+ static void createBWPrecalcTable();
+ static void freeBWPrecalcTable();
+
+ // management of the OT list
+ void clearOT();
+ void drawOT(CORO_PARAM);
+ void addPrim(RMGfxPrimitive *prim); // The pointer must be delted
+
+ operator byte *();
+ operator void *();
+ operator uint16 *();
+
+ // Offseting buffer
+ void offsetY(int nLines);
+
+ // Dirty rect methods
+ void addDirtyRect(const Common::Rect &r);
+ Common::List<Common::Rect> &getDirtyRects();
+ void clearDirtyRects();
+ void setTrackDirtyRects(bool v);
+ bool getTrackDirtyRects() const;
+};
+
+
+/**
+ * Ring buffer, which is both source and by destination
+ */
+class RMGfxWoodyBuffer: public RMGfxSourceBuffer16, public RMGfxTargetBuffer {
+public:
+ RMGfxWoodyBuffer();
+ RMGfxWoodyBuffer(int dimx, int dimy);
+ virtual ~RMGfxWoodyBuffer();
+
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/gfxengine.cpp b/engines/tony/gfxengine.cpp
new file mode 100644
index 0000000000..5c038e154d
--- /dev/null
+++ b/engines/tony/gfxengine.cpp
@@ -0,0 +1,862 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/savefile.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/custom.h"
+#include "tony/gfxengine.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+
+/****************************************************************************\
+* RMGfxEngine Methods
+\****************************************************************************/
+
+void ExitAllIdles(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ int nCurLoc = *(const int *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Closes idle
+ GLOBALS._bSkipSfxNoLoop = true;
+
+ CORO_INVOKE_2(mpalEndIdlePoll, nCurLoc, NULL);
+
+ GLOBALS._bIdleExited = true;
+ GLOBALS._bSkipSfxNoLoop = false;
+
+ CORO_END_CODE;
+}
+
+RMGfxEngine::RMGfxEngine() {
+ // Create big buffer where the frame will be rendered
+ _bigBuf.create(RM_BBX, RM_BBY, 16);
+ _bigBuf.offsetY(RM_SKIPY);
+ _bigBuf.setTrackDirtyRects(true);
+
+ _nCurLoc = 0;
+ _curAction = TA_GOTO;
+ _curActionObj = 0;
+ _nWipeType = 0;
+ _hWipeEvent = 0;
+ _nWipeStep = 0;
+ _bMustEnterMenu = false;
+ _bWiping = false;
+ _bGUIOption = false;
+ _bGUIInterface = false;
+ _bGUIInventory = false;
+ _bAlwaysDrawMouse = false;
+ _bOption = false;
+ _bLocationLoaded = false;
+ _bInput = false;
+}
+
+RMGfxEngine::~RMGfxEngine() {
+ // Close the buffer
+ _bigBuf.destroy();
+}
+
+void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) {
+ CORO_BEGIN_CONTEXT;
+ bool bRes;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bRes = false;
+
+ if (type == 0)
+ CORO_INVOKE_2(_opt.init, _bigBuf, _ctx->bRes);
+ else if (type == 1)
+ CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, true, _ctx->bRes);
+ else if (type == 2)
+ CORO_INVOKE_2(_opt.initNoLoadSave, _bigBuf, _ctx->bRes);
+ else if (type == 3)
+ CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, false, _ctx->bRes);
+ else if (type == 4)
+ CORO_INVOKE_3(_opt.initSaveMenuOnly, _bigBuf, false, _ctx->bRes);
+
+ if (_ctx->bRes) {
+ g_vm->pauseSound(true);
+
+ disableInput();
+ _inv.endCombine();
+ _curActionObj = 0;
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ _point.setCustomPointer(NULL);
+ enableMouse();
+ g_vm->grabThumbnail();
+
+ // Exists the IDLE to avoid premature death in loading
+ _bMustEnterMenu = true;
+ if (type == 1 || type == 2) {
+ GLOBALS._bIdleExited = true;
+ } else {
+ CORO_INVOKE_0(_tony.stopNoAction);
+
+ GLOBALS._bIdleExited = false;
+
+ CoroScheduler.createProcess(ExitAllIdles, &_nCurLoc, sizeof(int));
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Poll of input devices
+ _input.poll();
+
+ if (_bMustEnterMenu && GLOBALS._bIdleExited) {
+ _bOption = true;
+ _bMustEnterMenu = false;
+ GLOBALS._bIdleExited = false;
+ }
+
+ if (_bOption) {
+ CORO_INVOKE_1(_opt.doFrame, &_input);
+ _bOption = !_opt.isClosing();
+ if (!_bOption) {
+ disableMouse();
+ enableInput();
+ mpalStartIdlePoll(_nCurLoc);
+ g_vm->pauseSound(false);
+ }
+ }
+
+ if (bDrawLocation && _bLocationLoaded) {
+ // Location and objects
+ _loc.doFrame(&_bigBuf);
+
+ // Check the mouse input
+ if (_bInput && !_tony.inAction()) {
+ // If we are on the inventory, it is it who controls all input
+ if (_inv.haveFocus(_input.mousePos()) && !_inter.active()) {
+ // Left Click
+ // **********
+ if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) {
+ // Left click activates the combine, if we are on an object
+ if (_inv.leftClick(_input.mousePos(), _curActionObj)) {
+ _curAction = TA_COMBINE;
+ _point.setAction(_curAction);
+ }
+ } else
+
+ // Right Click
+ // ***********
+ if (_input.mouseRightClicked()) {
+ if (_itemName.isItemSelected()) {
+ _curActionObj = 0;
+ _inv.rightClick(_input.mousePos());
+ } else
+ _inv.rightClick(_input.mousePos());
+ } else
+
+ // Right Release
+ // *************
+ if (_input.mouseRightReleased()) {
+ if (_inv.rightRelease(_input.mousePos(), _curAction)) {
+ CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
+
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ }
+ }
+ } else {
+ // Options Menu
+ // ************
+ if (_bGUIOption) {
+ if (!_tony.inAction() && _bInput) {
+ if ((_input.mouseLeftClicked() && _input.mousePos()._x < 3 && _input.mousePos()._y < 3)) {
+ CORO_INVOKE_1(openOptionScreen, 0);
+ goto SKIPCLICKSINISTRO;
+ } else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE))
+ CORO_INVOKE_1(openOptionScreen, 0);
+ else if (!g_vm->getIsDemo()) {
+ if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5))
+ // Save game screen
+ CORO_INVOKE_1(openOptionScreen, 4);
+ else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7))
+ // Load game screen
+ CORO_INVOKE_1(openOptionScreen, 3);
+ }
+ }
+ }
+
+ // Left Click
+ // **************
+ if (_input.mouseLeftClicked() && !_inter.active()) {
+
+ if (_curAction != TA_COMBINE)
+ CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _point.curAction());
+ else if (_itemName.getSelectedItem() != NULL)
+ CORO_INVOKE_4(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), TA_COMBINE, _curActionObj);
+
+ if (_curAction == TA_COMBINE) {
+ _inv.endCombine();
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ }
+
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ }
+
+SKIPCLICKSINISTRO:
+ // Right Click
+ // ************
+ if (_curAction == TA_COMBINE) {
+ // During a combine, it cancels it
+ if (_input.mouseRightClicked()) {
+ _inv.endCombine();
+ _curActionObj = 0;
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ }
+ } else if (_input.mouseRightClicked() && _itemName.isItemSelected() && _point.getSpecialPointer() == RMPointer::PTR_NONE) {
+ if (_bGUIInterface) {
+ // Before opening the interface, replaces GOTO
+ _curAction = TA_GOTO;
+ _curActionObj = 0;
+ _point.setAction(_curAction);
+ _inter.clicked(_input.mousePos());
+ }
+ }
+
+
+ // Right Release
+ // *************
+ if (_input.mouseRightReleased()) {
+ if (_bGUIInterface) {
+ if (_inter.released(_input.mousePos(), _curAction)) {
+ _point.setAction(_curAction);
+ CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
+
+ _curAction = TA_GOTO;
+ _point.setAction(_curAction);
+ }
+ }
+ }
+ }
+
+ // Update the name under the mouse pointer
+ _itemName.setMouseCoord(_input.mousePos());
+ if (!_inter.active() && !_inv.miniActive())
+ CORO_INVOKE_4(_itemName.doFrame, _bigBuf, _loc, _point, _inv);
+ }
+
+ // Interface & Inventory
+ _inter.doFrame(_bigBuf, _input.mousePos());
+ _inv.doFrame(_bigBuf, _point, _input.mousePos(), (!_tony.inAction() && !_inter.active() && _bGUIInventory));
+ }
+
+ // Animate Tony
+ CORO_INVOKE_2(_tony.doFrame, &_bigBuf, _nCurLoc);
+
+ // Update screen scrolling to keep Tony in focus
+ if (_tony.mustUpdateScrolling() && _bLocationLoaded) {
+ RMPoint showThis = _tony.position();
+ showThis._y -= 60;
+ _loc.updateScrolling(showThis);
+ }
+
+ if (_bLocationLoaded)
+ _tony.setScrollPosition(_loc.scrollPosition());
+
+ if ((!_tony.inAction() && _bInput) || _bAlwaysDrawMouse) {
+ _point.showCursor();
+ } else {
+ _point.hideCursor();
+ }
+ _point.doFrame();
+
+ // **********************
+ // Draw the list in the OT
+ // **********************
+ CORO_INVOKE_0(_bigBuf.drawOT);
+
+#define FSTEP (480/32)
+
+ // Wipe
+ if (_bWiping) {
+ switch (_nWipeType) {
+ case 1:
+ if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) {
+ CoroScheduler.setEvent(_hWipeEvent);
+ _nWipeType = 3;
+ break;
+ }
+
+ _rcWipeEllipse.top += FSTEP;
+ _rcWipeEllipse.left += FSTEP;
+ _rcWipeEllipse.right -= FSTEP;
+ _rcWipeEllipse.bottom -= FSTEP;
+ break;
+
+ case 2:
+ if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) {
+ CoroScheduler.setEvent(_hWipeEvent);
+ _nWipeType = 3;
+ break;
+ }
+
+ _rcWipeEllipse.top -= FSTEP;
+ _rcWipeEllipse.left -= FSTEP;
+ _rcWipeEllipse.right += FSTEP;
+ _rcWipeEllipse.bottom += FSTEP;
+ break;
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::initCustomDll() {
+ setupGlobalVars(&_tony, &_point, &g_vm->_theBoxes, &_loc, &_inv, &_input);
+}
+
+void RMGfxEngine::itemIrq(uint32 dwItem, int nPattern, int nStatus) {
+ RMItem *item;
+ assert(GLOBALS._gfxEngine);
+
+ if (GLOBALS._gfxEngine->_bLocationLoaded) {
+ item = GLOBALS._gfxEngine->_loc.getItemFromCode(dwItem);
+ if (item != NULL) {
+ if (nPattern != -1) {
+ item->setPattern(nPattern, true);
+ }
+ if (nStatus != -1)
+ item->setStatus(nStatus);
+ }
+ }
+}
+
+void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
+ if (start._x == -1 || start._y == -1) {
+ start._x = ptTonyStart._x - RM_SX / 2;
+ start._y = ptTonyStart._y - RM_SY / 2;
+ }
+
+ _loc.setScrollPosition(start);
+
+ if (ptTonyStart._x == 0 && ptTonyStart._y == 0) {
+ } else {
+ _tony.setPosition(ptTonyStart, nLoc);
+ _tony.setScrollPosition(start);
+ }
+
+ _curAction = TA_GOTO;
+ _point.setCustomPointer(NULL);
+ _point.setSpecialPointer(RMPointer::PTR_NONE);
+ _point.setAction(_curAction);
+ _inter.reset();
+ _inv.reset();
+
+ mpalStartIdlePoll(_nCurLoc);
+}
+
+uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
+ bool bLoaded;
+ int i;
+
+ _nCurLoc = nLoc;
+
+ bLoaded = false;
+ for (i = 0; i < 5; i++) {
+ // Try the loading of the location
+ RMRes res(_nCurLoc);
+ if (!res.isValid())
+ continue;
+
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _loc.load(*ds);
+ delete ds;
+
+ initForNewLocation(nLoc, ptTonyStart, start);
+ bLoaded = true;
+ break;
+ }
+
+ if (!bLoaded)
+ error("Location was not loaded");
+
+ if (_bOption)
+ _opt.reInit(_bigBuf);
+
+ _bLocationLoaded = true;
+
+ // On entering the location
+ return CORO_INVALID_PID_VALUE; //mpalQueryDoAction(0, m_nCurLoc, 0);
+}
+
+void RMGfxEngine::unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Release the location
+ CORO_INVOKE_2(mpalEndIdlePoll, _nCurLoc, NULL);
+
+ // On Exit?
+ if (bDoOnExit) {
+ _ctx->h = mpalQueryDoAction(1, _nCurLoc, 0);
+ if (_ctx->h != CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
+ }
+
+ _bLocationLoaded = false;
+
+ _bigBuf.clearOT();
+ _loc.unload();
+
+ if (result != NULL)
+ *result = CORO_INVALID_PID_VALUE;
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::init() {
+ // Screen loading
+ RMResRaw *raw;
+ RMGfxSourceBuffer16 *load = NULL;
+ INIT_GFX16_FROMRAW(20038, load);
+ _bigBuf.addPrim(new RMGfxPrimitive(load));
+ _bigBuf.drawOT(Common::nullContext);
+ _bigBuf.clearOT();
+ delete load;
+
+ // Display 'Loading' screen
+ _bigBuf.addDirtyRect(Common::Rect(0, 0, RM_SX, RM_SY));
+ g_vm->_window.getNewFrame(*this, NULL);
+ g_vm->_window.repaint();
+
+ // Activate GUI
+ _bGUIOption = true;
+ _bGUIInterface = true;
+ _bGUIInventory = true;
+
+ GLOBALS._bSkipSfxNoLoop = false;
+ _bMustEnterMenu = false;
+ GLOBALS._bIdleExited = false;
+ _bOption = false;
+ _bWiping = false;
+ _hWipeEvent = CoroScheduler.createEvent(false, false);
+
+ // Initialize the IRQ function for items for MPAL
+ GLOBALS._gfxEngine = this;
+ mpalInstallItemIrq(itemIrq);
+
+ // Initialize the mouse pointer
+ _point.init();
+
+ // Initialize Tony
+ _tony.init();
+ _tony.linkToBoxes(&g_vm->_theBoxes);
+
+ // Initialize the inventory and the interface
+ _inv.init();
+ _inter.init();
+
+ // Download the location and set priorities @@@@@
+ _bLocationLoaded = false;
+
+ enableInput();
+
+ // Starting the game
+ _tony.executeAction(20, 1, 0);
+}
+
+void RMGfxEngine::close() {
+ _bigBuf.clearOT();
+
+ _inter.close();
+ _inv.close();
+ _tony.close();
+ _point.close();
+}
+
+void RMGfxEngine::enableInput() {
+ _bInput = true;
+}
+
+void RMGfxEngine::disableInput() {
+ _bInput = false;
+ _inter.reset();
+}
+
+void RMGfxEngine::enableMouse() {
+ _bAlwaysDrawMouse = true;
+}
+
+void RMGfxEngine::disableMouse() {
+ _bAlwaysDrawMouse = false;
+}
+
+void CharsSaveAll(Common::OutSaveFile *f);
+void CharsLoadAll(Common::InSaveFile *f);
+void MCharResetCodes();
+void SaveChangedHotspot(Common::OutSaveFile *f);
+void LoadChangedHotspot(Common::InSaveFile *f);
+void ReapplyChangedHotspot();
+
+void RestoreMusic(CORO_PARAM);
+void SaveMusic(Common::OutSaveFile *f);
+void LoadMusic(Common::InSaveFile *f);
+
+#define TONY_SAVEGAME_VERSION 8
+
+void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) {
+ Common::OutSaveFile *f;
+ byte *state;
+ uint thumbsize;
+ uint size;
+ int i;
+ char buf[4];
+ RMPoint tp = _tony.position();
+
+ // Saving: MPAL variables, current location, and Tony inventory position
+
+ // For now, we only save the MPAL state
+ size = mpalGetSaveStateSize();
+ state = new byte[size];
+ mpalSaveState(state);
+
+ thumbsize = 160 * 120 * 2;
+
+ buf[0] = 'R';
+ buf[1] = 'M';
+ buf[2] = 'S';
+ buf[3] = TONY_SAVEGAME_VERSION;
+
+ f = g_system->getSavefileManager()->openForSaving(fn);
+ if (f == NULL)
+ return;
+
+ f->write(buf, 4);
+ f->writeUint32LE(thumbsize);
+ f->write(curThumb, thumbsize);
+
+ // Difficulty level
+ i = mpalQueryGlobalVar("VERSIONEFACILE");
+ f->writeByte(i);
+
+ i = strlen(name.c_str());
+ f->writeByte(i);
+ f->write(name.c_str(), i);
+ f->writeUint32LE(_nCurLoc);
+ f->writeUint32LE(tp._x);
+ f->writeUint32LE(tp._y);
+
+ f->writeUint32LE(size);
+ f->write(state, size);
+ delete[] state;
+
+ // Inventory
+ size = _inv.getSaveStateSize();
+ state = new byte[size];
+ _inv.saveState(state);
+ f->writeUint32LE(size);
+ f->write(state, size);
+ delete[] state;
+
+ // boxes
+ size = g_vm->_theBoxes.getSaveStateSize();
+ state = new byte[size];
+ g_vm->_theBoxes.saveState(state);
+ f->writeUint32LE(size);
+ f->write(state, size);
+ delete[] state;
+
+ // New Ver5
+ bool bStat;
+
+ // Saves the state of the shepherdess and show yourself
+ bStat = _tony.getShepherdess();
+ f->writeByte(bStat);
+ bStat = _inter.getPerorate();
+ f->writeByte(bStat);
+
+ // Save the chars
+ CharsSaveAll(f);
+
+ // Save the options
+ f->writeByte(GLOBALS._bCfgInvLocked);
+ f->writeByte(GLOBALS._bCfgInvNoScroll);
+ f->writeByte(GLOBALS._bCfgTimerizedText);
+ f->writeByte(GLOBALS._bCfgInvUp);
+ f->writeByte(GLOBALS._bCfgAnni30);
+ f->writeByte(GLOBALS._bCfgAntiAlias);
+ f->writeByte(GLOBALS._bShowSubtitles);
+ f->writeByte(GLOBALS._bCfgTransparence);
+ f->writeByte(GLOBALS._bCfgInterTips);
+ f->writeByte(GLOBALS._bCfgDubbing);
+ f->writeByte(GLOBALS._bCfgMusic);
+ f->writeByte(GLOBALS._bCfgSFX);
+ f->writeByte(GLOBALS._nCfgTonySpeed);
+ f->writeByte(GLOBALS._nCfgTextSpeed);
+ f->writeByte(GLOBALS._nCfgDubbingVolume);
+ f->writeByte(GLOBALS._nCfgMusicVolume);
+ f->writeByte(GLOBALS._nCfgSFXVolume);
+
+ // Save the hotspots
+ SaveChangedHotspot(f);
+
+ // Save the music
+ SaveMusic(f);
+
+ f->finalize();
+ delete f;
+}
+
+void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) {
+ // PROBLEM: You should change the location in a separate process to do the OnEnter
+ CORO_BEGIN_CONTEXT;
+ Common::InSaveFile *f;
+ byte *state, *statecmp;
+ uint size, sizecmp;
+ char buf[4];
+ RMPoint tp;
+ int loc;
+ int ver;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->f = g_system->getSavefileManager()->openForLoading(fn);
+ if (_ctx->f == NULL)
+ return;
+ _ctx->f->read(_ctx->buf, 4);
+
+ if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') {
+ delete _ctx->f;
+ return;
+ }
+
+ _ctx->ver = _ctx->buf[3];
+
+ if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) {
+ delete _ctx->f;
+ return;
+ }
+
+ if (_ctx->ver >= 0x3) {
+ // There is a thumbnail. If the version is between 5 and 7, it's compressed
+ if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) {
+ _ctx->i = 0;
+ _ctx->i = _ctx->f->readUint32LE();
+ _ctx->f->seek(_ctx->i);
+ } else {
+ if (_ctx->ver >= 8)
+ // Skip thumbnail size
+ _ctx->f->skip(4);
+
+ _ctx->f->seek(160 * 120 * 2, SEEK_CUR);
+ }
+ }
+
+ if (_ctx->ver >= 0x5) {
+ // Skip the difficulty level
+ _ctx->f->seek(1, SEEK_CUR);
+ }
+
+ if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose
+ _ctx->i = _ctx->f->readByte();
+ _ctx->f->seek(_ctx->i, SEEK_CUR);
+ }
+
+ _ctx->loc = _ctx->f->readUint32LE();
+ _ctx->tp._x = _ctx->f->readUint32LE();
+ _ctx->tp._y = _ctx->f->readUint32LE();
+ _ctx->size = _ctx->f->readUint32LE();
+
+ if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) {
+ // MPAL was packed!
+ _ctx->sizecmp = _ctx->f->readUint32LE();
+ _ctx->state = new byte[_ctx->size];
+ _ctx->statecmp = new byte[_ctx->sizecmp];
+ _ctx->f->read(_ctx->statecmp, _ctx->sizecmp);
+ lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size);
+ delete[] _ctx->statecmp;
+ } else {
+ // Read uncompressed MPAL data
+ _ctx->state = new byte[_ctx->size];
+ _ctx->f->read(_ctx->state, _ctx->size);
+ }
+
+ mpalLoadState(_ctx->state);
+ delete[] _ctx->state;
+
+ // Inventory
+ _ctx->size = _ctx->f->readUint32LE();
+ _ctx->state = new byte[_ctx->size];
+ _ctx->f->read(_ctx->state, _ctx->size);
+ _inv.loadState(_ctx->state);
+ delete[] _ctx->state;
+
+ if (_ctx->ver >= 0x2) { // Versione 2: box please
+ _ctx->size = _ctx->f->readUint32LE();
+ _ctx->state = new byte[_ctx->size];
+ _ctx->f->read(_ctx->state, _ctx->size);
+ g_vm->_theBoxes.loadState(_ctx->state);
+ delete[] _ctx->state;
+ }
+
+ if (_ctx->ver >= 5) {
+ // Versione 5
+ bool bStat = false;
+
+ bStat = _ctx->f->readByte();
+ _tony.setShepherdess(bStat);
+ bStat = _ctx->f->readByte();
+ _inter.setPerorate(bStat);
+
+ CharsLoadAll(_ctx->f);
+ }
+
+ if (_ctx->ver >= 6) {
+ // Load options
+ GLOBALS._bCfgInvLocked = _ctx->f->readByte();
+ GLOBALS._bCfgInvNoScroll = _ctx->f->readByte();
+ GLOBALS._bCfgTimerizedText = _ctx->f->readByte();
+ GLOBALS._bCfgInvUp = _ctx->f->readByte();
+ GLOBALS._bCfgAnni30 = _ctx->f->readByte();
+ GLOBALS._bCfgAntiAlias = _ctx->f->readByte();
+ GLOBALS._bShowSubtitles = _ctx->f->readByte();
+ GLOBALS._bCfgTransparence = _ctx->f->readByte();
+ GLOBALS._bCfgInterTips = _ctx->f->readByte();
+ GLOBALS._bCfgDubbing = _ctx->f->readByte();
+ GLOBALS._bCfgMusic = _ctx->f->readByte();
+ GLOBALS._bCfgSFX = _ctx->f->readByte();
+ GLOBALS._nCfgTonySpeed = _ctx->f->readByte();
+ GLOBALS._nCfgTextSpeed = _ctx->f->readByte();
+ GLOBALS._nCfgDubbingVolume = _ctx->f->readByte();
+ GLOBALS._nCfgMusicVolume = _ctx->f->readByte();
+ GLOBALS._nCfgSFXVolume = _ctx->f->readByte();
+
+ // Load hotspots
+ LoadChangedHotspot(_ctx->f);
+ }
+
+ if (_ctx->ver >= 7) {
+ LoadMusic(_ctx->f);
+ }
+
+ delete _ctx->f;
+
+ CORO_INVOKE_2(unloadLocation, false, NULL);
+ loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1));
+ _tony.setPattern(RMTony::PAT_STANDRIGHT);
+
+ // On older versions, need to an enter action
+ if (_ctx->ver < 5)
+ mpalQueryDoAction(0, _ctx->loc, 0);
+ else {
+ // In the new ones, we just reset the mcode
+ MCharResetCodes();
+ }
+
+ if (_ctx->ver >= 6)
+ ReapplyChangedHotspot();
+
+ CORO_INVOKE_0(RestoreMusic);
+
+ _bGUIInterface = true;
+ _bGUIInventory = true;
+ _bGUIOption = true;
+
+ CORO_END_CODE;
+}
+
+void RMGfxEngine::pauseSound(bool bPause) {
+ if (_bLocationLoaded)
+ _loc.pauseSound(bPause);
+}
+
+void RMGfxEngine::initWipe(int type) {
+ _bWiping = true;
+ _nWipeType = type;
+ _nWipeStep = 0;
+
+ if (_nWipeType == 1)
+ _rcWipeEllipse = Common::Rect(80, 0, 640 - 80, 480);
+ else if (_nWipeType == 2)
+ _rcWipeEllipse = Common::Rect(320 - FSTEP, 240 - FSTEP, 320 + FSTEP, 240 + FSTEP);
+}
+
+void RMGfxEngine::closeWipe() {
+ _bWiping = false;
+}
+
+void RMGfxEngine::waitWipeEnd(CORO_PARAM) {
+ CoroScheduler.waitForSingleObject(coroParam, _hWipeEvent, CORO_INFINITE);
+}
+
+bool RMGfxEngine::canLoadSave() {
+ return _bInput && !_tony.inAction() && !g_vm->getIsDemo();
+}
+
+RMGfxEngine::operator RMGfxTargetBuffer &() {
+ return _bigBuf;
+}
+
+RMInput &RMGfxEngine::getInput() {
+ return _input;
+}
+
+RMPointer &RMGfxEngine::getPointer() {
+ return _point;
+}
+
+/**
+ * Link to graphic task
+ */
+void RMGfxEngine::linkGraphicTask(RMGfxTask *task) {
+ _bigBuf.addPrim(new RMGfxPrimitive(task));
+}
+
+void RMGfxEngine::setPerorate(bool bpal) {
+ _inter.setPerorate(bpal);
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/gfxengine.h b/engines/tony/gfxengine.h
new file mode 100644
index 0000000000..ab32a01972
--- /dev/null
+++ b/engines/tony/gfxengine.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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_GFXENGINE_H
+#define TONY_GFXENGINE_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/rect.h"
+#include "tony/mpal/memory.h"
+#include "tony/game.h"
+#include "tony/gfxcore.h"
+#include "tony/input.h"
+#include "tony/inventory.h"
+#include "tony/tonychar.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+class RMGfxEngine {
+private:
+ RMGfxTargetBuffer _bigBuf;
+ RMInput _input;
+ RMPointer _point;
+ RMLocation _loc;
+ RMOptionScreen _opt;
+ RMTony _tony;
+ RMInventory _inv;
+ RMInterface _inter;
+ RMTextItemName _itemName;
+
+ bool _bOption;
+ bool _bLocationLoaded;
+
+ bool _bInput;
+ bool _bAlwaysDrawMouse;
+
+ int _nCurLoc;
+ RMTonyAction _curAction;
+ int _curActionObj;
+
+ int _nWipeType;
+ uint32 _hWipeEvent;
+ int _nWipeStep;
+
+ bool _bMustEnterMenu;
+protected:
+ static void itemIrq(uint32 dwItem, int nPattern, int nStatus);
+ void initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start);
+public:
+ bool _bWiping;
+ Common::Rect _rcWipeEllipse;
+ bool _bGUIOption;
+ bool _bGUIInterface;
+ bool _bGUIInventory;
+public:
+ RMGfxEngine();
+ virtual ~RMGfxEngine();
+
+ // Draw the next frame
+ void doFrame(CORO_PARAM, bool bDrawLocation);
+
+ // Initializes the graphics engine
+ void init();
+
+ // Closes the graphics engine
+ void close();
+
+ // Warns when entering or exits the options menu
+ void openOptionScreen(CORO_PARAM, int type);
+
+ // Enables or disables mouse input
+ void enableInput();
+ void disableInput();
+
+ // Enables and disables mouse draw
+ void enableMouse();
+ void disableMouse();
+
+ operator RMGfxTargetBuffer &();
+ RMInput &getInput();
+ RMPointer &getPointer();
+
+ // Link to the custom function list
+ void initCustomDll();
+
+ // Link to graphic task
+ void linkGraphicTask(RMGfxTask *task);
+
+ // Manage a location
+ uint32 loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start);
+ void unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result);
+ int getCurrentLocation() const { return _nCurLoc; }
+
+ // State management
+ void saveState(const Common::String &fn, byte *curThumb, const Common::String &name);
+ void loadState(CORO_PARAM, const Common::String &fn);
+
+ // Pauses sound
+ void pauseSound(bool bPause);
+
+ // Wipe
+ void initWipe(int type);
+ void closeWipe();
+ void waitWipeEnd(CORO_PARAM);
+
+ void setPerorate(bool bpal);
+
+ bool canLoadSave();
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/globals.cpp b/engines/tony/globals.cpp
new file mode 100644
index 0000000000..8e4ae240a0
--- /dev/null
+++ b/engines/tony/globals.cpp
@@ -0,0 +1,135 @@
+/* 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/algorithm.h"
+#include "tony/globals.h"
+
+namespace Tony {
+
+Globals::Globals() {
+ _nextLoop = false;
+ _nextChannel = 0;
+ _nextSync = 0;
+ _curChannel = 0;
+ _flipflop = 0;
+ _curBackText = NULL;
+ _bTonyIsSpeaking = false;
+ _curChangedHotspot = 0;
+ _tony = NULL;
+ _pointer = NULL;
+ _boxes = NULL;
+ _loc = NULL;
+ _inventory = NULL;
+ _input = NULL;
+ _gfxEngine = NULL;
+ EnableGUI = NULL;
+ DisableGUI = NULL;
+
+ _dwTonyNumTexts = 0;
+ _bTonyInTexts = false;
+ _bStaticTalk = false;
+ _bAlwaysDisplay = false;
+ _bIdleExited = false;
+ _bSkipSfxNoLoop = false;
+ _bNoBullsEye = false;
+ _curDialog = 0;
+ _curSoundEffect = 0;
+ _bFadeOutStop = false;
+
+ _bSkipIdle = false;
+ _hSkipIdle = 0;
+ _lastMusic = 0;
+ _lastTappeto = 0;
+ Common::fill(&_ambiance[0], &_ambiance[200], 0);
+ _fullScreenMessageLoc = 0;
+
+ // MPAL global variables
+ _mpalError = 0;
+ _lpiifCustom = NULL;
+ _lplpFunctions = NULL;
+ _lplpFunctionStrings = NULL;
+ _nObjs = 0;
+ _nVars = 0;
+ _hVars = NULL;
+ _lpmvVars = NULL;
+ _nMsgs = 0;
+ _hMsgs = NULL;
+ _lpmmMsgs = NULL;
+ _nDialogs = 0;
+ _hDialogs = NULL;
+ _lpmdDialogs = NULL;
+ _nItems = 0;
+ _hItems = NULL;
+ _lpmiItems = NULL;
+ _nLocations = 0;
+ _hLocations = NULL;
+ _lpmlLocations = NULL;
+ _nScripts = 0;
+ _hScripts = NULL;
+ _lpmsScripts = NULL;
+ _nResources = 0;
+ _lpResources = NULL;
+ _bExecutingAction = false;
+ _bExecutingDialog = false;
+ Common::fill(&_nPollingLocations[0], &_nPollingLocations[MAXPOLLINGLOCATIONS], 0);
+ Common::fill(&_hEndPollingLocations[0], &_hEndPollingLocations[MAXPOLLINGLOCATIONS], 0);
+ Common::fill(&_pollingThreads[0], &_pollingThreads[MAXPOLLINGLOCATIONS], 0);
+ _hAskChoice = 0;
+ _hDoneChoice = 0;
+ _nExecutingAction = 0;
+ _nExecutingDialog = 0;
+ _nExecutingChoice = 0;
+ _nSelectedChoice = 0;
+ _nTonyNextTalkType = RMTony::TALK_NORMAL;
+ _saveTonyLoc = 0;
+
+ for (int i = 0; i < 16; ++i) {
+ Common::fill((byte *)&_character[i], (byte *)&_character[i] + sizeof(CharacterStruct), 0);
+ _isMChar[i] = false;
+ }
+
+ for (int i = 0; i < 10; ++i)
+ Common::fill((byte *)&_mCharacter[i], (byte *)&_mCharacter[i] + sizeof(MCharacterStruct), 0);
+ for (int i = 0; i < 256; ++i)
+ Common::fill((byte *)&_changedHotspot[i], (byte *)&_changedHotspot[i] + sizeof(ChangedHotspotStruct), 0);
+
+ // Set up globals that have explicit initial values
+ _bCfgInvLocked = false;
+ _bCfgInvNoScroll = false;
+ _bCfgTimerizedText = true;
+ _bCfgInvUp = false;
+ _bCfgAnni30 = false;
+ _bCfgAntiAlias = false;
+ _bCfgTransparence = true;
+ _bCfgInterTips = true;
+ _bShowSubtitles = true;
+ _nCfgTonySpeed = 3;
+ _nCfgTextSpeed = 5;
+ _bCfgDubbing = true;
+ _bCfgMusic = true;
+ _bCfgSFX = true;
+ _nCfgDubbingVolume = 10;
+ _nCfgMusicVolume = 7;
+ _nCfgSFXVolume = 10;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/globals.h b/engines/tony/globals.h
new file mode 100644
index 0000000000..d8d8d3eba5
--- /dev/null
+++ b/engines/tony/globals.h
@@ -0,0 +1,286 @@
+/* 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 TONY_GLOBALS
+#define TONY_GLOBALS
+
+#include "common/savefile.h"
+#include "tony/gfxengine.h"
+#include "tony/input.h"
+#include "tony/inventory.h"
+#include "tony/loc.h"
+#include "tony/tonychar.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpaldll.h"
+
+namespace Tony {
+
+#define AMBIANCE_CRICKETS 1
+#define AMBIANCE_CRICKETSMUFFLED 2
+#define AMBIANCE_CRICKETSWIND 3
+#define AMBIANCE_CRICKETSWIND1 4
+#define AMBIANCE_WIND 5
+#define AMBIANCE_SEA 6
+#define AMBIANCE_SEAHALFVOLUME 7
+
+struct CharacterStruct {
+ uint32 _code;
+ RMItem *_item;
+ byte _r, _g, _b;
+ int _talkPattern;
+ int _standPattern;
+ int _startTalkPattern, _endTalkPattern;
+ int _numTexts;
+
+ void save(Common::OutSaveFile *f) {
+ f->writeUint32LE(_code);
+ f->writeUint32LE(0);
+ f->writeByte(_r);
+ f->writeByte(_g);
+ f->writeByte(_b);
+ f->writeUint32LE(_talkPattern);
+ f->writeUint32LE(_standPattern);
+ f->writeUint32LE(_startTalkPattern);
+ f->writeUint32LE(_endTalkPattern);
+ f->writeUint32LE(_numTexts);
+ }
+ void load(Common::InSaveFile *f) {
+ _code = f->readUint32LE();
+ f->readUint32LE();
+ _item = NULL;
+ _r = f->readByte();
+ _g = f->readByte();
+ _b = f->readByte();
+ _talkPattern = f->readUint32LE();
+ _standPattern = f->readUint32LE();
+ _startTalkPattern = f->readUint32LE();
+ _endTalkPattern = f->readUint32LE();
+ _numTexts = f->readUint32LE();
+ }
+};
+
+struct MCharacterStruct {
+ uint32 _code;
+ RMItem *_item;
+ byte _r, _g, _b;
+ int _x, _y;
+ int _numTalks[10];
+ int _curGroup;
+ int _numTexts;
+ bool _bInTexts;
+ int _curTalk;
+ bool _bAlwaysBack;
+
+ void save(Common::OutSaveFile *f) {
+ f->writeUint32LE(_code);
+ f->writeUint32LE(0);
+ f->writeByte(_r);
+ f->writeByte(_g);
+ f->writeByte(_b);
+ f->writeUint32LE(_x);
+ f->writeUint32LE(_y);
+ for (int i = 0; i < 10; ++i)
+ f->writeUint32LE(_numTalks[i]);
+ f->writeUint32LE(_curGroup);
+ f->writeUint32LE(_numTexts);
+ f->writeByte(_bInTexts);
+ f->writeUint32LE(_curTalk);
+ f->writeByte(_bAlwaysBack);
+ }
+ void load(Common::InSaveFile *f) {
+ _code = f->readUint32LE();
+ f->readUint32LE();
+ _item = NULL;
+ _r = f->readByte();
+ _g = f->readByte();
+ _b = f->readByte();
+ _x = f->readUint32LE();
+ _y = f->readUint32LE();
+ for (int i = 0; i < 10; ++i)
+ _numTalks[i] = f->readUint32LE();
+ _curGroup = f->readUint32LE();
+ _numTexts = f->readUint32LE();
+ _bInTexts = f->readByte();
+ _curTalk = f->readUint32LE();
+ _bAlwaysBack = f->readByte();
+ }
+};
+
+struct ChangedHotspotStruct {
+ uint32 _dwCode;
+ uint32 _nX, _nY;
+
+ void save(Common::OutSaveFile *f) {
+ f->writeUint32LE(_dwCode);
+ f->writeUint32LE(_nX);
+ f->writeUint32LE(_nY);
+ }
+ void load(Common::InSaveFile *f) {
+ _dwCode = f->readUint32LE();
+ _nX = f->readUint32LE();
+ _nY = f->readUint32LE();
+ }
+};
+
+
+/**
+ * Description of a call to a custom function.
+ */
+typedef struct {
+ int _nCf;
+ int _arg1, _arg2, _arg3, _arg4;
+} CfCall;
+
+typedef CfCall *LpCfCall;
+
+struct CoroutineMutex {
+ CoroutineMutex() : _eventId(0), _ownerPid(0), _lockCount(0) { }
+
+ uint32 _eventId;
+ uint32 _ownerPid;
+ uint32 _lockCount;
+};
+
+/****************************************************************************\
+* Global variables
+\****************************************************************************/
+
+/**
+ * Globals class
+ */
+class Globals {
+public:
+ Globals();
+
+ Common::String _nextMusic;
+ bool _nextLoop;
+ int _nextChannel;
+ int _nextSync;
+ int _curChannel;
+ int _flipflop;
+ CharacterStruct _character[16];
+ MCharacterStruct _mCharacter[10];
+ ChangedHotspotStruct _changedHotspot[256];
+ bool _isMChar[16];
+ bool _bAlwaysDisplay;
+ RMPoint _saveTonyPos;
+ int _saveTonyLoc;
+ RMTextDialog *_curBackText;
+ bool _bTonyIsSpeaking;
+ int _curChangedHotspot;
+ bool _bCfgInvLocked;
+ bool _bCfgInvNoScroll;
+ bool _bCfgTimerizedText;
+ bool _bCfgInvUp;
+ bool _bCfgAnni30;
+ bool _bCfgAntiAlias;
+ bool _bShowSubtitles;
+ bool _bCfgTransparence;
+ bool _bCfgInterTips;
+ bool _bCfgDubbing;
+ bool _bCfgMusic;
+ bool _bCfgSFX;
+ int _nCfgTonySpeed;
+ int _nCfgTextSpeed;
+ int _nCfgDubbingVolume;
+ int _nCfgMusicVolume;
+ int _nCfgSFXVolume;
+ bool _bSkipSfxNoLoop;
+ bool _bIdleExited;
+ bool _bNoBullsEye;
+ int _curDialog;
+ int _curSoundEffect;
+ bool _bFadeOutStop;
+
+ RMTony *_tony;
+ RMPointer *_pointer;
+ RMGameBoxes *_boxes;
+ RMLocation *_loc;
+ RMInventory *_inventory;
+ RMInput *_input;
+ RMGfxEngine *_gfxEngine;
+
+ void (*EnableGUI)();
+ void (*DisableGUI)();
+
+ uint32 _dwTonyNumTexts;
+ bool _bTonyInTexts;
+ bool _bStaticTalk;
+ RMTony::CharacterTalkType _nTonyNextTalkType;
+
+ RMPoint _startLocPos[256];
+ CoroutineMutex _mut[10];
+
+ bool _bSkipIdle;
+ uint32 _hSkipIdle;
+
+ int _lastMusic, _lastTappeto;
+
+ int _ambiance[200];
+ RMPoint _fullScreenMessagePt;
+ int _fullScreenMessageLoc;
+
+ /**
+ * @defgroup MPAL variables
+ */
+ uint32 _mpalError;
+ LPITEMIRQFUNCTION _lpiifCustom;
+ LPLPCUSTOMFUNCTION _lplpFunctions;
+ Common::String *_lplpFunctionStrings;
+ uint16 _nObjs;
+ uint16 _nVars;
+ MpalHandle _hVars;
+ LpMpalVar _lpmvVars;
+ uint16 _nMsgs;
+ MpalHandle _hMsgs;
+ LpMpalMsg _lpmmMsgs;
+ uint16 _nDialogs;
+ MpalHandle _hDialogs;
+ LpMpalDialog _lpmdDialogs;
+ uint16 _nItems;
+ MpalHandle _hItems;
+ LpMpalItem _lpmiItems;
+ uint16 _nLocations;
+ MpalHandle _hLocations;
+ LpMpalLocation _lpmlLocations;
+ uint16 _nScripts;
+ MpalHandle _hScripts;
+ LpMpalScript _lpmsScripts;
+ Common::File _hMpr;
+ uint16 _nResources;
+ uint32 *_lpResources;
+ bool _bExecutingAction;
+ bool _bExecutingDialog;
+ uint32 _nPollingLocations[MAXPOLLINGLOCATIONS];
+ uint32 _hEndPollingLocations[MAXPOLLINGLOCATIONS];
+ uint32 _pollingThreads[MAXPOLLINGLOCATIONS];
+ uint32 _hAskChoice;
+ uint32 _hDoneChoice;
+ uint32 _nExecutingAction;
+ uint32 _nExecutingDialog;
+ uint32 _nExecutingChoice;
+ uint32 _nSelectedChoice;
+};
+
+} // End of namespace Tony
+
+#endif // TONY_GLOBALS
diff --git a/engines/tony/input.cpp b/engines/tony/input.cpp
new file mode 100644
index 0000000000..b96ccaf842
--- /dev/null
+++ b/engines/tony/input.cpp
@@ -0,0 +1,157 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/gfxengine.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+RMInput::RMInput() {
+ // Setup mouse fields
+ _clampMouse = false;
+ _mousePos.set(0, 0);
+ _leftButton = _rightButton = false;
+ _leftClickMouse = _leftReleaseMouse = false;
+ _rightClickMouse = _rightReleaseMouse = false;
+
+ Common::fill((byte *)&_event, (byte *)&_event + sizeof(Common::Event), 0);
+
+ // Setup keyboard fields
+ Common::fill(&_keyDown[0], &_keyDown[350], 0);
+}
+
+RMInput::~RMInput() {
+}
+
+void RMInput::poll() {
+ _leftClickMouse = _leftReleaseMouse = _rightClickMouse = _rightReleaseMouse = false;
+
+ // Get pending events
+ while (g_system->getEventManager()->pollEvent(_event) && !g_vm->shouldQuit()) {
+ switch (_event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ _mousePos.set(_event.mouse.x, _event.mouse.y);
+
+ if (_event.type == Common::EVENT_LBUTTONDOWN) {
+ _leftButton = true;
+ _leftClickMouse = true;
+ } else if (_event.type == Common::EVENT_LBUTTONUP) {
+ _leftButton = false;
+ _leftReleaseMouse = true;
+ } else if (_event.type == Common::EVENT_RBUTTONDOWN) {
+ _rightButton = true;
+ _rightClickMouse = true;
+ } else if (_event.type == Common::EVENT_RBUTTONUP) {
+ _rightButton = false;
+ _rightReleaseMouse = true;
+ } else
+ continue;
+
+ // Since a mouse button has changed, don't do any further event processing this frame
+ return;
+
+ case Common::EVENT_KEYDOWN:
+ // Check for debugger
+ if ((_event.kbd.keycode == Common::KEYCODE_d) && (_event.kbd.flags & Common::KBD_CTRL)) {
+ // Attach to the debugger
+ g_vm->_debugger->attach();
+ g_vm->_debugger->onFrame();
+ } else {
+ // Flag the given key as being down
+ _keyDown[(int)_event.kbd.keycode] = true;
+ }
+ return;
+
+ case Common::EVENT_KEYUP:
+ _keyDown[(int)_event.kbd.keycode] = false;
+ return;
+
+ default:
+ break;
+ }
+ }
+}
+
+bool RMInput::mouseLeft() {
+ return _leftButton;
+}
+
+bool RMInput::mouseRight() {
+ return _rightButton;
+}
+
+/**
+ * Return true if a key has been pressed
+ */
+bool RMInput::getAsyncKeyState(Common::KeyCode kc) {
+ // The act of testing for a particular key automatically clears the state, to prevent
+ // the same key being registered in multiple different frames
+ bool result = _keyDown[(int)kc];
+ _keyDown[(int)kc] = false;
+ return result;
+}
+
+/**
+ * Reading of the mouse
+ */
+RMPoint RMInput::mousePos() {
+ return _mousePos;
+}
+
+/**
+ * Events of mouse clicks
+ */
+bool RMInput::mouseLeftClicked() {
+ return _leftClickMouse;
+}
+
+bool RMInput::mouseRightClicked() {
+ return _rightClickMouse;
+}
+
+bool RMInput::mouseBothClicked() {
+ return _leftClickMouse && _rightClickMouse;
+}
+
+bool RMInput::mouseLeftReleased() {
+ return _leftReleaseMouse;
+}
+
+bool RMInput::mouseRightReleased() {
+ return _rightReleaseMouse;
+}
+
+bool RMInput::mouseBothReleased() {
+ return _leftReleaseMouse && _rightReleaseMouse;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/input.h b/engines/tony/input.h
new file mode 100644
index 0000000000..d07eaefe34
--- /dev/null
+++ b/engines/tony/input.h
@@ -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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_INPUT_H
+#define TONY_INPUT_H
+
+#include "common/events.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+class RMInput {
+private:
+ Common::Event _event;
+
+ // Mouse related fields
+ RMPoint _mousePos;
+ bool _clampMouse;
+ bool _leftButton, _rightButton;
+ bool _leftClickMouse, _leftReleaseMouse, _rightClickMouse, _rightReleaseMouse;
+
+ // Keyboard related fields
+ bool _keyDown[350];
+
+public:
+ RMInput();
+ ~RMInput();
+
+ /**
+ * Polling (must be performed once per frame)
+ */
+ void poll();
+
+ /**
+ * Reading of the mouse
+ */
+ RMPoint mousePos();
+
+ /**
+ * Current status of the mouse buttons
+ */
+ bool mouseLeft();
+ bool mouseRight();
+
+ /**
+ * Events of mouse clicks
+ */
+ bool mouseLeftClicked();
+ bool mouseRightClicked();
+ bool mouseBothClicked();
+ bool mouseLeftReleased();
+ bool mouseRightReleased();
+ bool mouseBothReleased();
+
+ /**
+ * Returns true if the given key is pressed
+ */
+ bool getAsyncKeyState(Common::KeyCode kc);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/inventory.cpp b/engines/tony/inventory.cpp
new file mode 100644
index 0000000000..81d62a035c
--- /dev/null
+++ b/engines/tony/inventory.cpp
@@ -0,0 +1,953 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/textconsole.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/inventory.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+
+/****************************************************************************\
+* RMInventory Methods
+\****************************************************************************/
+
+RMInventory::RMInventory() {
+ _items = NULL;
+ _state = CLOSED;
+ _bCombining = false;
+ _csModifyInterface = g_system->createMutex();
+ _nItems = 0;
+
+ Common::fill(_inv, _inv + 256, 0);
+ _nInv = 0;
+ _curPutY = 0;
+ _curPutTime = 0;
+ _curPos = 0;
+ _bHasFocus = false;
+ _nSelectObj = 0;
+ _nCombine = 0;
+ _bBlinkingRight = false;
+ _bBlinkingLeft = false;
+ _miniAction = 0;
+}
+
+RMInventory::~RMInventory() {
+ close();
+ g_system->deleteMutex(_csModifyInterface);
+}
+
+bool RMInventory::checkPointInside(const RMPoint &pt) {
+ if (!GLOBALS._bCfgInvUp)
+ return pt._y > RM_SY - 70;
+ else
+ return pt._y < 70;
+}
+
+
+void RMInventory::init() {
+ int i, j;
+ int curres;
+
+ // Create the main buffer
+ create(RM_SX, 68);
+ setPriority(185);
+
+ // Setup the inventory
+ _nInv = 0;
+ _curPos = 0;
+ _bCombining = false;
+
+ // New items
+ _nItems = 78; // @@@ Number of takeable items
+ _items = new RMInventoryItem[_nItems + 1];
+
+ curres = 10500;
+
+ // Loop through the items
+ for (i = 0; i <= _nItems; i++) {
+ // Load the items from the resource
+ RMRes res(curres);
+ assert(res.isValid());
+ Common::SeekableReadStream *ds = res.getReadStream();
+
+ // Initialize the MPAL inventory item by reading it in.
+ _items[i]._icon.setInitCurPattern(false);
+ _items[i]._icon.readFromStream(*ds);
+ delete ds;
+
+ // Puts in the default pattern 1
+ _items[i]._pointer = NULL;
+ _items[i]._status = 1;
+ _items[i]._icon.setPattern(1);
+ _items[i]._icon.doFrame(this, false);
+
+ curres++;
+ if (i == 0 || i == 28 || i == 29)
+ continue;
+
+ _items[i]._pointer = new RMGfxSourceBuffer8RLEByteAA[_items[i]._icon.numPattern()];
+
+ for (j = 0; j < _items[i]._icon.numPattern(); j++) {
+ RMResRaw raw(curres);
+
+ assert(raw.isValid());
+
+ _items[i]._pointer[j].init((const byte *)raw, raw.width(), raw.height(), true);
+ curres++;
+ }
+ }
+
+ _items[28]._icon.setPattern(1);
+ _items[29]._icon.setPattern(1);
+
+ // Download interface
+ RMRes res(RES_I_MINIINTER);
+ assert(res.isValid());
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _miniInterface.readFromStream(*ds);
+ _miniInterface.setPattern(1);
+ delete ds;
+
+ // Create the text for hints on the mini interface
+ _hints[0].setAlignType(RMText::HCENTER, RMText::VTOP);
+ _hints[1].setAlignType(RMText::HCENTER, RMText::VTOP);
+ _hints[2].setAlignType(RMText::HCENTER, RMText::VTOP);
+
+ // The text is taken from MPAL for translation
+ RMMessage msg1(15);
+ RMMessage msg2(13);
+ RMMessage msg3(14);
+
+ _hints[0].writeText(msg1[0], 1); // Examine
+ _hints[1].writeText(msg2[0], 1); // Take
+ _hints[2].writeText(msg3[0], 1); // Use
+
+
+ // Prepare initial inventory
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+}
+
+void RMInventory::close() {
+ // Has memory
+ if (_items != NULL) {
+ // Delete the item pointers
+ for (int i = 0; i <= _nItems; i++)
+ delete[] _items[i]._pointer;
+
+ // Delete the items array
+ delete[] _items;
+ _items = NULL;
+ }
+
+ destroy();
+}
+
+void RMInventory::reset() {
+ _state = CLOSED;
+ endCombine();
+}
+
+void RMInventory::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint pos;
+ RMPoint pos2;
+ RMGfxPrimitive *p;
+ RMGfxPrimitive *p2;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ prim->setDst(RMPoint(0, _curPutY));
+ g_system->lockMutex(_csModifyInterface);
+ CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
+ g_system->unlockMutex(_csModifyInterface);
+
+ if (_state == SELECTING) {
+
+ if (!GLOBALS._bCfgInvUp) {
+ _ctx->pos.set((_nSelectObj + 1) * 64 - 20, RM_SY - 113);
+ _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, RM_SY - 150);
+ } else {
+ _ctx->pos.set((_nSelectObj + 1) * 64 - 20, 72 - 4); // The brown part is at the top :(
+ _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, 119 - 4);
+ }
+
+ _ctx->p = new RMGfxPrimitive(prim->_task, _ctx->pos);
+ _ctx->p2 = new RMGfxPrimitive(prim->_task, _ctx->pos2);
+
+ // Draw the mini interface
+ CORO_INVOKE_2(_miniInterface.draw, bigBuf, _ctx->p);
+
+ if (GLOBALS._bCfgInterTips) {
+ if (_miniAction == 1) // Examine
+ CORO_INVOKE_2(_hints[0].draw, bigBuf, _ctx->p2);
+ else if (_miniAction == 2) // Talk
+ CORO_INVOKE_2(_hints[1].draw, bigBuf, _ctx->p2);
+ else if (_miniAction == 3) // Use
+ CORO_INVOKE_2(_hints[2].draw, bigBuf, _ctx->p2);
+ }
+
+ delete _ctx->p;
+ delete _ctx->p2;
+ }
+
+ CORO_END_CODE;
+}
+
+void RMInventory::removeThis(CORO_PARAM, bool &result) {
+ if (_state == CLOSED)
+ result = true;
+ else
+ result = false;
+}
+
+void RMInventory::removeItem(int code) {
+ int i;
+
+ for (i = 0; i < _nInv; i++)
+ if (_inv[i] == code - 10000) {
+ g_system->lockMutex(_csModifyInterface);
+
+ Common::copy(&_inv[i + 1], &_inv[i + 1] + (_nInv - i), &_inv[i]);
+ _nInv--;
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ return;
+ }
+}
+
+void RMInventory::addItem(int code) {
+ if (code <= 10000 || code >= 10101) {
+ // If we are here, it means that we are adding an item that should not be in the inventory
+ warning("RMInventory::addItem(%d) - Cannot find a valid icon for this item, and then it will not be added to the inventory", code);
+ } else {
+ g_system->lockMutex(_csModifyInterface);
+ if (_curPos + 8 == _nInv) {
+ // Break through the inventory! On the flashing pattern
+ _items[28]._icon.setPattern(2);
+ }
+
+ _inv[_nInv++] = code - 10000;
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+}
+
+void RMInventory::changeItemStatus(uint32 code, uint32 dwStatus) {
+ if (code <= 10000 || code >= 10101) {
+ error("RMInventory::changeItemStatus(%d) - Specified object code is not valid", code);
+ } else {
+ g_system->lockMutex(_csModifyInterface);
+ _items[code - 10000]._icon.setPattern(dwStatus);
+ _items[code - 10000]._status = dwStatus;
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+}
+
+
+void RMInventory::prepare() {
+ int i;
+
+ for (i = 1; i < RM_SX / 64 - 1; i++) {
+ if (i - 1 + _curPos < _nInv)
+ addPrim(new RMGfxPrimitive(&_items[_inv[i - 1 + _curPos]]._icon, RMPoint(i * 64, 0)));
+ else
+ addPrim(new RMGfxPrimitive(&_items[0]._icon, RMPoint(i * 64, 0)));
+ }
+
+ // Frecce
+ addPrim(new RMGfxPrimitive(&_items[29]._icon, RMPoint(0, 0)));
+ addPrim(new RMGfxPrimitive(&_items[28]._icon, RMPoint(640 - 64, 0)));
+}
+
+bool RMInventory::miniActive() {
+ return _state == SELECTING;
+}
+
+bool RMInventory::haveFocus(const RMPoint &mpos) {
+ // When we combine, have the focus only if we are on an arrow (to scroll)
+ if (_state == OPENED && _bCombining && checkPointInside(mpos) && (mpos._x < 64 || mpos._x > RM_SX - 64))
+ return true;
+
+ // If the inventory is open, focus we we go over it
+ if (_state == OPENED && !_bCombining && checkPointInside(mpos))
+ return true;
+
+ // If we are selecting a verb (and then right down), we always focus
+ if (_state == SELECTING)
+ return true;
+
+ return false;
+}
+
+void RMInventory::endCombine() {
+ _bCombining = false;
+}
+
+bool RMInventory::leftClick(const RMPoint &mpos, int &nCombineObj) {
+ int n;
+
+ // The left click picks an item from your inventory to use it with the background
+ n = mpos._x / 64;
+
+ if (_state == OPENED) {
+ if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
+ _bCombining = true; //m_state = COMBINING;
+ _nCombine = _inv[n - 1 + _curPos];
+ nCombineObj = _nCombine + 10000;
+
+ g_vm->playUtilSFX(1);
+ return true;
+ }
+ }
+
+ // Click the right arrow
+ if ((_state == OPENED) && _bBlinkingRight) {
+ g_system->lockMutex(_csModifyInterface);
+ _curPos++;
+
+ if (_curPos + 8 >= _nInv) {
+ _bBlinkingRight = false;
+ _items[28]._icon.setPattern(1);
+ }
+
+ if (_curPos > 0) {
+ _bBlinkingLeft = true;
+ _items[29]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+ // Click the left arrow
+ else if ((_state == OPENED) && _bBlinkingLeft) {
+ assert(_curPos > 0);
+ g_system->lockMutex(_csModifyInterface);
+ _curPos--;
+
+ if (_curPos == 0) {
+ _bBlinkingLeft = false;
+ _items[29]._icon.setPattern(1);
+ }
+
+ if (_curPos + 8 < _nInv) {
+ _bBlinkingRight = true;
+ _items[28]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+
+
+ return false;
+}
+
+void RMInventory::rightClick(const RMPoint &mpos) {
+ assert(checkPointInside(mpos));
+
+ if (_state == OPENED && !_bCombining) {
+ // Open the context interface
+ int n = mpos._x / 64;
+
+ if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
+ _state = SELECTING;
+ _miniAction = 0;
+ _nSelectObj = n - 1;
+
+ g_vm->playUtilSFX(0);
+ }
+ }
+
+ if ((_state == OPENED) && _bBlinkingRight) {
+ g_system->lockMutex(_csModifyInterface);
+ _curPos += 7;
+ if (_curPos + 8 > _nInv)
+ _curPos = _nInv - 8;
+
+ if (_curPos + 8 <= _nInv) {
+ _bBlinkingRight = false;
+ _items[28]._icon.setPattern(1);
+ }
+
+ if (_curPos > 0) {
+ _bBlinkingLeft = true;
+ _items[29]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ } else if ((_state == OPENED) && _bBlinkingLeft) {
+ assert(_curPos > 0);
+ g_system->lockMutex(_csModifyInterface);
+ _curPos -= 7;
+ if (_curPos < 0)
+ _curPos = 0;
+
+ if (_curPos == 0) {
+ _bBlinkingLeft = false;
+ _items[29]._icon.setPattern(1);
+ }
+
+ if (_curPos + 8 < _nInv) {
+ _bBlinkingRight = true;
+ _items[28]._icon.setPattern(2);
+ }
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+ g_system->unlockMutex(_csModifyInterface);
+ }
+}
+
+bool RMInventory::rightRelease(const RMPoint &mpos, RMTonyAction &curAction) {
+ if (_state == SELECTING) {
+ _state = OPENED;
+
+ if (_miniAction == 1) { // Examine
+ curAction = TA_EXAMINE;
+ return true;
+ } else if (_miniAction == 2) { // Talk
+ curAction = TA_TALK;
+ return true;
+ } else if (_miniAction == 3) { // Use
+ curAction = TA_USE;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define INVSPEED 20
+
+void RMInventory::doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen) {
+ bool bNeedRedraw = false;
+
+ if (_state != CLOSED) {
+ // Clean up the OT list
+ g_system->lockMutex(_csModifyInterface);
+ clearOT();
+
+ // DoFrame makes all the objects currently in the inventory be displayed
+ // @@@ Maybe we should do all takeable objects? Please does not help
+ for (int i = 0; i < _nInv; i++) {
+ if (_items[_inv[i]]._icon.doFrame(this, false) && (i >= _curPos && i <= _curPos + 7))
+ bNeedRedraw = true;
+ }
+
+ if ((_state == CLOSING || _state == OPENING || _state == OPENED) && checkPointInside(mpos)) {
+ if (mpos._x > RM_SX - 64) {
+ if (_curPos + 8 < _nInv && !_bBlinkingRight) {
+ _items[28]._icon.setPattern(3);
+ _bBlinkingRight = true;
+ bNeedRedraw = true;
+ }
+ } else if (_bBlinkingRight) {
+ _items[28]._icon.setPattern(2);
+ _bBlinkingRight = false;
+ bNeedRedraw = true;
+ }
+
+ if (mpos._x < 64) {
+ if (_curPos > 0 && !_bBlinkingLeft) {
+ _items[29]._icon.setPattern(3);
+ _bBlinkingLeft = true;
+ bNeedRedraw = true;
+ }
+ } else if (_bBlinkingLeft) {
+ _items[29]._icon.setPattern(2);
+ _bBlinkingLeft = false;
+ bNeedRedraw = true;
+ }
+ }
+
+ if (_items[28]._icon.doFrame(this, false))
+ bNeedRedraw = true;
+
+ if (_items[29]._icon.doFrame(this, false))
+ bNeedRedraw = true;
+
+ if (bNeedRedraw)
+ prepare();
+
+ g_system->unlockMutex(_csModifyInterface);
+ }
+
+ if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_i)) {
+ GLOBALS._bCfgInvLocked = !GLOBALS._bCfgInvLocked;
+ }
+
+ if (_bCombining) {//m_state == COMBINING)
+ ptr.setCustomPointer(&_items[_nCombine]._pointer[_items[_nCombine]._status - 1]);
+ ptr.setSpecialPointer(RMPointer::PTR_CUSTOM);
+ }
+
+ if (!GLOBALS._bCfgInvUp) {
+ if ((_state == CLOSED) && (mpos._y > RM_SY - 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = OPENING;
+ _curPutY = RM_SY - 1;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = OPENED;
+ _curPutY = RM_SY - 68;
+ }
+ } else if (_state == OPENED) {
+ if ((mpos._y < RM_SY - 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = CLOSING;
+ _curPutY = RM_SY - 68;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = CLOSED;
+ }
+ }
+ } else if (_state == OPENING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY -= 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY <= RM_SY - 68) {
+ _state = OPENED;
+ _curPutY = RM_SY - 68;
+ }
+
+ } else if (_state == CLOSING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY += 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY > 480)
+ _state = CLOSED;
+ }
+ } else {
+ if ((_state == CLOSED) && (mpos._y < 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = OPENING;
+ _curPutY = - 68;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = OPENED;
+ _curPutY = 0;
+ }
+ } else if (_state == OPENED) {
+ if ((mpos._y > 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
+ if (!GLOBALS._bCfgInvNoScroll) {
+ _state = CLOSING;
+ _curPutY = -2;
+ _curPutTime = g_vm->getTime();
+ } else {
+ _state = CLOSED;
+ }
+ }
+ } else if (_state == OPENING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY += 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY >= 0) {
+ _state = OPENED;
+ _curPutY = 0;
+ }
+ } else if (_state == CLOSING) {
+ while (_curPutTime + INVSPEED < g_vm->getTime()) {
+ _curPutY -= 3;
+ _curPutTime += INVSPEED;
+ }
+
+ if (_curPutY < -68)
+ _state = CLOSED;
+ }
+ }
+
+ if (_state == SELECTING) {
+ int startx = (_nSelectObj + 1) * 64 - 20;
+ int starty;
+
+ if (!GLOBALS._bCfgInvUp)
+ starty = RM_SY - 109;
+ else
+ starty = 70;
+
+ // Make sure it is on one of the verbs
+ if (mpos._y > starty && mpos._y < starty + 45) {
+ if (mpos._x > startx && mpos._x < startx + 40) {
+ if (_miniAction != 1) {
+ _miniInterface.setPattern(2);
+ _miniAction = 1;
+ g_vm->playUtilSFX(1);
+ }
+ } else if (mpos._x >= startx + 40 && mpos._x < startx + 80) {
+ if (_miniAction != 2) {
+ _miniInterface.setPattern(3);
+ _miniAction = 2;
+ g_vm->playUtilSFX(1);
+ }
+ } else if (mpos._x >= startx + 80 && mpos._x < startx + 108) {
+ if (_miniAction != 3) {
+ _miniInterface.setPattern(4);
+ _miniAction = 3;
+ g_vm->playUtilSFX(1);
+ }
+ } else {
+ _miniInterface.setPattern(1);
+ _miniAction = 0;
+ }
+ } else {
+ _miniInterface.setPattern(1);
+ _miniAction = 0;
+ }
+
+ // Update the mini-interface
+ _miniInterface.doFrame(&bigBuf, false);
+ }
+
+ if ((_state != CLOSED) && !_nInList) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+ }
+}
+
+bool RMInventory::itemInFocus(const RMPoint &mpt) {
+ if ((_state == OPENED || _state == OPENING) && checkPointInside(mpt))
+ return true;
+ else
+ return false;
+}
+
+RMItem *RMInventory::whichItemIsIn(const RMPoint &mpt) {
+ if (_state == OPENED) {
+ if (checkPointInside(mpt)) {
+ int n = mpt._x / 64;
+ if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0 && (!_bCombining || _inv[n - 1 + _curPos] != _nCombine))
+ return &_items[_inv[n - 1 + _curPos]]._icon;
+ }
+ }
+
+ return NULL;
+}
+
+int RMInventory::getSaveStateSize() {
+ // m_inv pattern m_nInv
+ return 256 * 4 + 256 * 4 + 4 ;
+}
+
+void RMInventory::saveState(byte *state) {
+ int i, x;
+
+ WRITE_LE_UINT32(state, _nInv);
+ state += 4;
+ Common::copy(_inv, _inv + 256, (uint32 *)state);
+ state += 256 * 4;
+
+ for (i = 0; i < 256; i++) {
+ if (i < _nItems)
+ x = _items[i]._status;
+ else
+ x = 0;
+
+ WRITE_LE_UINT32(state, x);
+ state += 4;
+ }
+}
+
+int RMInventory::loadState(byte *state) {
+ int i, x;
+
+ _nInv = READ_LE_UINT32(state);
+ state += 4;
+ Common::copy((uint32 *)state, (uint32 *)state + 256, _inv);
+ state += 256 * 4;
+
+ for (i = 0; i < 256; i++) {
+ x = READ_LE_UINT32(state);
+ state += 4;
+
+ if (i < _nItems) {
+ _items[i]._status = x;
+ _items[i]._icon.setPattern(x);
+ }
+ }
+
+ _curPos = 0;
+ _bCombining = false;
+
+ _items[29]._icon.setPattern(1);
+
+ if (_nInv > 8)
+ _items[28]._icon.setPattern(2);
+ else
+ _items[28]._icon.setPattern(1);
+
+ prepare();
+ drawOT(Common::nullContext);
+ clearOT();
+
+ return getSaveStateSize();
+}
+
+RMInventory &RMInventory::operator+=(RMItem *item) {
+ addItem(item->mpalCode());
+ return *this;
+}
+
+RMInventory &RMInventory::operator+=(RMItem &item) {
+ addItem(item.mpalCode());
+ return *this;
+}
+
+RMInventory &RMInventory::operator+=(int code) {
+ addItem(code);
+ return *this;
+}
+
+/****************************************************************************\
+* RMInterface methods
+\****************************************************************************/
+
+RMInterface::RMInterface() : RMGfxSourceBuffer8RLEByte() {
+ _bActive = _bPerorate = false;
+ _lastHotZone = -1;
+}
+
+RMInterface::~RMInterface() {
+}
+
+bool RMInterface::active() {
+ return _bActive;
+}
+
+int RMInterface::onWhichBox(RMPoint pt) {
+ int max, i;
+
+ pt -= _openStart;
+
+ // Check how many verbs you have to consider
+ max = 4;
+ if (_bPerorate)
+ max = 5;
+
+ // Find the verb
+ for (i = 0; i < max; i++) {
+ if (_hotbbox[i].ptInRect(pt))
+ return i;
+ }
+
+ // Found no verb
+ return -1;
+}
+
+void RMInterface::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ int h;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ prim->getDst().topLeft() = _openStart;
+ CORO_INVOKE_2(RMGfxSourceBuffer8RLEByte::draw, bigBuf, prim);
+
+ // Check if there is a draw hot zone
+ _ctx->h = onWhichBox(_mpos);
+ if (_ctx->h != -1) {
+ prim->getDst().topLeft() = _openStart;
+ CORO_INVOKE_2(_hotzone[_ctx->h].draw, bigBuf, prim);
+
+ if (_lastHotZone != _ctx->h) {
+ _lastHotZone = _ctx->h;
+ g_vm->playUtilSFX(1);
+ }
+
+ if (GLOBALS._bCfgInterTips) {
+ prim->getDst().topLeft() = _openStart + RMPoint(70, 177);
+ CORO_INVOKE_2(_hints[_ctx->h].draw, bigBuf, prim);
+ }
+ } else
+ _lastHotZone = -1;
+
+ CORO_END_CODE;
+}
+
+void RMInterface::doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos) {
+ // If needed, add to the OT schedule list
+ if (!_nInList && _bActive)
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+
+ _mpos = mousepos;
+}
+
+void RMInterface::clicked(const RMPoint &mousepos) {
+ _bActive = true;
+ _openPos = mousepos;
+
+ // Calculate the top left corner of the interface
+ _openStart = _openPos - RMPoint(_dimx / 2, _dimy / 2);
+ _lastHotZone = -1;
+
+ // Keep it inside the screen
+ if (_openStart._x < 0)
+ _openStart._x = 0;
+ if (_openStart._y < 0)
+ _openStart._y = 0;
+ if (_openStart._x + _dimx > RM_SX)
+ _openStart._x = RM_SX - _dimx;
+ if (_openStart._y + _dimy > RM_SY)
+ _openStart._y = RM_SY - _dimy;
+
+ // Play the sound effect
+ g_vm->playUtilSFX(0);
+}
+
+bool RMInterface::released(const RMPoint &mousepos, RMTonyAction &action) {
+ if (!_bActive)
+ return false;
+
+ _bActive = false;
+
+ switch (onWhichBox(mousepos)) {
+ case 0:
+ action = TA_TAKE;
+ break;
+
+ case 1:
+ action = TA_TALK;
+ break;
+
+ case 2:
+ action = TA_USE;
+ break;
+
+ case 3:
+ action = TA_EXAMINE;
+ break;
+
+ case 4:
+ action = TA_PERORATE;
+ break;
+
+ default: // No verb
+ return false;
+ }
+
+ return true;
+}
+
+void RMInterface::reset() {
+ _bActive = false;
+}
+
+void RMInterface::setPerorate(bool bOn) {
+ _bPerorate = bOn;
+}
+
+bool RMInterface::getPerorate() {
+ return _bPerorate;
+}
+
+void RMInterface::init() {
+ int i;
+ RMResRaw inter(RES_I_INTERFACE);
+ RMRes pal(RES_I_INTERPPAL);
+
+ setPriority(191);
+
+ RMGfxSourceBuffer::init(inter, inter.width(), inter.height());
+ loadPaletteWA(RES_I_INTERPAL);
+
+ for (i = 0; i < 5; i++) {
+ RMResRaw part(RES_I_INTERP1 + i);
+
+ _hotzone[i].init(part, part.width(), part.height());
+ _hotzone[i].loadPaletteWA(pal);
+ }
+
+ _hotbbox[0].setRect(126, 123, 159, 208); // Take
+ _hotbbox[1].setRect(90, 130, 125, 186); // About
+ _hotbbox[2].setRect(110, 60, 152, 125);
+ _hotbbox[3].setRect(56, 51, 93, 99);
+ _hotbbox[4].setRect(51, 105, 82, 172);
+
+ _hints[0].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[1].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[2].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[3].setAlignType(RMText::HRIGHT, RMText::VTOP);
+ _hints[4].setAlignType(RMText::HRIGHT, RMText::VTOP);
+
+ // The text is taken from MPAL for translation
+ RMMessage msg0(12);
+ RMMessage msg1(13);
+ RMMessage msg2(14);
+ RMMessage msg3(15);
+ RMMessage msg4(16);
+
+ _hints[0].writeText(msg0[0], 1); // Take
+ _hints[1].writeText(msg1[0], 1); // Talk
+ _hints[2].writeText(msg2[0], 1); // Use
+ _hints[3].writeText(msg3[0], 1); // Examine
+ _hints[4].writeText(msg4[0], 1); // Show Yourself
+
+ _bActive = false;
+ _bPerorate = false;
+ _lastHotZone = 0;
+}
+
+void RMInterface::close() {
+ int i;
+
+ destroy();
+
+ for (i = 0; i < 5; i++)
+ _hotzone[i].destroy();
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/inventory.h b/engines/tony/inventory.h
new file mode 100644
index 0000000000..ce94c86c1b
--- /dev/null
+++ b/engines/tony/inventory.h
@@ -0,0 +1,241 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_INVENTORY_H
+#define TONY_INVENTORY_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "tony/font.h"
+#include "tony/game.h"
+#include "tony/gfxcore.h"
+#include "tony/loc.h"
+
+namespace Tony {
+
+struct RMInventoryItem {
+ RMItem _icon;
+ RMGfxSourceBuffer8RLEByteAA *_pointer;
+ int _status;
+};
+
+class RMInventory : public RMGfxWoodyBuffer {
+private:
+ enum InventoryState {
+ CLOSED,
+ OPENING,
+ OPENED,
+ CLOSING,
+ SELECTING
+ };
+
+protected:
+ int _nItems;
+ RMInventoryItem *_items;
+
+ int _inv[256];
+ int _nInv;
+ int _curPutY;
+ uint32 _curPutTime;
+
+ int _curPos;
+ InventoryState _state;
+ bool _bHasFocus;
+ int _nSelectObj;
+ int _nCombine;
+ bool _bCombining;
+
+ bool _bBlinkingRight, _bBlinkingLeft;
+
+ int _miniAction;
+ RMItem _miniInterface;
+ RMText _hints[3];
+
+ OSystem::MutexRef _csModifyInterface;
+
+protected:
+ /**
+ * Prepare the image inventory. It should be recalled whenever the inventory changes
+ */
+ void prepare();
+
+ /**
+ * Check if the mouse Y position is conrrect, even under the inventory portion of the screen
+ */
+ bool checkPointInside(const RMPoint &pt);
+
+public:
+ RMInventory();
+ virtual ~RMInventory();
+
+ /**
+ * Prepare a frame
+ */
+ void doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen);
+
+ /**
+ * Initialization and closing
+ */
+ void init();
+ void close();
+ void reset();
+
+ /**
+ * Overload test for removal from OT list
+ */
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ /**
+ * Overload the drawing of the inventory
+ */
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ /**
+ * Method for determining whether the inventory currently has the focus
+ */
+ bool haveFocus(const RMPoint &mpos);
+
+ /**
+ * Method for determining if the mini interface is active
+ */
+ bool miniActive();
+
+ /**
+ * Handle the left mouse click (only when the inventory has the focus)
+ */
+ bool leftClick(const RMPoint &mpos, int &nCombineObj);
+
+ /**
+ * Handle the right mouse button (only when the inventory has the focus)
+ */
+ void rightClick(const RMPoint &mpos);
+ bool rightRelease(const RMPoint &mpos, RMTonyAction &curAction);
+
+ /**
+ * Warn that an item combine is over
+ */
+ void endCombine();
+
+public:
+ /**
+ * Add an item to the inventory
+ */
+ void addItem(int code);
+ RMInventory &operator+=(RMItem *item);
+ RMInventory &operator+=(RMItem &item);
+ RMInventory &operator+=(int code);
+
+ /**
+ * Removes an item
+ */
+ void removeItem(int code);
+
+ /**
+ * We are on an object?
+ */
+ RMItem *whichItemIsIn(const RMPoint &mpt);
+ bool itemInFocus(const RMPoint &mpt);
+
+ /**
+ * Change the icon of an item
+ */
+ void changeItemStatus(uint32 dwCode, uint32 dwStatus);
+
+ /**
+ * Save methods
+ */
+ int getSaveStateSize();
+ void saveState(byte *state);
+ int loadState(byte *state);
+};
+
+
+class RMInterface : public RMGfxSourceBuffer8RLEByte {
+private:
+ bool _bActive;
+ RMPoint _mpos;
+ RMPoint _openPos;
+ RMPoint _openStart;
+ RMText _hints[5];
+ RMGfxSourceBuffer8RLEByte _hotzone[5];
+ RMRect _hotbbox[5];
+ bool _bPerorate;
+ int _lastHotZone;
+
+protected:
+ /**
+ * Return which box a given point is in
+ */
+ int onWhichBox(RMPoint pt);
+
+public:
+ RMInterface();
+ virtual ~RMInterface();
+
+ /**
+ * The usual DoFrame (poll the graphics engine)
+ */
+ void doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos);
+
+ /**
+ * TRUE if it is active (you can select items)
+ */
+ bool active();
+
+ /**
+ * Initialization
+ */
+ void init();
+ void close();
+
+ /**
+ * Reset the interface
+ */
+ void reset();
+
+ /**
+ * Warns of mouse clicks and releases
+ */
+ void clicked(const RMPoint &mousepos);
+ bool released(const RMPoint &mousepos, RMTonyAction &action);
+
+ /**
+ * Enables or disables the fifth verb
+ */
+ void setPerorate(bool bOn);
+ bool getPerorate();
+
+ /**
+ * Overloaded Draw
+ */
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/loc.cpp b/engines/tony/loc.cpp
new file mode 100644
index 0000000000..4fe19594f9
--- /dev/null
+++ b/engines/tony/loc.cpp
@@ -0,0 +1,2322 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/memstream.h"
+#include "common/scummsys.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/game.h"
+#include "tony/loc.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+using namespace ::Tony::MPAL;
+
+
+/****************************************************************************\
+* RMPalette Methods
+\****************************************************************************/
+
+void RMPalette::readFromStream(Common::ReadStream &ds) {
+ ds.read(_data, 1024);
+}
+
+/****************************************************************************\
+* RMSlot Methods
+\****************************************************************************/
+
+void RMPattern::RMSlot::readFromStream(Common::ReadStream &ds, bool bLOX) {
+ byte type;
+
+ // Type
+ type = ds.readByte();
+ _type = (RMPattern::RMSlotType)type;
+
+ // Data
+ _data = ds.readSint32LE();
+
+ // Position
+ _pos.readFromStream(ds);
+
+ // Generic flag
+ _flag = ds.readByte();
+}
+
+
+/****************************************************************************\
+* RMPattern Methods
+\****************************************************************************/
+
+void RMPattern::readFromStream(Common::ReadStream &ds, bool bLOX) {
+ int i;
+
+ // Pattern name
+ if (!bLOX)
+ _name = readString(ds);
+
+ // Velocity
+ _speed = ds.readSint32LE();
+
+ // Position
+ _pos.readFromStream(ds);
+
+ // Flag for pattern looping
+ _bLoop = ds.readSint32LE();
+
+ // Number of slots
+ _nSlots = ds.readSint32LE();
+
+ // Create and read the slots
+ _slots = new RMSlot[_nSlots];
+
+ for (i = 0; i < _nSlots && !ds.err(); i++) {
+ if (bLOX)
+ _slots[i].readFromStream(ds, true);
+ else
+ _slots[i].readFromStream(ds, false);
+ }
+}
+
+void RMPattern::updateCoord() {
+ _curPos = _pos + _slots[_nCurSlot].pos();
+}
+
+void RMPattern::stopSfx(RMSfx *sfx) {
+ for (int i = 0; i < _nSlots; i++) {
+ if (_slots[i]._type == SOUND) {
+ if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_')
+ sfx[_slots[i]._data].stop();
+ else if (GLOBALS._bSkipSfxNoLoop)
+ sfx[_slots[i]._data].stop();
+ }
+ }
+}
+
+int RMPattern::init(RMSfx *sfx, bool bPlayP0, byte *bFlag) {
+ int i;
+
+ // Read the current time
+ _nStartTime = g_vm->getTime();
+ _nCurSlot = 0;
+
+ // Find the first frame of the pattern
+ i = 0;
+ while (_slots[i]._type != SPRITE) {
+ assert(i + 1 < _nSlots);
+ i++;
+ }
+
+ _nCurSlot = i;
+ _nCurSprite = _slots[i]._data;
+ if (bFlag)
+ *bFlag = _slots[i]._flag;
+
+ // Calculate the current coordinates
+ updateCoord();
+
+ // Check for sound:
+ // If the slot is 0, play
+ // If speed == 0, must play unless it goes into loop '_', or if specified by the parameter
+ // If speed != 0, play only the loop
+ for (i = 0; i < _nSlots; i++) {
+ if (_slots[i]._type == SOUND) {
+ if (i == 0) {
+ if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play(true);
+ } else {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play();
+ }
+ } else if (_speed == 0) {
+ if (bPlayP0) {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play();
+ } else if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play(true);
+ }
+ } else {
+ if (_bLoop && !sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
+ sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
+ sfx[_slots[i]._data].play(true);
+ }
+ }
+ }
+ }
+
+ return _nCurSprite;
+}
+
+int RMPattern::update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx) {
+ int CurTime = g_vm->getTime();
+
+ // If the speed is 0, then the pattern never advances
+ if (_speed == 0) {
+ CoroScheduler.pulseEvent(hEndPattern);
+ bFlag = _slots[_nCurSlot]._flag;
+ return _nCurSprite;
+ }
+
+ // Is it time to change the slots?
+ while (_nStartTime + _speed <= (uint32)CurTime) {
+ _nStartTime += _speed;
+ if (_slots[_nCurSlot]._type == SPRITE)
+ _nCurSlot++;
+ if (_nCurSlot == _nSlots) {
+ _nCurSlot = 0;
+ bFlag = _slots[_nCurSlot]._flag;
+
+ CoroScheduler.pulseEvent(hEndPattern);
+
+ // @@@ If there is no loop pattern, and there's a warning that it's the final
+ // frame, then remain on the last frame
+ if (!_bLoop) {
+ _nCurSlot = _nSlots - 1;
+ bFlag = _slots[_nCurSlot]._flag;
+ return _nCurSprite;
+ }
+ }
+
+ for (;;) {
+ switch (_slots[_nCurSlot]._type) {
+ case SPRITE:
+ // Read the next sprite
+ _nCurSprite = _slots[_nCurSlot]._data;
+
+ // Update the parent & child coordinates
+ updateCoord();
+ break;
+
+ case SOUND:
+ if (sfx != NULL) {
+ sfx[_slots[_nCurSlot]._data].setVolume(_slots[_nCurSlot].pos()._x);
+
+ if (sfx[_slots[_nCurSlot]._data]._name.empty() || sfx[_slots[_nCurSlot]._data]._name[0] != '_')
+ sfx[_slots[_nCurSlot]._data].play(false);
+ else
+ sfx[_slots[_nCurSlot]._data].play(true);
+ }
+ break;
+
+ case COMMAND:
+ assert(0);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ if (_slots[_nCurSlot]._type == SPRITE)
+ break;
+ _nCurSlot++;
+ }
+ }
+
+ // Return the current sprite
+ bFlag = _slots[_nCurSlot]._flag;
+ return _nCurSprite;
+}
+
+RMPattern::RMPattern() {
+ _slots = NULL;
+ _speed = 0;
+ _bLoop = 0;
+ _nSlots = 0;
+ _nCurSlot = 0;
+ _nCurSprite = 0;
+ _nStartTime = 0;
+ _slots = NULL;
+}
+
+/**
+ * Reads the position of the pattern
+ */
+RMPoint RMPattern::pos() {
+ return _curPos;
+}
+
+RMPattern::~RMPattern() {
+ if (_slots != NULL) {
+ delete[] _slots;
+ _slots = NULL;
+ }
+}
+
+/****************************************************************************\
+* RMSprite Methods
+\****************************************************************************/
+
+void RMSprite::init(RMGfxSourceBuffer *buf) {
+ _buf = buf;
+}
+
+void RMSprite::LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) {
+ uint32 pos = ds.pos();
+
+ *dimx = ds.readSint32LE();
+ *dimy = ds.readSint32LE();
+
+ ds.seek(pos);
+}
+
+void RMSprite::getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) {
+ uint32 pos = ds.pos();
+
+ _name = readString(ds);
+ *dimx = ds.readSint32LE();
+ *dimy = ds.readSint32LE();
+
+ ds.seek(pos);
+}
+
+void RMSprite::readFromStream(Common::SeekableReadStream &ds, bool bLOX) {
+ int dimx, dimy;
+
+ // Sprite name
+ if (!bLOX)
+ _name = readString(ds);
+
+ // Dimensions
+ dimx = ds.readSint32LE();
+ dimy = ds.readSint32LE();
+
+ // Bounding box
+ _rcBox.readFromStream(ds);
+
+ // Unused space
+ if (!bLOX)
+ ds.skip(32);
+
+ // Create buffer and read
+ _buf->init(ds, dimx, dimy);
+}
+
+void RMSprite::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ _buf->draw(coroParam, bigBuf, prim);
+}
+
+void RMSprite::setPalette(byte *buf) {
+ ((RMGfxSourceBufferPal *)_buf)->loadPalette(buf);
+}
+
+RMSprite::RMSprite() {
+ _buf = NULL;
+}
+
+RMSprite::~RMSprite() {
+ if (_buf) {
+ delete _buf;
+ _buf = NULL;
+ }
+}
+
+
+/****************************************************************************\
+* RMSfx Methods
+\****************************************************************************/
+
+void RMSfx::readFromStream(Common::ReadStream &ds, bool bLOX) {
+ int size;
+
+ // sfx name
+ _name = readString(ds);
+
+ size = ds.readSint32LE();
+
+ // Read the entire buffer into a MemoryReadStream
+ byte *buffer = (byte *)malloc(size);
+ ds.read(buffer, size);
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
+
+ // Create the sound effect
+ _fx = g_vm->createSFX(stream);
+ _fx->setLoop(false);
+}
+
+RMSfx::RMSfx() {
+ _fx = NULL;
+ _bPlayingLoop = false;
+}
+
+RMSfx::~RMSfx() {
+ if (_fx) {
+ _fx->release();
+ _fx = NULL;
+ }
+}
+
+void RMSfx::play(bool bLoop) {
+ if (_fx && !_bPlayingLoop) {
+ _fx->setLoop(bLoop);
+ _fx->play();
+
+ if (bLoop)
+ _bPlayingLoop = true;
+ }
+}
+
+void RMSfx::setVolume(int vol) {
+ if (_fx) {
+ _fx->setVolume(vol);
+ }
+}
+
+void RMSfx::pause(bool bPause) {
+ if (_fx) {
+ _fx->pause(bPause);
+ }
+}
+
+void RMSfx::stop() {
+ if (_fx) {
+ _fx->stop();
+ _bPlayingLoop = false;
+ }
+}
+
+
+
+/****************************************************************************\
+* RMItem Methods
+\****************************************************************************/
+
+int RMItem::getCurPattern() {
+ return _nCurPattern;
+}
+
+RMGfxSourceBuffer *RMItem::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) {
+ if (_cm == CM_256) {
+ RMGfxSourceBuffer8RLE *spr;
+
+ if (_FX == 2) { // AB
+ spr = new RMGfxSourceBuffer8RLEWordAB;
+ } else if (_FX == 1) { // OMBRA+AA
+ if (dimx == -1 || dimx > 255)
+ spr = new RMGfxSourceBuffer8RLEWordAA;
+ else
+ spr = new RMGfxSourceBuffer8RLEByteAA;
+
+ spr->setAlphaBlendColor(_FXparm);
+ if (bPreRLE)
+ spr->setAlreadyCompressed();
+ } else {
+ if (dimx == -1 || dimx > 255)
+ spr = new RMGfxSourceBuffer8RLEWord;
+ else
+ spr = new RMGfxSourceBuffer8RLEByte;
+
+ if (bPreRLE)
+ spr->setAlreadyCompressed();
+ }
+
+ return spr;
+ } else
+ return new RMGfxSourceBuffer16;
+}
+
+bool RMItem::isIn(const RMPoint &pt, int *size) {
+ RMRect rc;
+
+ if (!_bIsActive)
+ return false;
+
+ // Search for the right bounding box to use - use the sprite's if it has one, otherwise use the generic one
+ if (_nCurPattern != 0 && !_sprites[_nCurSprite]._rcBox.isEmpty())
+ rc = _sprites[_nCurSprite]._rcBox + calculatePos();
+ else if (!_rcBox.isEmpty())
+ rc = _rcBox;
+ // If no box, return immediately
+ else
+ return false;
+
+ if (size != NULL)
+ *size = rc.size();
+
+ return rc.ptInRect(pt + _curScroll);
+}
+
+void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) {
+ int i, dimx, dimy;
+ byte cm;
+
+ // MPAL code
+ _mpalCode = ds.readSint32LE();
+
+ // Object name
+ _name = readString(ds);
+
+ // Z (signed)
+ _z = ds.readSint32LE();
+
+ // Parent position
+ _pos.readFromStream(ds);
+
+ // Hotspot
+ _hot.readFromStream(ds);
+
+ // Bounding box
+ _rcBox.readFromStream(ds);
+
+ // Number of sprites, sound effects, and patterns
+ _nSprites = ds.readSint32LE();
+ _nSfx = ds.readSint32LE();
+ _nPatterns = ds.readSint32LE();
+
+ // Color mode
+ cm = ds.readByte();
+ _cm = (RMColorMode)cm;
+
+ // Flag for the presence of custom palette differences
+ _bPal = ds.readByte();
+
+ if (_cm == CM_256) {
+ // If there is a palette, read it in
+ if (_bPal)
+ _pal.readFromStream(ds);
+ }
+
+ // MPAL data
+ if (!bLOX)
+ ds.skip(20);
+
+ _FX = ds.readByte();
+ _FXparm = ds.readByte();
+
+ if (!bLOX)
+ ds.skip(106);
+
+ // Create sub-classes
+ if (_nSprites > 0)
+ _sprites = new RMSprite[_nSprites];
+ if (_nSfx > 0)
+ _sfx = new RMSfx[_nSfx];
+ _patterns = new RMPattern[_nPatterns + 1];
+
+ // Read in class data
+ if (!ds.err())
+ for (i = 0; i < _nSprites && !ds.err(); i++) {
+ // Download the sprites
+ if (bLOX) {
+ _sprites[i].LOXGetSizeFromStream(ds, &dimx, &dimy);
+ _sprites[i].init(newItemSpriteBuffer(dimx, dimy, true));
+ _sprites[i].readFromStream(ds, true);
+ } else {
+ _sprites[i].getSizeFromStream(ds, &dimx, &dimy);
+ _sprites[i].init(newItemSpriteBuffer(dimx, dimy, false));
+ _sprites[i].readFromStream(ds, false);
+ }
+
+ if (_cm == CM_256 && _bPal)
+ _sprites[i].setPalette(_pal._data);
+ }
+
+ if (!ds.err())
+ for (i = 0; i < _nSfx && !ds.err(); i++) {
+ if (bLOX)
+ _sfx[i].readFromStream(ds, true);
+ else
+ _sfx[i].readFromStream(ds, false);
+ }
+
+ // Read the pattern from pattern 1
+ if (!ds.err())
+ for (i = 1; i <= _nPatterns && !ds.err(); i++) {
+ if (bLOX)
+ _patterns[i].readFromStream(ds, true);
+ else
+ _patterns[i].readFromStream(ds, false);
+ }
+
+ // Initialize the current pattern
+ if (_bInitCurPattern)
+ setPattern(mpalQueryItemPattern(_mpalCode));
+
+ // Initialize the current activation state
+ _bIsActive = mpalQueryItemIsActive(_mpalCode);
+}
+
+
+RMGfxPrimitive *RMItem::newItemPrimitive() {
+ return new RMGfxPrimitive(this);
+}
+
+void RMItem::setScrollPosition(const RMPoint &scroll) {
+ _curScroll = scroll;
+}
+
+bool RMItem::doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList) {
+ int oldSprite = _nCurSprite;
+
+ // Pattern 0 = Do not draw anything!
+ if (_nCurPattern == 0)
+ return false;
+
+ // We do an update of the pattern, which also returns the current frame
+ if (_nCurPattern != 0) {
+ _nCurSprite = _patterns[_nCurPattern].update(_hEndPattern, _bCurFlag, _sfx);
+
+ // WORKAROUND: Currently, m_nCurSprite = -1 is used to flag that an item should be removed.
+ // However, this seems to be done inside a process waiting on an event pulsed inside the pattern
+ // Update method. So the value of m_nCurSprite = -1 is being destroyed with the return value
+ // replacing it. It may be that the current coroutine PulseEvent implementation is wrong somehow.
+ // In any case, a special check here is done for items that have ended
+ if (_nCurPattern == 0)
+ _nCurSprite = -1;
+ }
+
+ // If the function returned -1, it means that the pattern has finished
+ if (_nCurSprite == -1) {
+ // We have pattern 0, so leave. The class will self de-register from the OT list
+ _nCurPattern = 0;
+ return false;
+ }
+
+ // If we are not in the OT list, add ourselves
+ if (!_nInList && bAddToList)
+ bigBuf->addPrim(newItemPrimitive());
+
+ return oldSprite != _nCurSprite;
+}
+
+RMPoint RMItem::calculatePos() {
+ return _pos + _patterns[_nCurPattern].pos();
+}
+
+void RMItem::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // If CurSprite == -1, then the pattern is finished
+ if (_nCurSprite == -1)
+ return;
+
+ // Set the flag
+ prim->setFlag(_bCurFlag);
+
+ // Offset direction for scrolling
+ prim->getDst().offset(-_curScroll);
+
+ // We must offset the cordinates of the item inside the primitive
+ // It is estimated as nonno + (babbo + figlio)
+ prim->getDst().offset(calculatePos());
+
+ // No stretching, please
+ prim->setStretch(false);
+
+ // Now we turn to the generic surface drawing routines
+ CORO_INVOKE_2(_sprites[_nCurSprite].draw, bigBuf, prim);
+
+ CORO_END_CODE;
+}
+
+/**
+ * Overloaded priority: it's based on Z ordering
+ */
+int RMItem::priority() {
+ return _z;
+}
+
+/**
+ * Pattern number
+ */
+int RMItem::numPattern() {
+ return _nPatterns;
+}
+
+void RMItem::removeThis(CORO_PARAM, bool &result) {
+ // Remove from the OT list if the current frame is -1 (pattern over)
+ result = (_nCurSprite == -1);
+}
+
+
+void RMItem::setStatus(int nStatus) {
+ _bIsActive = (nStatus > 0);
+}
+
+RMPoint RMItem::hotspot() {
+ return _hot;
+}
+
+int RMItem::mpalCode() {
+ return _mpalCode;
+}
+
+void RMItem::setPattern(int nPattern, bool bPlayP0) {
+ assert(nPattern >= 0 && nPattern <= _nPatterns);
+
+ if (_sfx) {
+ if (_nCurPattern > 0)
+ _patterns[_nCurPattern].stopSfx(_sfx);
+ }
+
+ // Remember the current pattern
+ _nCurPattern = nPattern;
+
+ // Start the pattern to start the animation
+ if (_nCurPattern != 0)
+ _nCurSprite = _patterns[_nCurPattern].init(_sfx, bPlayP0, &_bCurFlag);
+ else {
+ _nCurSprite = -1;
+
+ // Look for the sound effect for pattern 0
+ if (bPlayP0) {
+ for (int i = 0; i < _nSfx; i++) {
+ if (_sfx[i]._name == "p0")
+ _sfx[i].play();
+ }
+ }
+ }
+}
+
+bool RMItem::getName(Common::String &name) {
+ char buf[256];
+
+ mpalQueryItemName(_mpalCode, buf);
+ name = buf;
+ if (buf[0] == '\0')
+ return false;
+ return true;
+}
+
+void RMItem::unload() {
+ if (_patterns != NULL) {
+ delete[] _patterns;
+ _patterns = NULL;
+ }
+
+ if (_sprites != NULL) {
+ delete[] _sprites;
+ _sprites = NULL;
+ }
+
+ if (_sfx != NULL) {
+ delete[] _sfx;
+ _sfx = NULL;
+ }
+}
+
+RMItem::RMItem() {
+ _bCurFlag = 0;
+ _patterns = NULL;
+ _sprites = NULL;
+ _sfx = NULL;
+ _curScroll.set(0, 0);
+ _bInitCurPattern = true;
+ _nCurPattern = 0;
+ _z = 0;
+ _cm = CM_256;
+ _FX = 0;
+ _FXparm = 0;
+ _mpalCode = 0;
+ _nSprites = 0;
+ _nSfx = 0;
+ _nPatterns = 0;
+ _bPal = 0;
+ _nCurSprite = 0;
+
+ _bIsActive = false;
+ memset(_pal._data, 0, sizeof(_pal._data));
+
+ _hEndPattern = CoroScheduler.createEvent(false, false);
+}
+
+RMItem::~RMItem() {
+ unload();
+ CoroScheduler.closeEvent(_hEndPattern);
+}
+
+
+void RMItem::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) {
+ CORO_BEGIN_CONTEXT;
+ uint32 h[2];
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_nCurPattern != 0) {
+ if (hCustomSkip == CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndPattern, CORO_INFINITE);
+ else {
+ _ctx->h[0] = hCustomSkip;
+ _ctx->h[1] = _hEndPattern;
+ CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, 2, &_ctx->h[0], false, CORO_INFINITE);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMItem::changeHotspot(const RMPoint &pt) {
+ _hot = pt;
+}
+
+void RMItem::setInitCurPattern(bool status) {
+ _bInitCurPattern = status;
+}
+
+void RMItem::playSfx(int nSfx) {
+ if (nSfx < _nSfx)
+ _sfx[nSfx].play();
+}
+
+void RMItem::pauseSound(bool bPause) {
+ int i;
+
+ for (i = 0; i < _nSfx; i++)
+ _sfx[i].pause(bPause);
+}
+
+
+
+/****************************************************************************\
+* RMWipe Methods
+\****************************************************************************/
+
+
+RMWipe::RMWipe() {
+ _hUnregistered = CoroScheduler.createEvent(false, false);
+ _hEndOfFade = CoroScheduler.createEvent(false, false);
+
+ _bMustRegister = false;
+ _bUnregister = false;
+ _bEndFade = false;
+ _bFading = false;
+ _nFadeStep = 0;
+
+}
+
+RMWipe::~RMWipe() {
+ CoroScheduler.closeEvent(_hUnregistered);
+ CoroScheduler.closeEvent(_hEndOfFade);
+}
+
+int RMWipe::priority() {
+ return 200;
+}
+
+void RMWipe::Unregister() {
+ RMGfxTask::Unregister();
+ assert(_nInList == 0);
+ CoroScheduler.setEvent(_hUnregistered);
+}
+
+void RMWipe::removeThis(CORO_PARAM, bool &result) {
+ result = _bUnregister;
+}
+
+void RMWipe::waitForFadeEnd(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfFade, CORO_INFINITE);
+
+ _bEndFade = true;
+ _bFading = false;
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+void RMWipe::closeFade() {
+ _wip0r.unload();
+}
+
+void RMWipe::initFade(int type) {
+ // Activate the fade
+ _bUnregister = false;
+ _bEndFade = false;
+
+ _nFadeStep = 0;
+
+ _bMustRegister = true;
+
+ RMRes res(RES_W_CIRCLE);
+ Common::SeekableReadStream *ds = res.getReadStream();
+ _wip0r.readFromStream(*ds);
+ delete ds;
+
+ _wip0r.setPattern(1);
+
+ _bFading = true;
+}
+
+void RMWipe::doFrame(RMGfxTargetBuffer &bigBuf) {
+ if (_bMustRegister) {
+ bigBuf.addPrim(new RMGfxPrimitive(this));
+ _bMustRegister = false;
+ }
+
+ if (_bFading) {
+ _wip0r.doFrame(&bigBuf, false);
+
+ _nFadeStep++;
+
+ if (_nFadeStep == 10) {
+ CoroScheduler.setEvent(_hEndOfFade);
+ }
+ }
+}
+
+void RMWipe::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bFading) {
+ CORO_INVOKE_2(_wip0r.draw, bigBuf, prim);
+ }
+
+ if (_bEndFade)
+ Common::fill((byte *)bigBuf, (byte *)bigBuf + bigBuf.getDimx() * bigBuf.getDimy() * 2, 0x0);
+
+ CORO_END_CODE;
+}
+
+/****************************************************************************\
+* RMCharacter Methods
+\****************************************************************************/
+
+/****************************************************************************/
+/* Find the shortest path between two nodes of the graph connecting the BOX */
+/* Returns path along the vector path path[] */
+/****************************************************************************/
+
+short RMCharacter::findPath(short source, short destination) {
+ static RMBox box[MAXBOXES]; // Matrix of adjacent boxes
+ static short nodeCost[MAXBOXES]; // Cost per node
+ static short valid[MAXBOXES]; // 0:Invalid 1:Valid 2:Saturated
+ static short nextNode[MAXBOXES]; // Next node
+ short minCost, error = 0;
+ RMBoxLoc *cur;
+
+ g_system->lockMutex(_csMove);
+
+ if (source == -1 || destination == -1) {
+ g_system->unlockMutex(_csMove);
+ return 0;
+ }
+
+ // Get the boxes
+ cur = _theBoxes->getBoxes(_curLocation);
+
+ // Make a backup copy to work on
+ for (int i = 0; i < cur->_numbBox; i++)
+ memcpy(&box[i], &cur->_boxes[i], sizeof(RMBox));
+
+ // Invalidate all nodes
+ for (int i = 0; i < cur->_numbBox; i++)
+ valid[i] = 0;
+
+ // Prepare source and variables for the procedure
+ nodeCost[source] = 0;
+ valid[source] = 1;
+ bool finish = false;
+
+ // Find the shortest path
+ while (!finish) {
+ minCost = 32000; // Reset the minimum cost
+ error = 1; // Possible error
+
+ // 1st cycle: explore possible new nodes
+ for (int i = 0; i < cur->_numbBox; i++)
+ if (valid[i] == 1) {
+ error = 0; // Failure de-bunked
+ int j = 0;
+ while (((box[i]._adj[j]) != 1) && (j < cur->_numbBox))
+ j++;
+
+ if (j >= cur->_numbBox)
+ valid[i] = 2; // nodo saturated?
+ else {
+ nextNode[i] = j;
+ if (nodeCost[i] + 1 < minCost)
+ minCost = nodeCost[i] + 1;
+ }
+ }
+
+ if (error)
+ finish = true; // All nodes saturated
+
+ // 2nd cycle: adding new nodes that were found, saturate old nodes
+ for (int i = 0; i < cur->_numbBox; i++)
+ if ((valid[i] == 1) && ((nodeCost[i] + 1) == minCost)) {
+ box[i]._adj[nextNode[i]] = 2;
+ nodeCost[nextNode[i]] = minCost;
+ valid[nextNode[i]] = 1;
+ for (int j = 0; j < cur->_numbBox; j++)
+ if (box[j]._adj[nextNode[i]] == 1)
+ box[j]._adj[nextNode[i]] = 0;
+
+ if (nextNode[i] == destination)
+ finish = true;
+ }
+ }
+
+ // Remove the path from the adjacent modified matrixes
+ if (!error) {
+ _pathLength = nodeCost[destination];
+ short k = _pathLength;
+ _path[k] = destination;
+
+ while (_path[k] != source) {
+ int i = 0;
+ while (box[i]._adj[_path[k]] != 2)
+ i++;
+ k--;
+ _path[k] = i;
+ }
+
+ _pathLength++;
+ }
+
+ g_system->unlockMutex(_csMove);
+
+ return !error;
+}
+
+
+void RMCharacter::goTo(CORO_PARAM, RMPoint destcoord, bool bReversed) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_pos == destcoord) {
+ if (_minPath == 0) {
+ CORO_INVOKE_0(stop);
+ CoroScheduler.pulseEvent(_hEndOfPath);
+ return;
+ }
+ }
+
+ _status = WALK;
+ _lineStart = _pos;
+ _lineEnd = destcoord;
+ _dx = _lineStart._x - _lineEnd._x;
+ _dy = _lineStart._y - _lineEnd._y;
+ _fx = _dx;
+ _fy = _dy;
+ _dx = ABS(_dx);
+ _dy = ABS(_dy);
+ _walkSpeed = _curSpeed;
+ _walkCount = 0;
+
+ if (bReversed) {
+ while (0) ;
+ }
+
+ int nPatt = getCurPattern();
+
+ if (_dx > _dy) {
+ _slope = _fy / _fx;
+ if (_lineEnd._x < _lineStart._x)
+ _walkSpeed = -_walkSpeed;
+ _walkStatus = 1;
+
+ // Change the pattern for the new direction
+ _bNeedToStop = true;
+ if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) {
+ if (nPatt != PAT_WALKLEFT)
+ setPattern(PAT_WALKLEFT);
+ } else {
+ if (nPatt != PAT_WALKRIGHT)
+ setPattern(PAT_WALKRIGHT);
+ }
+ } else {
+ _slope = _fx / _fy;
+ if (_lineEnd._y < _lineStart._y)
+ _walkSpeed = -_walkSpeed;
+ _walkStatus = 0;
+
+ _bNeedToStop = true;
+ if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) {
+ if (nPatt != PAT_WALKUP)
+ setPattern(PAT_WALKUP);
+ } else {
+ if (nPatt != PAT_WALKDOWN)
+ setPattern(PAT_WALKDOWN);
+ }
+ }
+
+ _olddx = _dx;
+ _olddy = _dy;
+
+ CORO_END_CODE;
+}
+
+
+RMPoint RMCharacter::searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint punto) {
+ short passi, minimo;
+ RMPoint nuovo, trovato;
+ minimo = 32000;
+
+ if (UP) {
+ nuovo = punto;
+ passi = 0;
+ while ((inWhichBox(nuovo) == -1) && (nuovo._y >= 0)) {
+ nuovo._y--;
+ passi++;
+ }
+ if ((inWhichBox(nuovo) != -1) && (passi < minimo) &&
+ findPath(inWhichBox(_pos), inWhichBox(nuovo))) {
+ minimo = passi;
+ nuovo._y--; // to avoid error?
+ trovato = nuovo;
+ }
+ }
+
+ if (DOWN) {
+ nuovo = punto;
+ passi = 0;
+ while ((inWhichBox(nuovo) == -1) && (nuovo._y < 480)) {
+ nuovo._y++;
+ passi++;
+ }
+ if ((inWhichBox(nuovo) != -1) && (passi < minimo) &&
+ findPath(inWhichBox(_pos), inWhichBox(nuovo))) {
+ minimo = passi;
+ nuovo._y++; // to avoid error?
+ trovato = nuovo;
+ }
+ }
+
+ if (RIGHT) {
+ nuovo = punto;
+ passi = 0;
+ while ((inWhichBox(nuovo) == -1) && (nuovo._x < 640)) {
+ nuovo._x++;
+ passi++;
+ }
+ if ((inWhichBox(nuovo) != -1) && (passi < minimo) &&
+ findPath(inWhichBox(_pos), inWhichBox(nuovo))) {
+ minimo = passi;
+ nuovo._x++; // to avoid error?
+ trovato = nuovo;
+ }
+ }
+
+ if (LEFT) {
+ nuovo = punto;
+ passi = 0;
+ while ((inWhichBox(nuovo) == -1) && (nuovo._x >= 0)) {
+ nuovo._x--;
+ passi++;
+ }
+ if ((inWhichBox(nuovo) != -1) && (passi < minimo) &&
+ findPath(inWhichBox(_pos), inWhichBox(nuovo))) {
+ minimo = passi;
+ nuovo._x--; // to avoid error?
+ trovato = nuovo;
+ }
+ }
+
+ if (minimo == 32000)
+ trovato = punto;
+
+ return trovato;
+}
+
+
+RMPoint RMCharacter::nearestPoint(const RMPoint &punto) {
+ return searching(1, 1, 1, 1, punto);
+}
+
+
+short RMCharacter::scanLine(const RMPoint &punto) {
+ int Ldx, Ldy, Lcount;
+ float Lfx, Lfy, Lslope;
+ RMPoint Lstart, Lend, Lscan;
+ signed char Lspeed, Lstatus;
+
+ Lstart = _pos;
+ Lend = punto;
+ Ldx = Lstart._x - Lend._x;
+ Ldy = Lstart._y - Lend._y;
+ Lfx = Ldx;
+ Lfy = Ldy;
+ Ldx = ABS(Ldx);
+ Ldy = ABS(Ldy);
+ Lspeed = 1;
+ Lcount = 0;
+
+ if (Ldx > Ldy) {
+ Lslope = Lfy / Lfx;
+ if (Lend._x < Lstart._x)
+ Lspeed = -Lspeed;
+ Lstatus = 1;
+ } else {
+ Lslope = Lfx / Lfy;
+ if (Lend._y < Lstart._y)
+ Lspeed = - Lspeed;
+ Lstatus = 0;
+ }
+
+ Lscan = Lstart; // Start scanning
+ while (inWhichBox(Lscan) != -1) {
+ Lcount++;
+ if (Lstatus) {
+ Ldx = Lspeed * Lcount;
+ Ldy = (int)(Lslope * Ldx);
+ } else {
+ Ldy = Lspeed * Lcount;
+ Ldx = (int)(Lslope * Ldy);
+ }
+
+ Lscan._x = Lstart._x + Ldx;
+ Lscan._y = Lstart._y + Ldy;
+
+ if ((ABS(Lscan._x - Lend._x) <= 1) && (ABS(Lscan._y - Lend._y) <= 1))
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Calculates intersections between the straight line and the closest BBOX
+ */
+RMPoint RMCharacter::invScanLine(const RMPoint &punto) {
+ int Ldx, Ldy, Lcount;
+ float Lfx, Lfy, Lslope;
+ RMPoint Lstart, Lend, Lscan;
+ signed char Lspeed, Lstatus, Lbox = -1;
+
+ Lstart = punto; // Exchange!
+ Lend = _pos; // :-)
+ Ldx = Lstart._x - Lend._x;
+ Ldy = Lstart._y - Lend._y;
+ Lfx = Ldx;
+ Lfy = Ldy;
+ Ldx = ABS(Ldx);
+ Ldy = ABS(Ldy);
+ Lspeed = 1;
+ Lcount = 0;
+
+ if (Ldx > Ldy) {
+ Lslope = Lfy / Lfx;
+ if (Lend._x < Lstart._x)
+ Lspeed = -Lspeed;
+ Lstatus = 1;
+ } else {
+ Lslope = Lfx / Lfy;
+ if (Lend._y < Lstart._y)
+ Lspeed = -Lspeed;
+ Lstatus = 0;
+ }
+ Lscan = Lstart;
+
+ for (;;) {
+ if (inWhichBox(Lscan) != -1) {
+ if (inWhichBox(Lscan) != Lbox) {
+ if (inWhichBox(_pos) == inWhichBox(Lscan) || findPath(inWhichBox(_pos), inWhichBox(Lscan)))
+ return Lscan;
+ else
+ Lbox = inWhichBox(Lscan);
+ }
+ }
+
+ Lcount++;
+ if (Lstatus) {
+ Ldx = Lspeed * Lcount;
+ Ldy = (int)(Lslope * Ldx);
+ } else {
+ Ldy = Lspeed * Lcount;
+ Ldx = (int)(Lslope * Ldy);
+ }
+ Lscan._x = Lstart._x + Ldx;
+ Lscan._y = Lstart._y + Ldy;
+
+ // WORKAROUND: Handles cases where the points never fall inside a bounding box
+ if (Lscan._x < -100 || Lscan._y < -100 || Lscan._x >= 1000 || Lscan._y >= 1000)
+ return punto;
+ }
+}
+
+
+/**
+ * Returns the HotSpot coordinate closest to the player
+ */
+
+RMPoint RMCharacter::nearestHotSpot(int sourcebox, int destbox) {
+ RMPoint puntocaldo;
+ short cc;
+ int x, y, distanzaminima;
+ distanzaminima = 10000000;
+ RMBoxLoc *cur = _theBoxes->getBoxes(_curLocation);
+
+ for (cc = 0; cc < cur->_boxes[sourcebox]._numHotspot; cc++)
+ if ((cur->_boxes[sourcebox]._hotspot[cc]._destination) == destbox) {
+ x = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hotx - _pos._x);
+ y = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hoty - _pos._y);
+
+ if ((x * x + y * y) < distanzaminima) {
+ distanzaminima = x * x + y * y;
+ puntocaldo._x = cur->_boxes[sourcebox]._hotspot[cc]._hotx;
+ puntocaldo._y = cur->_boxes[sourcebox]._hotspot[cc]._hoty;
+ }
+ }
+
+ return puntocaldo;
+}
+
+void RMCharacter::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bDrawNow) {
+ prim->getDst() += _fixedScroll;
+
+ CORO_INVOKE_2(RMItem::draw, bigBuf, prim);
+ }
+
+ CORO_END_CODE;
+}
+
+void RMCharacter::newBoxEntered(int nBox) {
+ RMBoxLoc *cur;
+ bool bOldReverse;
+
+ // Recall on ExitBox
+ mpalQueryDoAction(3, _curLocation, _curBox);
+
+ cur = _theBoxes->getBoxes(_curLocation);
+ bOldReverse = cur->_boxes[_curBox]._bReversed;
+ _curBox = nBox;
+
+ // If Z is changed, we must remove it from the OT
+ if (cur->_boxes[_curBox]._destZ != _z) {
+ _bRemoveFromOT = true;
+ _z = cur->_boxes[_curBox]._destZ;
+ }
+
+ // Movement management is reversed, only if we are not in the shortest path. If we are in the shortest
+ // path, directly do the DoFrame
+ if (_bMovingWithoutMinpath) {
+ if ((cur->_boxes[_curBox]._bReversed && !bOldReverse) || (!cur->_boxes[_curBox]._bReversed && bOldReverse)) {
+ switch (getCurPattern()) {
+ case PAT_WALKUP:
+ setPattern(PAT_WALKDOWN);
+ break;
+ case PAT_WALKDOWN:
+ setPattern(PAT_WALKUP);
+ break;
+ case PAT_WALKRIGHT:
+ setPattern(PAT_WALKLEFT);
+ break;
+ case PAT_WALKLEFT:
+ setPattern(PAT_WALKRIGHT);
+ break;
+ }
+ }
+ }
+
+ // Recall On EnterBox
+ mpalQueryDoAction(2, _curLocation, _curBox);
+}
+
+void RMCharacter::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc) {
+ CORO_BEGIN_CONTEXT;
+ bool bEndNow;
+ RMBoxLoc *cur;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bEndNow = false;
+ _bEndOfPath = false;
+ _bDrawNow = (_curLocation == loc);
+
+ g_system->lockMutex(_csMove);
+
+ // If we're walking..
+ if (_status != STAND) {
+ // If we are going horizontally
+ if (_walkStatus == 1) {
+ _dx = _walkSpeed * _walkCount;
+ _dy = (int)(_slope * _dx);
+ _pos._x = _lineStart._x + _dx;
+ _pos._y = _lineStart._y + _dy;
+
+ // Right
+ if (((_walkSpeed > 0) && (_pos._x > _lineEnd._x)) || ((_walkSpeed < 0) && (_pos._x < _lineEnd._x))) {
+ _pos = _lineEnd;
+ _status = STAND;
+ _ctx->bEndNow = true;
+ }
+ }
+
+ // If we are going vertical
+ if (_walkStatus == 0) {
+ _dy = _walkSpeed * _walkCount;
+ _dx = (int)(_slope * _dy);
+ _pos._x = _lineStart._x + _dx;
+ _pos._y = _lineStart._y + _dy;
+
+ // Down
+ if (((_walkSpeed > 0) && (_pos._y > _lineEnd._y)) || ((_walkSpeed < 0) && (_pos._y < _lineEnd._y))) {
+ _pos = _lineEnd;
+ _status = STAND;
+ _ctx->bEndNow = true;
+ }
+ }
+
+ // Check if the character came out of the BOX in error, in which case he returns immediately
+ if (inWhichBox(_pos) == -1) {
+ _pos._x = _lineStart._x + _olddx;
+ _pos._y = _lineStart._y + _olddy;
+ }
+
+ // If we have just moved to a temporary location, and is over the shortest path, we stop permanently
+ if (_ctx->bEndNow && _minPath == 0) {
+ if (!_bEndOfPath)
+ CORO_INVOKE_0(stop);
+ _bEndOfPath = true;
+ CoroScheduler.pulseEvent(_hEndOfPath);
+ }
+
+ _walkCount++;
+
+ // Update the character Z. @@@ Should remove only if the Z was changed
+
+ // Check if the box was changed
+ if (!_theBoxes->isInBox(_curLocation, _curBox, _pos))
+ newBoxEntered(inWhichBox(_pos));
+
+ // Update the old coordinates
+ _olddx = _dx;
+ _olddy = _dy;
+ }
+
+ // If we stop
+ if (_status == STAND) {
+ // Check if there is still the shortest path to calculate
+ if (_minPath == 1) {
+ _ctx->cur = _theBoxes->getBoxes(_curLocation);
+
+ // If we still have to go through a box
+ if (_pathCount < _pathLength) {
+ // Check if the box we're going into is active
+ if (_ctx->cur->_boxes[_path[_pathCount - 1]]._bActive) {
+ // Move in a straight line towards the nearest hotspot, taking into account the reversing
+ // NEWBOX = path[pathcount-1]
+ CORO_INVOKE_2(goTo, nearestHotSpot(_path[_pathCount - 1], _path[_pathCount]), _ctx->cur->_boxes[_path[_pathCount - 1]]._bReversed);
+ _pathCount++;
+ } else {
+ // If the box is off, we can only block all
+ // @@@ Whilst this should not happen, because have improved
+ // the search for the minimum path
+ _minPath = 0;
+ if (!_bEndOfPath)
+ CORO_INVOKE_0(stop);
+ _bEndOfPath = true;
+ CoroScheduler.pulseEvent(_hEndOfPath);
+ }
+ } else {
+ // If we have already entered the last box, we just have to move in a straight line towards the
+ // point of arrival
+ // NEWBOX = InWhichBox(pathend)
+ _minPath = 0;
+ CORO_INVOKE_2(goTo, _pathEnd, _ctx->cur->_boxes[inWhichBox(_pathEnd)]._bReversed);
+ }
+ }
+ }
+
+ g_system->unlockMutex(_csMove);
+
+ // Invoke the DoFrame of the item
+ RMItem::doFrame(bigBuf);
+
+ CORO_END_CODE;
+}
+
+bool RMCharacter::endOfPath() {
+ return _bEndOfPath;
+}
+
+void RMCharacter::stop(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _bMoving = false;
+
+ // You never know..
+ _status = STAND;
+ _minPath = 0;
+
+ if (!_bNeedToStop)
+ return;
+
+ _bNeedToStop = false;
+
+ switch (getCurPattern()) {
+ case PAT_WALKUP:
+ setPattern(PAT_STANDUP);
+ break;
+
+ case PAT_WALKDOWN:
+ setPattern(PAT_STANDDOWN);
+ break;
+
+ case PAT_WALKLEFT:
+ setPattern(PAT_STANDLEFT);
+ break;
+
+ case PAT_WALKRIGHT:
+ setPattern(PAT_STANDRIGHT);
+ break;
+
+ default:
+ setPattern(PAT_STANDDOWN);
+ break;
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Check if the character is moving
+ */
+bool RMCharacter::isMoving() {
+ return _bMoving;
+}
+
+inline int RMCharacter::inWhichBox(const RMPoint &pt) {
+ return _theBoxes->whichBox(_curLocation, pt);
+}
+
+
+void RMCharacter::move(CORO_PARAM, RMPoint pt, bool *result) {
+ CORO_BEGIN_CONTEXT;
+ RMPoint dest;
+ int numbox;
+ RMBoxLoc *cur;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _bMoving = true;
+
+ // 0, 0 does not do anything, just stops the character
+ if (pt._x == 0 && pt._y == 0) {
+ _minPath = 0;
+ _status = STAND;
+ CORO_INVOKE_0(stop);
+ if (result)
+ *result = true;
+ return;
+ }
+
+ // If clicked outside the box
+ _ctx->numbox = inWhichBox(pt);
+ if (_ctx->numbox == -1) {
+ // Find neareste point inside the box
+ _ctx->dest = nearestPoint(pt);
+
+ // ???!??
+ if (_ctx->dest == pt)
+ _ctx->dest = invScanLine(pt);
+
+ pt = _ctx->dest;
+ _ctx->numbox = inWhichBox(pt);
+ }
+
+ _ctx->cur = _theBoxes->getBoxes(_curLocation);
+
+ _minPath = 0;
+ _status = STAND;
+ _bMovingWithoutMinpath = true;
+ if (scanLine(pt))
+ CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed);
+ else if (findPath(inWhichBox(_pos), inWhichBox(pt))) {
+ _bMovingWithoutMinpath = false;
+ _minPath = 1;
+ _pathCount = 1;
+ _pathEnd = pt;
+ } else {
+ // @@@ This case is whether a hotspot is inside a box, but there is
+ // a path to get there. We use the InvScanLine to search around a point
+ _ctx->dest = invScanLine(pt);
+ pt = _ctx->dest;
+
+ if (scanLine(pt))
+ CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed);
+ else if (findPath(inWhichBox(_pos), inWhichBox(pt))) {
+ _bMovingWithoutMinpath = false;
+ _minPath = 1;
+ _pathCount = 1;
+ _pathEnd = pt;
+ if (result)
+ *result = true;
+ } else {
+ if (result)
+ *result = false;
+ }
+
+ return;
+ }
+
+ if (result)
+ *result = true;
+
+ CORO_END_CODE;
+}
+
+void RMCharacter::setPosition(const RMPoint &pt, int newloc) {
+ RMBoxLoc *box;
+
+ _minPath = 0;
+ _status = STAND;
+ _pos = pt;
+
+ if (newloc != -1)
+ _curLocation = newloc;
+
+ // Update the character's Z value
+ box = _theBoxes->getBoxes(_curLocation);
+ _curBox = inWhichBox(_pos);
+ assert(_curBox != -1);
+ _z = box->_boxes[_curBox]._destZ;
+ _bRemoveFromOT = true;
+}
+
+void RMCharacter::waitForEndMovement(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bMoving)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfPath, CORO_INFINITE);
+
+ CORO_END_CODE;
+}
+
+void RMCharacter::setFixedScroll(const RMPoint &fix) {
+ _fixedScroll = fix;
+}
+
+void RMCharacter::setSpeed(int speed) {
+ _curSpeed = speed;
+}
+
+void RMCharacter::removeThis(CORO_PARAM, bool &result) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bRemoveFromOT)
+ result = true;
+ else
+ CORO_INVOKE_1(RMItem::removeThis, result);
+
+ CORO_END_CODE;
+}
+
+RMCharacter::RMCharacter() {
+ _csMove = g_system->createMutex();
+ _hEndOfPath = CoroScheduler.createEvent(false, false);
+ _minPath = 0;
+ _curSpeed = 3;
+ _bRemoveFromOT = false;
+ _bMoving = false;
+ _curLocation = 0;
+ _curBox = 0;
+ _dx = _dy = 0;
+ _olddx = _olddy = 0;
+ _fx = _fy = _slope = 0;
+ _walkSpeed = _walkStatus = 0;
+ _nextBox = 0;
+ _pathLength = _pathCount = 0;
+ _status = STAND;
+ _theBoxes = NULL;
+ _walkCount = 0;
+ _bEndOfPath = false;
+ _bMovingWithoutMinpath = false;
+ _bDrawNow = false;
+ _bNeedToStop = false;
+
+ memset(_path, 0, sizeof(_path));
+
+ _pos.set(0, 0);
+}
+
+RMCharacter::~RMCharacter() {
+ g_system->deleteMutex(_csMove);
+ CoroScheduler.closeEvent(_hEndOfPath);
+}
+
+void RMCharacter::linkToBoxes(RMGameBoxes *boxes) {
+ _theBoxes = boxes;
+}
+
+/****************************************************************************\
+* RMBox Methods
+\****************************************************************************/
+
+void RMBox::readFromStream(Common::ReadStream &ds) {
+ uint16 w;
+ int i;
+ byte b;
+
+ // Bbox
+ _left = ds.readSint32LE();
+ _top = ds.readSint32LE();
+ _right = ds.readSint32LE();
+ _bottom = ds.readSint32LE();
+
+ // Adjacency
+ for (i = 0; i < MAXBOXES; i++) {
+ _adj[i] = ds.readSint32LE();
+ }
+
+ // Misc
+ _numHotspot = ds.readSint32LE();
+ _destZ = ds.readByte();
+ b = ds.readByte();
+ _bActive = b;
+ b = ds.readByte();
+ _bReversed = b;
+
+ // Reversed expansion space
+ for (i = 0; i < 30; i++)
+ ds.readByte();
+
+ // Hotspots
+ for (i = 0; i < _numHotspot; i++) {
+ w = ds.readUint16LE();
+ _hotspot[i]._hotx = w;
+ w = ds.readUint16LE();
+ _hotspot[i]._hoty = w;
+ w = ds.readUint16LE();
+ _hotspot[i]._destination = w;
+ }
+}
+
+/****************************************************************************\
+* RMBoxLoc Methods
+\****************************************************************************/
+
+RMBoxLoc::RMBoxLoc() {
+ _boxes = NULL;
+ _numbBox = 0;
+}
+
+RMBoxLoc::~RMBoxLoc() {
+ delete[] _boxes;
+}
+
+void RMBoxLoc::readFromStream(Common::ReadStream &ds) {
+ int i;
+ char buf[2];
+ byte ver;
+
+ // ID and version
+ buf[0] = ds.readByte();
+ buf[1] = ds.readByte();
+ ver = ds.readByte();
+ assert(buf[0] == 'B' && buf[1] == 'X');
+ assert(ver == 3);
+
+ // Number of boxes
+ _numbBox = ds.readSint32LE();
+
+ // Allocate memory for the boxes
+ _boxes = new RMBox[_numbBox];
+
+ // Read in boxes
+ for (i = 0; i < _numbBox; i++)
+ _boxes[i].readFromStream(ds);
+}
+
+void RMBoxLoc::recalcAllAdj() {
+ int i, j;
+
+ for (i = 0; i < _numbBox; i++) {
+ Common::fill(_boxes[i]._adj, _boxes[i]._adj + MAXBOXES, 0);
+
+ for (j = 0; j < _boxes[i]._numHotspot; j++)
+ if (_boxes[_boxes[i]._hotspot[j]._destination]._bActive)
+ _boxes[i]._adj[_boxes[i]._hotspot[j]._destination] = 1;
+ }
+}
+
+/****************************************************************************\
+* RMGameBoxes methods
+\****************************************************************************/
+
+RMGameBoxes::RMGameBoxes() {
+ _nLocBoxes = 0;
+ Common::fill(_allBoxes, _allBoxes + GAME_BOXES_SIZE, (RMBoxLoc *)NULL);
+}
+
+RMGameBoxes::~RMGameBoxes() {
+ for (int i = 1; i <= _nLocBoxes; ++i)
+ delete _allBoxes[i];
+}
+
+void RMGameBoxes::init() {
+ int i;
+
+ // Load boxes from disk
+ _nLocBoxes = 130;
+ for (i = 1; i <= _nLocBoxes; i++) {
+ RMRes res(10000 + i);
+
+ Common::SeekableReadStream *ds = res.getReadStream();
+
+ _allBoxes[i] = new RMBoxLoc();
+ _allBoxes[i]->readFromStream(*ds);
+
+ _allBoxes[i]->recalcAllAdj();
+
+ delete ds;
+ }
+}
+
+void RMGameBoxes::close() {
+}
+
+RMBoxLoc *RMGameBoxes::getBoxes(int nLoc) {
+ return _allBoxes[nLoc];
+}
+
+int RMGameBoxes::getLocBoxesCount() const {
+ return _nLocBoxes;
+}
+
+bool RMGameBoxes::isInBox(int nLoc, int nBox, const RMPoint &pt) {
+ RMBoxLoc *cur = getBoxes(nLoc);
+
+ if ((pt._x >= cur->_boxes[nBox]._left) && (pt._x <= cur->_boxes[nBox]._right) &&
+ (pt._y >= cur->_boxes[nBox]._top) && (pt._y <= cur->_boxes[nBox]._bottom))
+ return true;
+ else
+ return false;
+}
+
+int RMGameBoxes::whichBox(int nLoc, const RMPoint &punto) {
+ int i;
+ RMBoxLoc *cur = getBoxes(nLoc);
+
+ if (!cur)
+ return -1;
+
+ for (i = 0; i < cur->_numbBox; i++) {
+ if (cur->_boxes[i]._bActive) {
+ if ((punto._x >= cur->_boxes[i]._left) && (punto._x <= cur->_boxes[i]._right) &&
+ (punto._y >= cur->_boxes[i]._top) && (punto._y <= cur->_boxes[i]._bottom))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void RMGameBoxes::changeBoxStatus(int nLoc, int nBox, int status) {
+ _allBoxes[nLoc]->_boxes[nBox]._bActive = status;
+ _allBoxes[nLoc]->recalcAllAdj();
+}
+
+int RMGameBoxes::getSaveStateSize() {
+ int size;
+ int i;
+
+ size = 4;
+
+ for (i = 1; i <= _nLocBoxes; i++) {
+ size += 4;
+ size += _allBoxes[i]->_numbBox;
+ }
+
+ return size;
+}
+
+void RMGameBoxes::saveState(byte *state) {
+ int i, j;
+
+ // Save the number of locations with boxes
+ WRITE_LE_UINT32(state, _nLocBoxes);
+ state += 4;
+
+ // For each location, write out the number of boxes and their status
+ for (i = 1; i <= _nLocBoxes; i++) {
+ WRITE_LE_UINT32(state, _allBoxes[i]->_numbBox);
+ state += 4;
+
+ for (j = 0; j < _allBoxes[i]->_numbBox; j++)
+ *state++ = _allBoxes[i]->_boxes[j]._bActive;
+ }
+}
+
+void RMGameBoxes::loadState(byte *state) {
+ int i, j;
+ int nloc, nbox;
+
+ // Load number of items
+ nloc = READ_LE_UINT32(state);
+ state += 4;
+
+ assert(nloc <= _nLocBoxes);
+
+ // For each location, read the number of boxes and their status
+ for (i = 1; i <= nloc; i++) {
+ nbox = READ_LE_UINT32(state);
+ state += 4;
+
+ for (j = 0; j < nbox ; j++) {
+ if (j < _allBoxes[i]->_numbBox)
+ _allBoxes[i]->_boxes[j]._bActive = *state;
+
+ state++;
+ }
+
+ _allBoxes[i]->recalcAllAdj();
+ }
+}
+
+/****************************************************************************\
+* RMLocation Methods
+\****************************************************************************/
+
+/**
+ * Standard constructor
+ */
+RMLocation::RMLocation() {
+ _nItems = 0;
+ _items = NULL;
+ _buf = NULL;
+ TEMPNumLoc = 0;
+ _cmode = CM_256;
+}
+
+RMPoint RMLocation::TEMPGetTonyStart() {
+ return TEMPTonyStart;
+}
+
+int RMLocation::TEMPGetNumLoc() {
+ return TEMPNumLoc;
+}
+
+/**
+ * Load a location (.LOC) from a given data stream
+ *
+ * @param ds Data stream
+ * @returns True if succeeded OK, false in case of error.
+ */
+bool RMLocation::load(Common::SeekableReadStream &ds) {
+ char id[3];
+ int dimx, dimy;
+ byte ver;
+ byte cm;
+ int i;
+
+ // Reset dirty rectangling
+ _prevScroll.set(-1, -1);
+ _prevFixedScroll.set(-1, -1);
+
+ // Check the ID
+ ds.read(id, 3);
+
+ // Check if we are in a LOX
+ if (id[0] == 'L' && id[1] == 'O' && id[2] == 'X')
+ return loadLOX(ds);
+
+ // Otherwise, check that it is a normal LOC
+ if (id[0] != 'L' || id[1] != 'O' || id[2] != 'C')
+ return false;
+
+ // Version
+ ver = ds.readByte();
+ assert(ver == 6);
+
+ // Location name
+ _name = readString(ds);
+
+ // Skip the MPAL bailouts (64 bytes)
+ TEMPNumLoc = ds.readSint32LE();
+ TEMPTonyStart._x = ds.readSint32LE();
+ TEMPTonyStart._y = ds.readSint32LE();
+ ds.skip(64 - 4 * 3);
+
+ // Skip flag associated with the background (?)
+ ds.skip(1);
+
+ // Location dimensions
+ dimx = ds.readSint32LE();
+ dimy = ds.readSint32LE();
+ _curScroll.set(0, 0);
+
+ // Read the color mode
+ cm = ds.readByte();
+ _cmode = (RMColorMode)cm;
+
+ // Initialize the source buffer and read the location
+ switch (_cmode) {
+ case CM_256:
+ _buf = new RMGfxSourceBuffer8;
+ break;
+
+ case CM_65K:
+ _buf = new RMGfxSourceBuffer16;
+ break;
+
+ default:
+ assert(0);
+ break;
+ };
+
+ // Initialize the surface, loading the palette if necessary
+ _buf->init(ds, dimx, dimy, true);
+
+ // Check the size of the location
+// assert(dimy!=512);
+
+ // Number of objects
+ _nItems = ds.readSint32LE();
+
+ // Create and read in the objects
+ if (_nItems > 0)
+ _items = new RMItem[_nItems];
+
+
+ g_vm->freezeTime();
+ for (i = 0; i < _nItems && !ds.err(); i++)
+ _items[i].readFromStream(ds);
+ g_vm->unfreezeTime();
+
+ return ds.err();
+}
+
+
+bool RMLocation::loadLOX(Common::SeekableReadStream &ds) {
+ int dimx, dimy;
+ byte ver;
+ int i;
+
+ // Version
+ ver = ds.readByte();
+ assert(ver == 1);
+
+ // Location name
+ _name = readString(ds);
+
+ // Location number
+ TEMPNumLoc = ds.readSint32LE();
+ TEMPTonyStart._x = ds.readSint32LE();
+ TEMPTonyStart._y = ds.readSint32LE();
+
+ // Dimensions
+ dimx = ds.readSint32LE();
+ dimy = ds.readSint32LE();
+ _curScroll.set(0, 0);
+
+ // It's always 65K (16-bit) mode
+ _cmode = CM_65K;
+ _buf = new RMGfxSourceBuffer16;
+
+ // Initialize the surface, loading in the palette if necessary
+ _buf->init(ds, dimx, dimy, true);
+
+ // Number of items
+ _nItems = ds.readSint32LE();
+
+ // Create and read objects
+ if (_nItems > 0)
+ _items = new RMItem[_nItems];
+
+ for (i = 0; i < _nItems && !ds.err(); i++)
+ _items[i].readFromStream(ds, true);
+
+ return ds.err();
+}
+
+
+/**
+ * Draw method overloaded from RMGfxSourceBUffer8
+ */
+void RMLocation::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ bool priorTracking;
+ bool hasChanges;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Set the position of the source scrolling
+ if (_buf->getDimy() > RM_SY || _buf->getDimx() > RM_SX) {
+ prim->setSrc(RMRect(_curScroll, _curScroll + RMPoint(640, 480)));
+ }
+
+ prim->setDst(_fixedScroll);
+
+ // Check whether dirty rects are being tracked, and if there are changes, leave tracking
+ // turned on so a dirty rect will be added for the entire background
+ _ctx->priorTracking = bigBuf.getTrackDirtyRects();
+ _ctx->hasChanges = (_prevScroll != _curScroll) || (_prevFixedScroll != _fixedScroll);
+ bigBuf.setTrackDirtyRects(_ctx->priorTracking && _ctx->hasChanges);
+
+ // Invoke the drawing method fo the image class, which will draw the location background
+ CORO_INVOKE_2(_buf->draw, bigBuf, prim);
+
+ if (_ctx->hasChanges) {
+ _prevScroll = _curScroll;
+ _prevFixedScroll = _fixedScroll;
+ }
+ bigBuf.setTrackDirtyRects(_ctx->priorTracking);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Prepare a frame, adding the location to the OT list, and all the items that have changed animation frame.
+ */
+void RMLocation::doFrame(RMGfxTargetBuffer *bigBuf) {
+ int i;
+
+ // If the location is not in the OT list, add it in
+ if (!_nInList)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+
+ // Process all the location items
+ for (i = 0; i < _nItems; i++)
+ _items[i].doFrame(bigBuf);
+}
+
+
+RMItem *RMLocation::getItemFromCode(uint32 dwCode) {
+ int i;
+
+ for (i = 0; i < _nItems; i++) {
+ if (_items[i].mpalCode() == (int)dwCode)
+ return &_items[i];
+ }
+
+ return NULL;
+}
+
+RMItem *RMLocation::whichItemIsIn(const RMPoint &pt) {
+ int found = -1;
+ int foundSize = 0;
+ int size;
+
+ for (int i = 0; i < _nItems; i++) {
+ size = 0;
+ if (_items[i].isIn(pt, &size)) {
+ if (found == -1 || size < foundSize) {
+ foundSize = size;
+ found = i;
+ }
+ }
+ }
+
+ if (found == -1)
+ return NULL;
+ else
+ return &_items[found];
+}
+
+RMLocation::~RMLocation() {
+ unload();
+}
+
+void RMLocation::unload() {
+ // Clear memory
+ if (_items) {
+ delete[] _items;
+ _items = NULL;
+ }
+
+ // Destroy the buffer
+ if (_buf) {
+ delete _buf;
+ _buf = NULL;
+ }
+}
+
+void RMLocation::updateScrolling(const RMPoint &ptShowThis) {
+ RMPoint oldScroll = _curScroll;
+
+ if (_curScroll._x + 250 > ptShowThis._x) {
+ _curScroll._x = ptShowThis._x - 250;
+ } else if (_curScroll._x + RM_SX - 250 < ptShowThis._x) {
+ _curScroll._x = ptShowThis._x + 250 - RM_SX;
+ } else if (ABS(_curScroll._x + RM_SX / 2 - ptShowThis._x) > 32 && _buf->getDimx() > RM_SX) {
+ if (_curScroll._x + RM_SX / 2 < ptShowThis._x)
+ _curScroll._x++;
+ else
+ _curScroll._x--;
+ }
+
+ if (_curScroll._y + 180 > ptShowThis._y) {
+ _curScroll._y = ptShowThis._y - 180;
+ } else if (_curScroll._y + RM_SY - 180 < ptShowThis._y) {
+ _curScroll._y = ptShowThis._y + 180 - RM_SY;
+ } else if (ABS(_curScroll._y + RM_SY / 2 - ptShowThis._y) > 16 && _buf->getDimy() > RM_SY) {
+ if (_curScroll._y + RM_SY / 2 < ptShowThis._y)
+ _curScroll._y++;
+ else
+ _curScroll._y--;
+ }
+
+ if (_curScroll._x < 0)
+ _curScroll._x = 0;
+ if (_curScroll._y < 0)
+ _curScroll._y = 0;
+ if (_curScroll._x + RM_SX > _buf->getDimx())
+ _curScroll._x = _buf->getDimx() - RM_SX;
+ if (_curScroll._y + RM_SY > _buf->getDimy())
+ _curScroll._y = _buf->getDimy() - RM_SY;
+
+ if (oldScroll != _curScroll) {
+ for (int i = 0; i < _nItems; i++)
+ _items[i].setScrollPosition(_curScroll);
+ }
+}
+
+void RMLocation::setFixedScroll(const RMPoint &scroll) {
+ _fixedScroll = scroll;
+
+ for (int i = 0; i < _nItems; i++)
+ _items[i].setScrollPosition(_curScroll - _fixedScroll);
+}
+
+void RMLocation::setScrollPosition(const RMPoint &scroll) {
+ RMPoint pt = scroll;
+ if (pt._x < 0)
+ pt._x = 0;
+ if (pt._y < 0)
+ pt._y = 0;
+ if (pt._x + RM_SX > _buf->getDimx())
+ pt._x = _buf->getDimx() - RM_SX;
+ if (pt._y + RM_SY > _buf->getDimy())
+ pt._y = _buf->getDimy() - RM_SY;
+
+ _curScroll = pt;
+
+ for (int i = 0; i < _nItems; i++)
+ _items[i].setScrollPosition(_curScroll);
+}
+
+
+void RMLocation::pauseSound(bool bPause) {
+ int i;
+
+ for (i = 0; i < _nItems; i++)
+ _items[i].pauseSound(bPause);
+}
+
+/**
+ * Read the current scroll position
+ */
+RMPoint RMLocation::scrollPosition() {
+ return _curScroll;
+}
+
+/****************************************************************************\
+* RMMessage Methods
+\****************************************************************************/
+
+RMMessage::RMMessage(uint32 dwId) {
+ load(dwId);
+}
+
+RMMessage::RMMessage() {
+ _lpMessage = NULL;
+ _nPeriods = 0;
+ for (int i = 0; i < 256; i++)
+ _lpPeriods[i] = 0;
+}
+
+RMMessage::~RMMessage() {
+ if (_lpMessage)
+ globalDestroy(_lpMessage);
+}
+
+void RMMessage::load(uint32 dwId) {
+ _lpMessage = mpalQueryMessage(dwId);
+ assert(_lpMessage != NULL);
+
+ if (_lpMessage)
+ parseMessage();
+}
+
+void RMMessage::parseMessage() {
+ char *p;
+
+ assert(_lpMessage != NULL);
+
+ _nPeriods = 1;
+ p = _lpPeriods[0] = _lpMessage;
+
+ for (;;) {
+ // Find the end of the current period
+ while (*p != '\0')
+ p++;
+
+ // If there is another '0' at the end of the string, the end has been found
+ p++;
+ if (*p == '\0')
+ break;
+
+ // Otherwise there is another line, and remember it's start
+ _lpPeriods[_nPeriods++] = p;
+ }
+}
+
+bool RMMessage::isValid() {
+ return _lpMessage != NULL;
+}
+
+int RMMessage::numPeriods() {
+ return _nPeriods;
+}
+
+char *RMMessage::period(int num) {
+ return _lpPeriods[num];
+}
+
+char *RMMessage::operator[](int num) {
+ return _lpPeriods[num];
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/loc.h b/engines/tony/loc.h
new file mode 100644
index 0000000000..61eece2440
--- /dev/null
+++ b/engines/tony/loc.h
@@ -0,0 +1,582 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_LOC_H
+#define TONY_LOC_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/file.h"
+#include "tony/sound.h"
+#include "tony/utils.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* Various defines
+\****************************************************************************/
+
+/**
+ * Valid color modes
+ */
+typedef enum {
+ CM_256,
+ CM_65K
+} RMColorMode;
+
+
+/****************************************************************************\
+* Class declarations
+\****************************************************************************/
+
+/**
+ * Generic palette
+ */
+class RMPalette {
+public:
+ byte _data[1024];
+
+public:
+ void readFromStream(Common::ReadStream &ds);
+};
+
+
+/**
+ * Sound effect of an object
+ */
+class RMSfx {
+public:
+ Common::String _name;
+ FPSfx *_fx;
+ bool _bPlayingLoop;
+
+public:
+ RMSfx();
+ virtual ~RMSfx();
+
+ void play(bool bLoop = false);
+ void setVolume(int vol);
+ void pause(bool bPause);
+ void stop();
+
+ void readFromStream(Common::ReadStream &ds, bool bLOX = false);
+};
+
+
+/**
+ * Object pattern
+ */
+class RMPattern {
+public:
+ // Type of slot
+ enum RMSlotType {
+ DUMMY1 = 0,
+ DUMMY2,
+ SPRITE,
+ SOUND,
+ COMMAND,
+ SPECIAL
+ };
+
+ // Class slot
+ class RMSlot {
+ private:
+ RMPoint _pos; // Child co-ordinates
+
+ public:
+ RMSlotType _type;
+ int _data;
+ byte _flag;
+
+ public:
+ RMPoint pos() {
+ return _pos;
+ }
+
+ void readFromStream(Common::ReadStream &ds, bool bLOX = false);
+ };
+
+public:
+ Common::String _name;
+
+private:
+ int _speed;
+ RMPoint _pos; // Parent coordinates
+ RMPoint _curPos; // Parent + child coordinates
+ int _bLoop;
+ int _nSlots;
+ int _nCurSlot;
+ int _nCurSprite;
+
+ RMSlot *_slots;
+
+ uint32 _nStartTime;
+
+public:
+ RMPattern();
+ virtual ~RMPattern();
+
+ // A warning that the pattern now and the current
+ int init(RMSfx *sfx, bool bPlayP0 = false, byte *bFlag = NULL);
+
+ // Update the pattern, checking to see if it's time to change slot and executing
+ // any associated commands
+ int update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx);
+
+ // Stop a sound effect
+ void stopSfx(RMSfx *sfx);
+
+ // Reads the position of the pattern
+ RMPoint pos();
+
+ void readFromStream(Common::ReadStream &ds, bool bLOX = false);
+
+private:
+ void updateCoord();
+};
+
+
+/**
+ * Sprite (frame) animation of an item
+ */
+class RMSprite : public RMGfxTask {
+public:
+ Common::String _name;
+ RMRect _rcBox;
+
+protected:
+ RMGfxSourceBuffer *_buf;
+
+public:
+ RMSprite();
+ virtual ~RMSprite();
+
+ void init(RMGfxSourceBuffer *buf);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+ void setPalette(byte *lpBuf);
+ void getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy);
+ void LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy);
+
+ void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false);
+};
+
+
+/**
+ * Data on an item
+ */
+class RMItem : public RMGfxTask {
+public:
+ Common::String _name;
+
+protected:
+ int _z;
+ RMPoint _pos; // Coordinate ancestor
+ RMColorMode _cm;
+ RMPoint _curScroll;
+
+ byte _FX;
+ byte _FXparm;
+
+ virtual int getCurPattern();
+
+private:
+ int _nCurPattern;
+ int _mpalCode;
+ RMPoint _hot;
+ RMRect _rcBox;
+ int _nSprites, _nSfx, _nPatterns;
+ byte _bPal;
+ RMPalette _pal;
+
+ RMSprite *_sprites;
+ RMSfx *_sfx;
+ RMPattern *_patterns;
+
+ byte _bCurFlag;
+ int _nCurSprite;
+ bool _bIsActive;
+ uint32 _hEndPattern;
+ bool _bInitCurPattern;
+
+public:
+ RMPoint calculatePos();
+
+public:
+ RMItem();
+ virtual ~RMItem();
+
+ // Process to make the object move on any animations.
+ // Returns TRUE if it should be redrawn on the next frame
+ bool doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList = true);
+
+ // Sets the current scrolling position
+ void setScrollPosition(const RMPoint &scroll);
+
+ // Overloading of check whether to remove from active list
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Overloaded Draw
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Overloaded priority: it's based on Z ordering
+ virtual int priority();
+
+ // Pattern number
+ int numPattern();
+
+ // Set anew animation pattern, changing abruptly from the current
+ virtual void setPattern(int nPattern, bool bPlayP0 = false);
+
+ // Set a new status
+ void setStatus(int nStatus);
+
+ bool isIn(const RMPoint &pt, int *size = NULL);
+ RMPoint hotspot();
+ bool getName(Common::String &name);
+ int mpalCode();
+
+ // Unload
+ void unload();
+
+ // Wait for the end of the current pattern
+ void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE);
+
+ // Sets a new hotspot fro the object
+ void changeHotspot(const RMPoint &pt);
+
+ void setInitCurPattern(bool status);
+
+ void playSfx(int nSfx);
+
+ void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false);
+
+ void pauseSound(bool bPause);
+
+protected:
+ // Create a primitive that has as it's task this item
+ virtual RMGfxPrimitive *newItemPrimitive();
+
+ // Allocate memory for the sprites
+ virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE);
+};
+
+
+#define MAXBOXES 50 // Maximum number of allowed boxes
+#define MAXHOTSPOT 20 // Maximum nimber of allowed hotspots
+
+class RMBox {
+public:
+ struct Hotspot {
+ int _hotx, _hoty; // Hotspot coordinates
+ int _destination; // Hotspot destination
+ };
+
+public:
+ int _left, _top, _right, _bottom; // Vertici bounding boxes
+ int _adj[MAXBOXES]; // List of adjacent bounding boxes
+ int _numHotspot; // Hotspot number
+ uint8 _destZ; // Z value for the bounding box
+ Hotspot _hotspot[MAXHOTSPOT]; // List of hotspots
+
+ bool _bActive;
+ bool _bReversed;
+
+ void readFromStream(Common::ReadStream &ds);
+};
+
+
+class RMBoxLoc {
+public:
+ int _numbBox;
+ RMBox *_boxes;
+
+ void readFromStream(Common::ReadStream &ds);
+
+public:
+ RMBoxLoc();
+ virtual ~RMBoxLoc();
+
+ void recalcAllAdj();
+};
+
+#define GAME_BOXES_SIZE 200
+
+class RMGameBoxes {
+protected:
+ RMBoxLoc *_allBoxes[GAME_BOXES_SIZE];
+ int _nLocBoxes;
+
+public:
+ RMGameBoxes();
+ ~RMGameBoxes();
+
+ void init();
+ void close();
+
+ // Get binding boxes for a given location
+ RMBoxLoc *getBoxes(int nLoc);
+ int getLocBoxesCount() const;
+
+ // Return the box which contains a given point
+ int whichBox(int nLoc, const RMPoint &pt);
+
+ // Check whether a point is inside a given box
+ bool isInBox(int nLoc, int nBox, const RMPoint &pt);
+
+ // Change the status of a box
+ void changeBoxStatus(int nLoc, int nBox, int status);
+
+ // Save state handling
+ int getSaveStateSize();
+ void saveState(byte *buf);
+ void loadState(byte *buf);
+};
+
+class RMCharacter : protected RMItem {
+public:
+ enum Patterns {
+ PAT_STANDUP = 1,
+ PAT_STANDDOWN,
+ PAT_STANDLEFT,
+ PAT_STANDRIGHT,
+ PAT_WALKUP,
+ PAT_WALKDOWN,
+ PAT_WALKLEFT,
+ PAT_WALKRIGHT
+ };
+
+private:
+ enum CharacterStatus {
+ STAND,
+ WALK
+ };
+
+ signed short _walkCount;
+ int _dx, _dy, _olddx, _olddy;
+ float _fx, _fy, _slope;
+ RMPoint _lineStart, _lineEnd, _pathEnd;
+ signed char _walkSpeed, _walkStatus;
+ char _minPath;
+ short _nextBox;
+ short _path[MAXBOXES];
+ short _pathLength, _pathCount;
+ int _curBox;
+
+ CharacterStatus _status;
+ int _curSpeed;
+ bool _bEndOfPath;
+ uint32 _hEndOfPath;
+ OSystem::MutexRef _csMove;
+ int _curLocation;
+ bool _bRemoveFromOT;
+ bool _bMovingWithoutMinpath;
+ RMGameBoxes *_theBoxes;
+
+ RMPoint _fixedScroll;
+
+private:
+ int inWhichBox(const RMPoint &pt);
+
+ short findPath(short source, short destination);
+ RMPoint searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint punto);
+ RMPoint nearestPoint(const RMPoint &punto);
+
+ void goTo(CORO_PARAM, RMPoint destcoord, bool bReversed = false);
+ short scanLine(const RMPoint &punto);
+ RMPoint invScanLine(const RMPoint &punto);
+ RMPoint nearestHotSpot(int sourcebox, int destbox);
+
+ void newBoxEntered(int nBox);
+
+protected:
+ bool _bMoving;
+ bool _bDrawNow;
+ bool _bNeedToStop;
+
+public:
+ RMCharacter();
+ virtual ~RMCharacter();
+
+ void linkToBoxes(RMGameBoxes *theBoxes);
+
+ virtual void removeThis(CORO_PARAM, bool &result);
+
+ // Update the position of a character
+ void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc);
+
+ // Overloaded draw
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // TRUE if you just stopped
+ bool endOfPath();
+
+ // Change the pattern of a character to STOP
+ virtual void stop(CORO_PARAM);
+
+ // Check if the character is moving
+ bool isMoving();
+
+ // Move the character to a certain position
+ void move(CORO_PARAM, RMPoint pt, bool *result = NULL);
+
+ // Place the character in a certain position WITHOUT moving
+ void setPosition(const RMPoint &pt, int newloc = -1);
+
+ // Wait for the end of movement
+ void waitForEndMovement(CORO_PARAM);
+
+ void setFixedScroll(const RMPoint &fix);
+ void setSpeed(int speed);
+};
+
+
+class RMWipe : public RMGfxTask {
+private:
+ bool _bFading;
+ bool _bEndFade;
+ bool _bUnregister;
+ uint32 _hUnregistered;
+ int _nFadeStep;
+ uint32 _hEndOfFade;
+ bool _bMustRegister;
+
+ RMItem _wip0r;
+
+public:
+ RMWipe();
+ virtual ~RMWipe();
+
+ void doFrame(RMGfxTargetBuffer &bigBuf);
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ void initFade(int type);
+ void closeFade();
+ void waitForFadeEnd(CORO_PARAM);
+
+ virtual void Unregister();
+ virtual void removeThis(CORO_PARAM, bool &result);
+ virtual int priority();
+};
+
+
+/**
+ * Location
+ */
+class RMLocation : public RMGfxTaskSetPrior {
+public:
+ Common::String _name; // Name
+
+private:
+ RMColorMode _cmode; // Color mode
+ RMGfxSourceBuffer *_buf; // Location picture
+
+ int _nItems; // Number of objects
+ RMItem *_items; // Objects
+
+ RMPoint _curScroll; // Current scroll position
+ RMPoint _fixedScroll;
+
+ RMPoint _prevScroll; // Previous scroll position
+ RMPoint _prevFixedScroll;
+
+public:
+ // @@@@@@@@@@@@@@@@@@@@@@@
+
+ RMPoint TEMPTonyStart;
+ RMPoint TEMPGetTonyStart();
+
+ int TEMPNumLoc;
+ int TEMPGetNumLoc();
+
+public:
+ RMLocation();
+ virtual ~RMLocation();
+
+ // Load variations
+ bool load(Common::SeekableReadStream &ds);
+ bool loadLOX(Common::SeekableReadStream &ds);
+
+ // Unload
+ void unload();
+
+ // Overloaded draw
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ // Prepare a frame by drawing the location and all it's items
+ void doFrame(RMGfxTargetBuffer *bigBuf);
+
+ // Return the item at a given point
+ RMItem *whichItemIsIn(const RMPoint &pt);
+
+ // Return the item based on it's MPAL code
+ RMItem *getItemFromCode(uint32 dwCode);
+
+ // Set the current scroll position
+ void setScrollPosition(const RMPoint &scroll);
+
+ // Sets an additinal offset for scrolling
+ void setFixedScroll(const RMPoint &scroll);
+
+ // Update the scrolling coordinates to display the specified point
+ void updateScrolling(const RMPoint &ptShowThis);
+
+ // Read the current scroll position
+ RMPoint scrollPosition();
+
+ // Pause sound
+ void pauseSound(bool bPause);
+};
+
+
+/**
+ * MPAL message, composed of more ASCIIZ
+ */
+class RMMessage {
+private:
+ char *_lpMessage;
+ char *_lpPeriods[256];
+ int _nPeriods;
+
+private:
+ void parseMessage();
+
+public:
+ RMMessage();
+ RMMessage(uint32 dwId);
+ virtual ~RMMessage();
+
+ void load(uint32 dwId);
+ bool isValid();
+ int numPeriods();
+ char *period(int num);
+ char *operator[](int num);
+};
+
+} // End of namespace Tony
+
+#endif /* TONY_H */
diff --git a/engines/tony/module.mk b/engines/tony/module.mk
new file mode 100644
index 0000000000..d66cf6f065
--- /dev/null
+++ b/engines/tony/module.mk
@@ -0,0 +1,33 @@
+MODULE := engines/tony
+
+MODULE_OBJS := \
+ custom.o \
+ debugger.o \
+ detection.o \
+ font.o \
+ game.o \
+ gfxcore.o \
+ gfxengine.o \
+ globals.o \
+ input.o \
+ inventory.o \
+ loc.o \
+ sound.o \
+ tony.o \
+ tonychar.o \
+ utils.o \
+ window.o \
+ mpal/expr.o \
+ mpal/loadmpc.o \
+ mpal/memory.o \
+ mpal/mpal.o \
+ mpal/mpalutils.o \
+ mpal/lzo.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_TONY), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/tony/mpal/expr.cpp b/engines/tony/mpal/expr.cpp
new file mode 100644
index 0000000000..7923d263c0
--- /dev/null
+++ b/engines/tony/mpal/expr.cpp
@@ -0,0 +1,433 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/mpaldll.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/**
+ * @defgroup Mathamatical operations
+ */
+//@{
+
+#define OP_MUL ((1 << 4) | 0)
+#define OP_DIV ((1 << 4) | 1)
+#define OP_MODULE ((1 << 4) | 2)
+#define OP_ADD ((2 << 4) | 0)
+#define OP_SUB ((2 << 4) | 1)
+#define OP_SHL ((3 << 4) | 0)
+#define OP_SHR ((3 << 4) | 1)
+#define OP_MINOR ((4 << 4) | 0)
+#define OP_MAJOR ((4 << 4) | 1)
+#define OP_MINEQ ((4 << 4) | 2)
+#define OP_MAJEQ ((4 << 4) | 3)
+#define OP_EQUAL ((5 << 4) | 0)
+#define OP_NOEQUAL ((5 << 4) | 1)
+#define OP_BITAND ((6 << 4) | 0)
+#define OP_BITXOR ((7 << 4) | 0)
+#define OP_BITOR ((8 << 4) | 0)
+#define OP_AND ((9 << 4) | 0)
+#define OP_OR ((10 << 4) | 0)
+
+
+/**
+ * Object types that can be contained in an EXPRESSION structure
+ */
+enum ExprListTypes {
+ ELT_NUMBER = 1,
+ ELT_VAR = 2,
+ ELT_PARENTH = 3,
+ ELT_PARENTH2 = 4
+};
+
+//@}
+
+/**
+ * @defgroup Structures
+ */
+//@{
+
+/**
+ * Mathamatical framework to manage operations
+ */
+typedef struct {
+ byte _type; // Tipo di oggetto (vedi enum ExprListTypes)
+ byte _unary; // Unary operatore (NON SUPPORTATO)
+
+ union {
+ int _num; // Numero (se type==ELT_NUMBER)
+ char *_name; // Nome variabile (se type==ELT_VAR)
+ MpalHandle _son; // Handle a espressione (type==ELT_PARENTH)
+ byte *_pson; // Handle lockato (type==ELT_PARENTH2)
+ } _val;
+
+ byte _symbol; // Simbolo matematico (vedi #define OP_*)
+
+} Expression;
+typedef Expression *LpExpression;
+
+//@}
+
+/**
+ * Duplicate a mathematical expression.
+ *
+ * @param h Handle to the original expression
+ * @retruns Pointer to the cloned expression
+ */
+static byte *duplicateExpression(MpalHandle h) {
+ byte *orig, *clone;
+ LpExpression one, two;
+
+ orig = (byte *)globalLock(h);
+
+ int num = *(byte *)orig;
+ one = (LpExpression)(orig+1);
+
+ clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + 1);
+ two = (LpExpression)(clone + 1);
+
+ memcpy(clone, orig, sizeof(Expression) * num + 1);
+
+ for (int i = 0; i < num; i++) {
+ if (one->_type == ELT_PARENTH) {
+ two->_type = ELT_PARENTH2;
+ two->_val._pson = duplicateExpression(two->_val._son);
+ }
+
+ ++one;
+ ++two;
+ }
+
+ globalUnlock(h);
+ return clone;
+}
+
+static int Compute(int a, int b, byte symbol) {
+ switch (symbol) {
+ case OP_MUL:
+ return a * b;
+ case OP_DIV:
+ return a / b;
+ case OP_MODULE:
+ return a % b;
+ case OP_ADD:
+ return a + b;
+ case OP_SUB:
+ return a - b;
+ case OP_SHL:
+ return a << b;
+ case OP_SHR:
+ return a >> b;
+ case OP_MINOR:
+ return a < b;
+ case OP_MAJOR:
+ return a > b;
+ case OP_MINEQ:
+ return a <= b;
+ case OP_MAJEQ:
+ return a >= b;
+ case OP_EQUAL:
+ return a == b;
+ case OP_NOEQUAL:
+ return a != b;
+ case OP_BITAND:
+ return a & b;
+ case OP_BITXOR:
+ return a ^ b;
+ case OP_BITOR:
+ return a | b;
+ case OP_AND:
+ return a && b;
+ case OP_OR:
+ return a || b;
+ default:
+ GLOBALS._mpalError = 1;
+ break;
+ }
+
+ return 0;
+}
+
+static void solve(LpExpression one, int num) {
+ LpExpression two, three;
+ int j;
+
+ while (num > 1) {
+ two = one + 1;
+ if ((two->_symbol == 0) || (one->_symbol & 0xF0) <= (two->_symbol & 0xF0)) {
+ two->_val._num = Compute(one->_val._num, two->_val._num, one->_symbol);
+ memmove(one, two, (num - 1) * sizeof(Expression));
+ --num;
+ } else {
+ j = 1;
+ three = two + 1;
+ while ((three->_symbol != 0) && (two->_symbol & 0xF0) > (three->_symbol & 0xF0)) {
+ ++two;
+ ++three;
+ ++j;
+ }
+
+ three->_val._num = Compute(two->_val._num, three->_val._num, two->_symbol);
+ memmove(two, three, (num - j - 1) * sizeof(Expression));
+ --num;
+ }
+ }
+}
+
+
+/**
+ * Calculates the result of a mathematical expression, replacing the current
+ * value of any variable.
+ *
+ * @param expr Pointer to an expression duplicated by DuplicateExpression
+ * @returns Value
+ */
+static int evaluateAndFreeExpression(byte *expr) {
+ LpExpression one, cur;
+
+ int num = *expr;
+ one = (LpExpression)(expr + 1);
+
+ // 1) Substitutions of variables
+ cur = one;
+ for (int i = 0; i < num; i++, cur++) {
+ if (cur->_type == ELT_VAR) {
+ cur->_type = ELT_NUMBER;
+ cur->_val._num = varGetValue(cur->_val._name);
+ }
+ }
+
+ // 2) Replacement of brackets (using recursive calls)
+ cur = one;
+ for (int i = 0; i < num; i++, cur++) {
+ if (cur->_type == ELT_PARENTH2) {
+ cur->_type = ELT_NUMBER;
+ cur->_val._num = evaluateAndFreeExpression(cur->_val._pson);
+ }
+ }
+
+ // 3) algebraic resolution
+ solve(one, num);
+ int val = one->_val._num;
+ globalDestroy(expr);
+
+ return val;
+}
+
+
+/**
+ * Parses a mathematical expression from the MPC file
+ *
+ * @param buf Buffer containing the expression to evaluate
+ * @param h Pointer to a handle that, at the end of execution,
+ * will point to the area of memory containing the parsed expression
+ * @returns Pointer to the buffer immediately after the expression, or NULL if error.
+ */
+const byte *parseExpression(const byte *lpBuf, MpalHandle *h) {
+ LpExpression cur;
+ byte *start;
+
+ uint32 num = *lpBuf;
+ lpBuf++;
+
+ if (num == 0)
+ return NULL;
+
+ *h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, num * sizeof(Expression) + 1);
+ if (*h == NULL)
+ return NULL;
+
+ start = (byte *)globalLock(*h);
+ *start = (byte)num;
+
+ cur = (LpExpression)(start + 1);
+
+ for (uint32 i = 0;i < num; i++) {
+ cur->_type = *(lpBuf);
+ cur->_unary = *(lpBuf + 1);
+ lpBuf += 2;
+ switch (cur->_type) {
+ case ELT_NUMBER:
+ cur->_val._num = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case ELT_VAR:
+ cur->_val._name = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, (*lpBuf) + 1);
+ if (cur->_val._name == NULL)
+ return NULL;
+ memcpy(cur->_val._name, lpBuf + 1, *lpBuf);
+ lpBuf += *lpBuf + 1;
+ break;
+
+ case ELT_PARENTH:
+ lpBuf = parseExpression(lpBuf, &cur->_val._son);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ cur->_symbol = *lpBuf;
+ lpBuf++;
+
+ cur++;
+ }
+
+ if (*lpBuf != 0)
+ return NULL;
+
+ lpBuf++;
+
+ return lpBuf;
+}
+
+
+/**
+ * Calculate the value of a mathamatical expression
+ *
+ * @param h Handle to the expression
+ * @returns Numeric value
+ */
+int evaluateExpression(MpalHandle h) {
+ int ret;
+
+ lockVar();
+ ret = evaluateAndFreeExpression(duplicateExpression(h));
+ unlockVar();
+
+ return ret;
+}
+
+/**
+ * Compare two mathematical expressions together
+ *
+ * @param h1 Expression to be compared
+ * @param h2 Expression to be compared
+ */
+bool compareExpressions(MpalHandle h1, MpalHandle h2) {
+ byte *e1, *e2;
+ LpExpression one, two;
+
+ e1 = (byte *)globalLock(h1);
+ e2 = (byte *)globalLock(h2);
+
+ int num1 = *(byte *)e1;
+ int num2 = *(byte *)e2;
+
+ if (num1 != num2) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+
+ one = (LpExpression)(e1 + 1);
+ two = (LpExpression)(e2 + 1);
+
+ for (int i = 0; i < num1; i++) {
+ if (one->_type != two->_type || (i != num1 - 1 && one->_symbol != two->_symbol)) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+
+ switch (one->_type) {
+ case ELT_NUMBER:
+ if (one->_val._num != two->_val._num) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+ break;
+
+ case ELT_VAR:
+ if (strcmp(one->_val._name, two->_val._name) != 0) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+ break;
+
+ case ELT_PARENTH:
+ if (!compareExpressions(one->_val._son, two->_val._son)) {
+ globalUnlock(h1);
+ globalUnlock(h2);
+ return false;
+ }
+ break;
+ }
+
+ ++one;
+ ++two;
+ }
+
+ globalUnlock(h1);
+ globalUnlock(h2);
+
+ return true;
+}
+
+/**
+ * Frees an expression that was previously parsed
+ *
+ * @param h Handle for the expression
+ */
+void freeExpression(MpalHandle h) {
+ byte *data = (byte *)globalLock(h);
+ int num = *data;
+ LpExpression cur = (LpExpression)(data + 1);
+
+ for (int i = 0; i < num; ++i, ++cur) {
+ switch (cur->_type) {
+ case ELT_VAR:
+ globalDestroy(cur->_val._name);
+ break;
+
+ case ELT_PARENTH:
+ freeExpression(cur->_val._son);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ globalUnlock(h);
+ globalFree(h);
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/expr.h b/engines/tony/mpal/expr.h
new file mode 100644
index 0000000000..9036099993
--- /dev/null
+++ b/engines/tony/mpal/expr.h
@@ -0,0 +1,79 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef MPAL_EXPR_H
+#define MPAL_EXPR_H
+
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Function Prototypes
+\****************************************************************************/
+
+/**
+ * Parses a mathematical expression from the MPC file
+ *
+ * @param buf Buffer containing the expression to evaluate
+ * @param h Pointer to a handle that, at the end of execution,
+ * will point to the area of memory containing the parsed expression
+ * @returns Pointer to the buffer immediately after the expression, or NULL if error.
+ */
+const byte *parseExpression(const byte *lpBuf, MpalHandle *h);
+
+/**
+ * Calculate the value of a mathamatical expression
+ *
+ * @param h Handle to the expression
+ * @returns Numeric value
+ */
+int evaluateExpression(MpalHandle h);
+
+/**
+ * Compare two mathematical expressions together
+ *
+ * @param h1 Expression to be compared
+ * @param h2 Expression to be compared
+ */
+bool compareExpressions(MpalHandle h1, MpalHandle h2);
+
+/**
+ * Frees an expression that was previously parsed
+ *
+ * @param h Handle for the expression
+ */
+void freeExpression(MpalHandle h);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp
new file mode 100644
index 0000000000..953820be74
--- /dev/null
+++ b/engines/tony/mpal/loadmpc.cpp
@@ -0,0 +1,790 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "mpal.h"
+#include "mpaldll.h"
+#include "memory.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Static functions
+\****************************************************************************/
+
+static bool compareCommands(struct Command *cmd1, struct Command *cmd2) {
+ if (cmd1->_type == 2 && cmd2->_type == 2) {
+ if (strcmp(cmd1->_lpszVarName, cmd2->_lpszVarName) == 0 &&
+ compareExpressions(cmd1->_expr, cmd2->_expr))
+ return true;
+ else
+ return false;
+ } else
+ return (memcmp(cmd1, cmd2, sizeof(struct Command)) == 0);
+}
+
+/**
+ * Parses a script from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled script.
+ * @param lpmsScript Pointer to a structure that will be filled with the
+ * data of the script.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ */
+static const byte *ParseScript(const byte *lpBuf, LpMpalScript lpmsScript) {
+ lpmsScript->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ lpmsScript->_nMoments = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ int curCmd = 0;
+
+ for (uint i = 0; i < lpmsScript->_nMoments; i++) {
+ lpmsScript->_moment[i]._dwTime = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_moment[i]._nCmds = *lpBuf;
+ lpBuf++;
+
+ for (int j = 0; j < lpmsScript->_moment[i]._nCmds; j++) {
+ lpmsScript->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+ switch (lpmsScript->_command[curCmd]._type) {
+ case 1:
+ lpmsScript->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmsScript->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case 2: { // Variable assign
+ int len = *lpBuf;
+ lpBuf++;
+ lpmsScript->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmsScript->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+ memcpy(lpmsScript->_command[curCmd]._lpszVarName, lpBuf, len);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmsScript->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+ }
+ default:
+ return NULL;
+ }
+
+ lpmsScript->_moment[i]._cmdNum[j] = curCmd;
+ curCmd++;
+ }
+ }
+ return lpBuf;
+}
+
+/**
+ * Frees a script allocated via a previous call to ParseScript
+ *
+ * @param lpmsScript Pointer to a script structure
+ */
+static void FreeScript(LpMpalScript lpmsScript) {
+ for (int i = 0; i < MAX_COMMANDS_PER_SCRIPT && (lpmsScript->_command[i]._type); ++i, ++lpmsScript) {
+ if (lpmsScript->_command[i]._type == 2) {
+ // Variable Assign
+ globalDestroy(lpmsScript->_command[i]._lpszVarName);
+ freeExpression(lpmsScript->_command[i]._expr);
+ }
+ }
+}
+
+/**
+ * Parses a dialog from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled dialog.
+ * @param lpmdDialog Pointer to a structure that will be filled with the
+ * data of the dialog.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ */
+static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) {
+ uint32 num2, num3;
+ byte *lpLock;
+
+ lpmdDialog->_nObj = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ // Periods
+ uint32 num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (num >= MAX_PERIODS_PER_DIALOG - 1)
+ error("Too much periods in dialog #%d", lpmdDialog->_nObj);
+
+ uint32 i;
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_periodNums[i] = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_periods[i] = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, *lpBuf + 1);
+ lpLock = (byte *)globalLock(lpmdDialog->_periods[i]);
+ Common::copy(lpBuf + 1, lpBuf + 1 + *lpBuf, lpLock);
+ globalUnlock(lpmdDialog->_periods[i]);
+ lpBuf += (*lpBuf) + 1;
+ }
+
+ lpmdDialog->_periodNums[i] = 0;
+ lpmdDialog->_periods[i] = NULL;
+
+ // Groups
+ num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ uint32 curCmd = 0;
+
+ if (num >= MAX_GROUPS_PER_DIALOG)
+ error("Too much groups in dialog #%d", lpmdDialog->_nObj);
+
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_group[i]._num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_group[i]._nCmds = *lpBuf; lpBuf++;
+
+ if (lpmdDialog->_group[i]._nCmds >= MAX_COMMANDS_PER_GROUP)
+ error("Too much commands in group #%d in dialog #%d", lpmdDialog->_group[i]._num, lpmdDialog->_nObj);
+
+ for (uint32 j = 0; j < lpmdDialog->_group[i]._nCmds; j++) {
+ lpmdDialog->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+
+ switch (lpmdDialog->_command[curCmd]._type) {
+ // Call custom function
+ case 1:
+ lpmdDialog->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_command[curCmd]._arg1 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg2 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg3 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg4 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ // Variable assign
+ case 2: {
+ uint32 len = *lpBuf;
+ lpBuf++;
+ lpmdDialog->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmdDialog->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+
+ Common::copy(lpBuf, lpBuf + len, lpmdDialog->_command[curCmd]._lpszVarName);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmdDialog->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+ }
+ // Do Choice
+ case 3:
+ lpmdDialog->_command[curCmd]._nChoice = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ uint32 kk;
+ for (kk = 0;kk < curCmd; kk++) {
+ if (compareCommands(&lpmdDialog->_command[kk], &lpmdDialog->_command[curCmd])) {
+ lpmdDialog->_group[i]._cmdNum[j] = kk;
+
+ // Free any data allocated for the duplictaed command
+ if (lpmdDialog->_command[curCmd]._type == 2) {
+ globalDestroy(lpmdDialog->_command[curCmd]._lpszVarName);
+ freeExpression(lpmdDialog->_command[curCmd]._expr);
+
+ lpmdDialog->_command[curCmd]._lpszVarName = NULL;
+ lpmdDialog->_command[curCmd]._expr = 0;
+ lpmdDialog->_command[curCmd]._type = 0;
+ }
+ break;
+ }
+ }
+
+ if (kk == curCmd) {
+ lpmdDialog->_group[i]._cmdNum[j] = curCmd;
+ curCmd++;
+ }
+ }
+ }
+
+ if (curCmd >= MAX_COMMANDS_PER_DIALOG)
+ error("Too much commands in dialog #%d", lpmdDialog->_nObj);
+
+ // Choices
+ num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (num >= MAX_CHOICES_PER_DIALOG)
+ error("Too much choices in dialog #%d", lpmdDialog->_nObj);
+
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_choice[i]._nChoice = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ num2 = *lpBuf++;
+
+ if (num2 >= MAX_SELECTS_PER_CHOICE)
+ error("Too much selects in choice #%d in dialog #%d", lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
+
+ for (uint32 j = 0; j < num2; j++) {
+ // When
+ switch (*lpBuf++) {
+ case 0:
+ lpmdDialog->_choice[i]._select[j]._when = NULL;
+ break;
+
+ case 1:
+ lpBuf = parseExpression(lpBuf, &lpmdDialog->_choice[i]._select[j]._when);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ case 2:
+ return NULL;
+ }
+
+ // Attrib
+ lpmdDialog->_choice[i]._select[j]._attr = *lpBuf++;
+
+ // Data
+ lpmdDialog->_choice[i]._select[j]._dwData = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ // PlayGroup
+ num3 = *lpBuf++;
+
+ if (num3 >= MAX_PLAYGROUPS_PER_SELECT)
+ error("Too much playgroups in select #%d in choice #%d in dialog #%d", j, lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
+
+ for (uint32 z = 0; z < num3; z++) {
+ lpmdDialog->_choice[i]._select[j]._wPlayGroup[z] = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ }
+
+ lpmdDialog->_choice[i]._select[j]._wPlayGroup[num3] = 0;
+ }
+
+ // Mark the last selection
+ lpmdDialog->_choice[i]._select[num2]._dwData = 0;
+ }
+
+ lpmdDialog->_choice[num]._nChoice = 0;
+
+ return lpBuf;
+}
+
+
+/**
+ * Parses an item from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled dialog.
+ * @param lpmiItem Pointer to a structure that will be filled with the
+ * data of the item.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ * @remarks It's necessary that the structure that is passed has been
+ * completely initialized to 0 beforehand.
+ */
+static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) {
+ lpmiItem->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ byte len = *lpBuf;
+ lpBuf++;
+ memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)127, len));
+ lpBuf += len;
+
+ if (len >= MAX_DESCRIBE_SIZE)
+ error("Describe too long in item #%d", lpmiItem->_nObj);
+
+ lpmiItem->_nActions=*lpBuf;
+ lpBuf++;
+
+ // Allocation action
+ if (lpmiItem->_nActions > 0)
+ lpmiItem->_action = (ItemAction *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(struct ItemAction) * (int)lpmiItem->_nActions);
+
+ uint32 curCmd = 0;
+
+ for (uint32 i = 0; i < lpmiItem->_nActions; i++) {
+ lpmiItem->_action[i]._num = *lpBuf;
+ lpBuf++;
+
+ lpmiItem->_action[i]._wParm = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (lpmiItem->_action[i]._num == 0xFF) {
+ lpmiItem->_action[i]._wTime = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ lpmiItem->_action[i]._perc = *lpBuf;
+ lpBuf++;
+ }
+
+
+ if (*lpBuf == 0) {
+ lpBuf++;
+ lpmiItem->_action[i]._when = NULL;
+ } else {
+ lpBuf++;
+ lpBuf = parseExpression(lpBuf,&lpmiItem->_action[i]._when);
+ if (lpBuf == NULL)
+ return NULL;
+ }
+
+ lpmiItem->_action[i]._nCmds=*lpBuf;
+ lpBuf++;
+
+ if (lpmiItem->_action[i]._nCmds >= MAX_COMMANDS_PER_ACTION)
+ error("Too much commands in action #%d in item #%d", lpmiItem->_action[i]._num, lpmiItem->_nObj);
+
+ for (uint32 j = 0; j < lpmiItem->_action[i]._nCmds; j++) {
+ lpmiItem->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+ switch (lpmiItem->_command[curCmd]._type) {
+ case 1: // Call custom function
+ lpmiItem->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmiItem->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case 2: // Variable assign
+ len = *lpBuf;
+ lpBuf++;
+ lpmiItem->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmiItem->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+ memcpy(lpmiItem->_command[curCmd]._lpszVarName, lpBuf, len);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmiItem->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ uint32 kk;
+ for (kk = 0; kk < curCmd; kk++) {
+ if (compareCommands(&lpmiItem->_command[kk], &lpmiItem->_command[curCmd])) {
+ lpmiItem->_action[i]._cmdNum[j] = kk;
+
+ // Free any data allocated for the duplictaed command
+ if (lpmiItem->_command[curCmd]._type == 2) {
+ globalDestroy(lpmiItem->_command[curCmd]._lpszVarName);
+ freeExpression(lpmiItem->_command[curCmd]._expr);
+
+ lpmiItem->_command[curCmd]._lpszVarName = NULL;
+ lpmiItem->_command[curCmd]._expr = 0;
+ lpmiItem->_command[curCmd]._type = 0;
+ }
+ break;
+ }
+ }
+
+ if (kk == curCmd) {
+ lpmiItem->_action[i]._cmdNum[j] = curCmd;
+ curCmd++;
+
+ if (curCmd >= MAX_COMMANDS_PER_ITEM) {
+ error("Too much commands in item #%d", lpmiItem->_nObj);
+ //curCmd=0;
+ }
+ }
+ }
+ }
+
+ lpmiItem->_dwRes = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ return lpBuf;
+}
+
+/**
+ * Frees an item parsed from a prior call to ParseItem
+ *
+ * @param lpmiItem Pointer to an item structure
+ */
+static void freeItem(LpMpalItem lpmiItem) {
+ // Free the actions
+ if (lpmiItem->_action) {
+ for (int i = 0; i < lpmiItem->_nActions; ++i) {
+ if (lpmiItem->_action[i]._when != 0)
+ freeExpression(lpmiItem->_action[i]._when);
+ }
+
+ globalDestroy(lpmiItem->_action);
+ }
+
+ // Free the commands
+ for (int i = 0; i < MAX_COMMANDS_PER_ITEM && (lpmiItem->_command[i]._type); ++i) {
+ if (lpmiItem->_command[i]._type == 2) {
+ // Variable Assign
+ globalDestroy(lpmiItem->_command[i]._lpszVarName);
+ freeExpression(lpmiItem->_command[i]._expr);
+ }
+ }
+}
+
+/**
+ * Parses a location from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled location.
+ * @param lpmiLocation Pointer to a structure that will be filled with the
+ * data of the location.
+ * @returns Pointer to the buffer after the location, or NULL on failure.
+ */
+static const byte *ParseLocation(const byte *lpBuf, LpMpalLocation lpmlLocation) {
+ lpmlLocation->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmlLocation->_dwXlen = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmlLocation->_dwYlen = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmlLocation->_dwPicRes = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ return lpBuf;
+}
+
+
+/****************************************************************************\
+* Exported functions
+\****************************************************************************/
+/**
+ * @defgroup Exported functions
+ */
+//@{
+
+/**
+ * Reads and interprets the MPC file, and create structures for various directives
+ * in the global variables
+ *
+ * @param lpBuf Buffer containing the MPC file data, excluding the header.
+ * @returns True if succeeded OK, false if failure.
+ */
+bool parseMpc(const byte *lpBuf) {
+ byte *lpTemp;
+
+ // 1. Variables
+ if (lpBuf[0] != 'V' || lpBuf[1] != 'A' || lpBuf[2] != 'R' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nVars = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ GLOBALS._hVars = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalVar) * (uint32)GLOBALS._nVars);
+ if (GLOBALS._hVars == NULL)
+ return false;
+
+ GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
+
+ for (uint16 i = 0; i < GLOBALS._nVars; i++) {
+ uint16 wLen = *(const byte *)lpBuf;
+ lpBuf++;
+ memcpy(GLOBALS._lpmvVars->_lpszVarName, lpBuf, MIN(wLen, (uint16)32));
+ lpBuf += wLen;
+ GLOBALS._lpmvVars->_dwVal = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ lpBuf++; // Skip 'ext'
+ GLOBALS._lpmvVars++;
+ }
+
+ globalUnlock(GLOBALS._hVars);
+
+ // 2. Messages
+ if (lpBuf[0] != 'M' || lpBuf[1] != 'S' || lpBuf[2] != 'G' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nMsgs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+#ifdef NEED_LOCK_MSGS
+ GLOBALS._hMsgs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalMsg) * (uint32)GLOBALS._nMsgs);
+ if (GLOBALS._hMsgs == NULL)
+ return false;
+
+ GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+#else
+ GLOBALS._lpmmMsgs=(LPMPALMSG)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MPALMSG) * (uint32)GLOBALS._nMsgs);
+ if (GLOBALS._lpmmMsgs==NULL)
+ return false;
+#endif
+
+ for (uint16 i = 0; i < GLOBALS._nMsgs; i++) {
+ GLOBALS._lpmmMsgs->_wNum = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ uint16 j;
+ for (j = 0; lpBuf[j] != 0;)
+ j += lpBuf[j] + 1;
+
+ GLOBALS._lpmmMsgs->_hText = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, j + 1);
+ lpTemp = (byte *)globalLock(GLOBALS._lpmmMsgs->_hText);
+
+ for (j = 0; lpBuf[j] != 0;) {
+ memcpy(lpTemp, &lpBuf[j + 1], lpBuf[j]);
+ lpTemp += lpBuf[j];
+ *lpTemp ++= '\0';
+ j += lpBuf[j] + 1;
+ }
+
+ lpBuf += j + 1;
+ *lpTemp = '\0';
+
+ globalUnlock(GLOBALS._lpmmMsgs->_hText);
+ GLOBALS._lpmmMsgs++;
+ }
+
+#ifdef NEED_LOCK_MSGS
+ globalUnlock(GLOBALS._hMsgs);
+#endif
+
+ // 3. Objects
+ if (lpBuf[0] != 'O' || lpBuf[1] != 'B' || lpBuf[2] != 'J' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nObjs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Check out the dialogs
+ GLOBALS._nDialogs = 0;
+ GLOBALS._hDialogs = GLOBALS._lpmdDialogs = NULL;
+ if (*((const byte *)lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Dialog", 6) == 0) {
+ GLOBALS._nDialogs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ GLOBALS._hDialogs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nDialogs * sizeof(MpalDialog));
+ if (GLOBALS._hDialogs == NULL)
+ return false;
+
+ GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+
+ for (uint16 i = 0; i < GLOBALS._nDialogs; i++) {
+ if ((lpBuf = parseDialog(lpBuf + 7, &GLOBALS._lpmdDialogs[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hDialogs);
+ }
+
+ // Check the items
+ GLOBALS._nItems = 0;
+ GLOBALS._hItems = GLOBALS._lpmiItems = NULL;
+ if (*(lpBuf + 2) == 4 && strncmp((const char *)lpBuf + 3, "Item", 4) == 0) {
+ GLOBALS._nItems = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory and read them in
+ GLOBALS._hItems = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nItems * sizeof(MpalItem));
+ if (GLOBALS._hItems == NULL)
+ return false;
+
+ GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+
+ for (uint16 i = 0; i < GLOBALS._nItems; i++) {
+ if ((lpBuf = parseItem(lpBuf + 5, &GLOBALS._lpmiItems[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hItems);
+ }
+
+ // Check the locations
+ GLOBALS._nLocations = 0;
+ GLOBALS._hLocations = GLOBALS._lpmlLocations = NULL;
+ if (*(lpBuf + 2) == 8 && strncmp((const char *)lpBuf + 3, "Location", 8) == 0) {
+ GLOBALS._nLocations = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory and read them in
+ GLOBALS._hLocations = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nLocations * sizeof(MpalLocation));
+ if (GLOBALS._hLocations == NULL)
+ return false;
+
+ GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
+
+ for (uint16 i = 0; i < GLOBALS._nLocations; i++) {
+ if ((lpBuf = ParseLocation(lpBuf + 9, &GLOBALS._lpmlLocations[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hLocations);
+ }
+
+ // Check the scripts
+ GLOBALS._nScripts = 0;
+ GLOBALS._hScripts = GLOBALS._lpmsScripts = NULL;
+ if (*(lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Script", 6) == 0) {
+ GLOBALS._nScripts = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory
+ GLOBALS._hScripts = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nScripts * sizeof(MpalScript));
+ if (GLOBALS._hScripts == NULL)
+ return false;
+
+ GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+
+ for (uint16 i = 0; i < GLOBALS._nScripts; i++) {
+ if ((lpBuf = ParseScript(lpBuf + 7, &GLOBALS._lpmsScripts[i])) == NULL)
+ return false;
+
+ // Sort the various moments of the script
+ //qsort(
+ //GLOBALS.lpmsScripts[i].Moment,
+ //GLOBALS.lpmsScripts[i].nMoments,
+ //sizeof(GLOBALS.lpmsScripts[i].Moment[0]),
+ //(int (*)(const void *, const void *))CompareMoments
+ //);
+ }
+
+ globalUnlock(GLOBALS._hScripts);
+ }
+
+ if (lpBuf[0] != 'E' || lpBuf[1] != 'N' || lpBuf[2] != 'D' || lpBuf[3] != '0')
+ return false;
+
+ return true;
+}
+
+/**
+ * Free the given dialog
+ */
+static void freeDialog(LpMpalDialog lpmdDialog) {
+ // Free the periods
+ for (int i = 0; i < MAX_PERIODS_PER_DIALOG && (lpmdDialog->_periods[i]); ++i)
+ globalFree(lpmdDialog->_periods[i]);
+
+ for (int i = 0; i < MAX_COMMANDS_PER_DIALOG && (lpmdDialog->_command[i]._type); i++) {
+ if (lpmdDialog->_command[i]._type == 2) {
+ // Variable assign
+ globalDestroy(lpmdDialog->_command[i]._lpszVarName);
+ freeExpression(lpmdDialog->_command[i]._expr);
+ }
+ }
+
+ // Free the choices
+ for (int i = 0; i < MAX_CHOICES_PER_DIALOG; ++i) {
+ for (int j = 0; j < MAX_SELECTS_PER_CHOICE; j++) {
+ if (lpmdDialog->_choice[i]._select[j]._when)
+ freeExpression(lpmdDialog->_choice[i]._select[j]._when);
+ }
+ }
+}
+
+/**
+ * Frees any data allocated from the parsing of the MPC file
+ */
+void freeMpc() {
+ // Free variables
+ globalFree(GLOBALS._hVars);
+
+ // Free messages
+ LpMpalMsg lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+ for (int i = 0; i < GLOBALS._nMsgs; i++, ++lpmmMsgs)
+ globalFree(lpmmMsgs->_hText);
+
+ globalUnlock(GLOBALS._hMsgs);
+ globalFree(GLOBALS._hMsgs);
+
+ // Free objects
+ if (GLOBALS._hDialogs) {
+ LpMpalDialog lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+
+ for (int i = 0; i < GLOBALS._nDialogs; i++, ++lpmdDialogs)
+ freeDialog(lpmdDialogs);
+
+ globalFree(GLOBALS._hDialogs);
+ }
+
+ // Free items
+ if (GLOBALS._hItems) {
+ LpMpalItem lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+
+ for (int i = 0; i < GLOBALS._nItems; ++i, ++lpmiItems)
+ freeItem(lpmiItems);
+
+ globalUnlock(GLOBALS._hItems);
+ globalFree(GLOBALS._hItems);
+ }
+
+ // Free the locations
+ if (GLOBALS._hLocations) {
+ globalFree(GLOBALS._hLocations);
+ }
+
+ // Free the scripts
+ if (GLOBALS._hScripts) {
+ LpMpalScript lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+
+ for (int i = 0; i < GLOBALS._nScripts; ++i, ++lpmsScripts) {
+ FreeScript(lpmsScripts);
+ }
+
+ globalUnlock(GLOBALS._hScripts);
+ }
+}
+
+//@}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/loadmpc.h b/engines/tony/mpal/loadmpc.h
new file mode 100644
index 0000000000..20956288aa
--- /dev/null
+++ b/engines/tony/mpal/loadmpc.h
@@ -0,0 +1,59 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef __LOADMPC_H
+#define __LOADMPC_H
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Function prototypes
+\****************************************************************************/
+
+/**
+ * Reads and interprets the MPC file, and create structures for various directives
+ * in the global variables
+ *
+ * @param lpBuf Buffer containing the MPC file data, excluding the header.
+ * @returns True if succeeded OK, false if failure.
+ */
+bool parseMpc(const byte *lpBuf);
+
+/**
+ * Frees any data allocated from the parsing of the MPC file
+ */
+void freeMpc();
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/lzo.cpp b/engines/tony/mpal/lzo.cpp
new file mode 100644
index 0000000000..3d0751a5ca
--- /dev/null
+++ b/engines/tony/mpal/lzo.cpp
@@ -0,0 +1,511 @@
+/* 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.
+ *
+ *
+ */
+/* minilzo.c -- mini subset of the LZO real-time data compression library
+
+ This file is part of the LZO real-time data compression library.
+
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library 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.
+
+ The LZO library 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 the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/lzo/
+ */
+
+#include "lzo.h"
+#include "common/textconsole.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+#define pd(a, b) ((uint32) ((a) - (b)))
+
+#define TEST_IP (ip < ip_end)
+#define TEST_OP 1
+#define NEED_IP(x) ((void) 0)
+#define NEED_OP(x) ((void) 0)
+#define TEST_LB(m_pos) ((void) 0)
+
+#define M2_MAX_OFFSET 0x0800
+#define LZO1X
+
+/**
+ * Decompresses an LZO compressed resource
+ */
+int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) {
+ register byte *op;
+ register const byte *ip;
+ register uint32 t = 0;
+#if defined(COPY_DICT)
+ uint32 m_off;
+ const byte *dict_end;
+#else
+ register const byte *m_pos;
+#endif
+
+ const byte * const ip_end = in + in_len;
+#if defined(HAVE_ANY_OP)
+ byte * const op_end = out + *out_len;
+#endif
+#if defined(LZO1Z)
+ uint32 last_m_off = 0;
+#endif
+
+#if defined(COPY_DICT)
+ if (dict)
+ {
+ if (dict_len > M4_MAX_OFFSET)
+ {
+ dict += dict_len - M4_MAX_OFFSET;
+ dict_len = M4_MAX_OFFSET;
+ }
+ dict_end = dict + dict_len;
+ }
+ else
+ {
+ dict_len = 0;
+ dict_end = NULL;
+ }
+#endif
+
+ *out_len = 0;
+
+ op = out;
+ ip = in;
+
+ if (*ip > 17)
+ {
+ t = *ip++ - 17;
+ if (t < 4)
+ goto match_next;
+ assert(t > 0); NEED_OP(t); NEED_IP(t+1);
+ do *op++ = *ip++; while (--t > 0);
+ goto first_literal_run;
+ }
+
+ while (TEST_IP && TEST_OP)
+ {
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 15 + *ip++;
+ }
+ assert(t > 0); NEED_OP(t+3); NEED_IP(t+4);
+#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4)
+ t += 3;
+ if (t >= 8) do
+ {
+ UA_COPY64(op, ip);
+ op += 8; ip += 8; t -= 8;
+ } while (t >= 8);
+ if (t >= 4)
+ {
+ UA_COPY32(op, ip);
+ op += 4; ip += 4; t -= 4;
+ }
+ if (t > 0)
+ {
+ *op++ = *ip++;
+ if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } }
+ }
+#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+ if (PTR_ALIGNED2_4(op, ip))
+ {
+#endif
+ UA_COPY32(op, ip);
+ op += 4; ip += 4;
+ if (--t > 0)
+ {
+ if (t >= 4)
+ {
+ do {
+ UA_COPY32(op, ip);
+ op += 4; ip += 4; t -= 4;
+ } while (t >= 4);
+ if (t > 0) do *op++ = *ip++; while (--t > 0);
+ }
+ else
+ do *op++ = *ip++; while (--t > 0);
+ }
+#if !defined(LZO_UNALIGNED_OK_4)
+ }
+ else
+#endif
+#endif
+#if !defined(LZO_UNALIGNED_OK_4) && !defined(LZO_UNALIGNED_OK_8)
+ {
+ *op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
+ do *op++ = *ip++; while (--t > 0);
+ }
+#endif
+
+first_literal_run:
+
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+#else
+ m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2);
+#endif
+ NEED_OP(3);
+ t = 3; COPY_DICT(t, m_off)
+#else
+#if defined(LZO1Z)
+ t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+ m_pos = op - t;
+ last_m_off = t;
+#else
+ m_pos = op - (1 + M2_MAX_OFFSET);
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+#endif
+ TEST_LB(m_pos); NEED_OP(3);
+ *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;
+#endif
+ goto match_done;
+
+ do {
+match:
+ if (t >= 64)
+ {
+#if defined(COPY_DICT)
+#if defined(LZO1X)
+ m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3);
+ t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+ m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2);
+ t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+ m_off = t & 0x1f;
+ if (m_off >= 0x1c)
+ m_off = last_m_off;
+ else
+ {
+ m_off = 1 + (m_off << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+ }
+ t = (t >> 5) - 1;
+#endif
+#else
+#if defined(LZO1X)
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 7;
+ m_pos -= *ip++ << 3;
+ t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 3;
+ m_pos -= *ip++ << 2;
+ t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+ {
+ uint32 off = t & 0x1f;
+ m_pos = op;
+ if (off >= 0x1c)
+ {
+ assert(last_m_off > 0);
+ m_pos -= last_m_off;
+ }
+ else
+ {
+ off = 1 + (off << 6) + (*ip++ >> 2);
+ m_pos -= off;
+ last_m_off = off;
+ }
+ }
+ t = (t >> 5) - 1;
+#endif
+ TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+ goto copy_match;
+#endif
+ }
+ else if (t >= 32)
+ {
+ t &= 31;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 31 + *ip++;
+ }
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+ last_m_off = m_off;
+#else
+ m_off = 1 + (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#else
+#if defined(LZO1Z)
+ {
+ uint32 off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+ m_pos = op - off;
+ last_m_off = off;
+ }
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+ m_pos = op - 1;
+ m_pos -= UA_GET16(ip) >> 2;
+#else
+ m_pos = op - 1;
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#endif
+ ip += 2;
+ }
+ else if (t >= 16)
+ {
+#if defined(COPY_DICT)
+ m_off = (t & 8) << 11;
+#else
+ m_pos = op;
+ m_pos -= (t & 8) << 11;
+#endif
+ t &= 7;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 7 + *ip++;
+ }
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off += (ip[0] << 6) + (ip[1] >> 2);
+#else
+ m_off += (ip[0] >> 2) + (ip[1] << 6);
+#endif
+ ip += 2;
+ if (m_off == 0)
+ goto eof_found;
+ m_off += 0x4000;
+#if defined(LZO1Z)
+ last_m_off = m_off;
+#endif
+#else
+#if defined(LZO1Z)
+ m_pos -= (ip[0] << 6) + (ip[1] >> 2);
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+ m_pos -= UA_GET16(ip) >> 2;
+#else
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+ ip += 2;
+ if (m_pos == op)
+ goto eof_found;
+ m_pos -= 0x4000;
+#if defined(LZO1Z)
+ last_m_off = pd((const byte *)op, m_pos);
+#endif
+#endif
+ }
+ else
+ {
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = 1 + (t << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+#else
+ m_off = 1 + (t >> 2) + (*ip++ << 2);
+#endif
+ NEED_OP(2);
+ t = 2; COPY_DICT(t, m_off)
+#else
+#if defined(LZO1Z)
+ t = 1 + (t << 6) + (*ip++ >> 2);
+ m_pos = op - t;
+ last_m_off = t;
+#else
+ m_pos = op - 1;
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+#endif
+ TEST_LB(m_pos); NEED_OP(2);
+ *op++ = *m_pos++; *op++ = *m_pos;
+#endif
+ goto match_done;
+ }
+
+#if defined(COPY_DICT)
+
+ NEED_OP(t+3-1);
+ t += 3-1; COPY_DICT(t, m_off)
+
+#else
+
+ TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4)
+ if (op - m_pos >= 8)
+ {
+ t += (3 - 1);
+ if (t >= 8) do
+ {
+ UA_COPY64(op, m_pos);
+ op += 8; m_pos += 8; t -= 8;
+ } while (t >= 8);
+ if (t >= 4)
+ {
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4;
+ }
+ if (t > 0)
+ {
+ *op++ = m_pos[0];
+ if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } }
+ }
+ }
+ else
+#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+ if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op, m_pos))
+ {
+ assert((op - m_pos) >= 4);
+#else
+ if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4)
+ {
+#endif
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4 - (3 - 1);
+ do {
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4;
+ } while (t >= 4);
+ if (t > 0) do *op++ = *m_pos++; while (--t > 0);
+ }
+ else
+#endif
+ {
+copy_match:
+ *op++ = *m_pos++; *op++ = *m_pos++;
+ do *op++ = *m_pos++; while (--t > 0);
+ }
+
+#endif
+
+match_done:
+#if defined(LZO1Z)
+ t = ip[-1] & 3;
+#else
+ t = ip[-2] & 3;
+#endif
+ if (t == 0)
+ break;
+
+match_next:
+ assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+1);
+#if 0
+ do *op++ = *ip++; while (--t > 0);
+#else
+ *op++ = *ip++;
+ if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } }
+#endif
+ t = *ip++;
+ } while (TEST_IP && TEST_OP);
+ }
+
+#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
+ *out_len = pd(op, out);
+ return LZO_E_EOF_NOT_FOUND;
+#endif
+
+eof_found:
+ assert(t == 1);
+ *out_len = pd(op, out);
+ return (ip == ip_end ? LZO_E_OK :
+ (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+#if defined(HAVE_NEED_IP)
+input_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_INPUT_OVERRUN;
+#endif
+
+#if defined(HAVE_NEED_OP)
+output_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_OUTPUT_OVERRUN;
+#endif
+
+#if defined(LZO_TEST_OVERRUN_LOOKBEHIND)
+lookbehind_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_LOOKBEHIND_OVERRUN;
+#endif
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/lzo.h b/engines/tony/mpal/lzo.h
new file mode 100644
index 0000000000..ebb1c4b516
--- /dev/null
+++ b/engines/tony/mpal/lzo.h
@@ -0,0 +1,111 @@
+/* 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.
+ *
+ *
+ */
+/* minilzo.c -- mini subset of the LZO real-time data compression library
+
+ This file is part of the LZO real-time data compression library.
+
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library 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.
+
+ The LZO library 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 the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/lzo/
+ */
+
+#ifndef TONY_MPAL_LZO_H
+#define TONY_MPAL_LZO_H
+
+#include "common/scummsys.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/* Error codes for the compression/decompression functions. Negative
+ * values are errors, positive values will be used for special but
+ * normal events.
+ */
+#define LZO_E_OK 0
+#define LZO_E_ERROR (-1)
+#define LZO_E_OUT_OF_MEMORY (-2) /* [lzo_alloc_func_t failure] */
+#define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */
+#define LZO_E_INPUT_OVERRUN (-4)
+#define LZO_E_OUTPUT_OVERRUN (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN (-6)
+#define LZO_E_EOF_NOT_FOUND (-7)
+#define LZO_E_INPUT_NOT_CONSUMED (-8)
+#define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */
+#define LZO_E_INVALID_ARGUMENT (-10)
+
+#define LZO1X_999_MEM_COMPRESS ((uint32) (14 * 16384L * sizeof(uint16)))
+
+/**
+ * Decompresses an LZO compressed resource
+ */
+int lzo1x_decompress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len);
+
+/**
+ * Comrpess a data block into an LZO stream
+ */
+int lzo1x_1_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem);
+
+/**
+ * better compression ratio at the cost of more memory and time
+ */
+int lzo1x_999_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif /* already included */
diff --git a/engines/tony/mpal/memory.cpp b/engines/tony/mpal/memory.cpp
new file mode 100644
index 0000000000..428c07b3b7
--- /dev/null
+++ b/engines/tony/mpal/memory.cpp
@@ -0,0 +1,129 @@
+/* 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/algorithm.h"
+#include "common/textconsole.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* MemoryManager methods
+\****************************************************************************/
+
+const uint32 BLOCK_ID = 0x12345678;
+
+/**
+ * Allocates a new memory block
+ * @return Returns a MemoryItem instance for the new block
+ */
+MpalHandle MemoryManager::allocate(uint32 size, uint flags) {
+ MemoryItem *newItem = (MemoryItem *)malloc(sizeof(MemoryItem) + size);
+ newItem->_id = BLOCK_ID;
+ newItem->_size = size;
+ newItem->_lockCount = 0;
+
+ // If requested, clear the allocated data block
+ if ((flags & GMEM_ZEROINIT) != 0) {
+ byte *dataP = newItem->_data;
+ Common::fill(dataP, dataP + size, 0);
+ }
+
+ return (MpalHandle)newItem;
+}
+
+/**
+ * Allocates a new memory block and returns its data pointer
+ * @return Data pointer to allocated block
+ */
+void *MemoryManager::alloc(uint32 size, uint flags) {
+ MemoryItem *item = (MemoryItem *)allocate(size, flags);
+ ++item->_lockCount;
+ return &item->_data[0];
+}
+
+#define OFFSETOF(type, field) ((unsigned long) &(((type *) 0)->field))
+
+/**
+ * Returns a reference to the MemoryItem for a gien byte pointer
+ * @param block Byte pointer
+ */
+MemoryItem *MemoryManager::getItem(MpalHandle handle) {
+ MemoryItem *rec = (MemoryItem *)((byte *)handle - OFFSETOF(MemoryItem, _data));
+ assert(rec->_id == BLOCK_ID);
+ return rec;
+}
+
+/**
+ * Returns a size of a memory block given its pointer
+ */
+uint32 MemoryManager::getSize(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ return item->_size;
+}
+
+/**
+ * Erases a given item
+ */
+void MemoryManager::freeBlock(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ free(item);
+}
+
+/**
+ * Erases a given item
+ */
+void MemoryManager::destroyItem(MpalHandle handle) {
+ MemoryItem *item = getItem(handle);
+ assert(item->_id == BLOCK_ID);
+ free(item);
+}
+
+/**
+ * Locks an item for access
+ */
+byte *MemoryManager::lockItem(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ ++item->_lockCount;
+ return &item->_data[0];
+}
+
+/**
+ * Unlocks a locked item
+ */
+void MemoryManager::unlockItem(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ assert(item->_lockCount > 0);
+ --item->_lockCount;
+}
+
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/memory.h b/engines/tony/mpal/memory.h
new file mode 100644
index 0000000000..ba7865938f
--- /dev/null
+++ b/engines/tony/mpal/memory.h
@@ -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.
+ *
+ *
+ */
+
+#ifndef TONY_MPAL_MEMORY
+#define TONY_MPAL_MEMORY
+
+#include "common/scummsys.h"
+#include "common/list.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+typedef void *MpalHandle;
+
+struct MemoryItem {
+ uint32 _id;
+ uint32 _size;
+ int _lockCount;
+ byte _data[1];
+
+ // Casting for access to data
+ operator void *() { return &_data[0]; }
+};
+
+class MemoryManager {
+private:
+ static MemoryItem *getItem(MpalHandle handle);
+public:
+ static MpalHandle allocate(uint32 size, uint flags);
+ static void *alloc(uint32 size, uint flags);
+ static void freeBlock(MpalHandle handle);
+ static void destroyItem(MpalHandle handle);
+ static uint32 getSize(MpalHandle handle);
+ static byte *lockItem(MpalHandle handle);
+ static void unlockItem(MpalHandle handle);
+};
+
+// defines
+#define globalAlloc(flags, size) MemoryManager::alloc(size, flags)
+#define globalAllocate(flags, size) MemoryManager::allocate(size, flags)
+#define globalFree(handle) MemoryManager::freeBlock(handle)
+#define globalDestroy(handle) MemoryManager::destroyItem(handle)
+#define globalLock(handle) MemoryManager::lockItem(handle)
+#define globalUnlock(handle) MemoryManager::unlockItem(handle)
+#define globalSize(handle) MemoryManager::getSize(handle)
+
+#define GMEM_FIXED 1
+#define GMEM_MOVEABLE 2
+#define GMEM_ZEROINIT 4
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp
new file mode 100644
index 0000000000..533a4d22b9
--- /dev/null
+++ b/engines/tony/mpal/mpal.cpp
@@ -0,0 +1,2102 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "tony/tony.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpaldll.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+#define GETARG(type) va_arg(v, type)
+
+/****************************************************************************\
+* Copyright
+\****************************************************************************/
+
+const char *mpalCopyright =
+ "\n\nMPAL - MultiPurpose Adventure Language for Windows 95\n"
+ "Copyright 1997-98 Giovanni Bajo and Luca Giusti\n"
+ "ALL RIGHTS RESERVED\n"
+ "\n"
+ "\n";
+
+/****************************************************************************\
+* Internal functions
+\****************************************************************************/
+
+/**
+ * Locks the variables for access
+ */
+void lockVar() {
+ GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
+}
+
+/**
+ * Unlocks variables after use
+ */
+void unlockVar() {
+ globalUnlock(GLOBALS._hVars);
+}
+
+/**
+ * Locks the messages for access
+ */
+static void LockMsg() {
+#ifdef NEED_LOCK_MSGS
+ GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+#endif
+}
+
+/**
+ * Unlocks the messages after use
+ */
+static void UnlockMsg() {
+#ifdef NEED_LOCK_MSGS
+ globalUnlock(GLOBALS._hMsgs);
+#endif
+}
+
+/**
+ * Locks the dialogs for access
+ */
+static void lockDialogs() {
+ GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+}
+
+/**
+ * Unlocks the dialogs after use
+ */
+static void unlockDialogs() {
+ globalUnlock(GLOBALS._hDialogs);
+}
+
+/**
+ * Locks the location data structures for access
+ */
+static void lockLocations() {
+ GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
+}
+
+/**
+ * Unlocks the location structures after use
+ */
+static void unlockLocations() {
+ globalUnlock(GLOBALS._hLocations);
+}
+
+/**
+ * Locks the items structures for use
+ */
+static void lockItems() {
+ GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+}
+
+/**
+ * Unlocks the items structures after use
+ */
+static void unlockItems() {
+ globalUnlock(GLOBALS._hItems);
+}
+
+/**
+ * Locks the script data structures for use
+ */
+static void LockScripts() {
+ GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+}
+
+/**
+ * Unlocks the script data structures after use
+ */
+static void unlockScripts() {
+ globalUnlock(GLOBALS._hScripts);
+}
+
+/**
+ * Returns the current value of a global variable
+ *
+ * @param lpszVarName Name of the variable
+ * @returns Current value
+ * @remarks Before using this method, you must call lockVar() to
+ * lock the global variablves for use. Then afterwards, you will
+ * need to remember to call UnlockVar()
+ */
+int32 varGetValue(const char *lpszVarName) {
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ for (int i = 0; i < GLOBALS._nVars; v++, i++)
+ if (strcmp(lpszVarName, v->_lpszVarName) == 0)
+ return v->_dwVal;
+
+ GLOBALS._mpalError = 1;
+ return 0;
+}
+
+/**
+ * Sets the value of a MPAL global variable
+ * @param lpszVarName Name of the variable
+ * @param val Value to set
+ */
+void varSetValue(const char *lpszVarName, int32 val) {
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ for (uint i = 0; i < GLOBALS._nVars; v++, i++)
+ if (strcmp(lpszVarName, v->_lpszVarName) == 0) {
+ v->_dwVal = val;
+ if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Pattern.", 8) == 0) {
+ i = 0;
+ sscanf(v->_lpszVarName, "Pattern.%u", &i);
+ GLOBALS._lpiifCustom(i, val, -1);
+ } else if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Status.", 7) == 0) {
+ i = 0;
+ sscanf(v->_lpszVarName,"Status.%u", &i);
+ GLOBALS._lpiifCustom(i, -1, val);
+ }
+ return;
+ }
+
+ GLOBALS._mpalError = 1;
+ return;
+}
+
+/**
+ * Find the index of a location within the location array. Remember to call LockLoc() beforehand.
+ *
+ * @param nLoc Location number to search for
+ * @returns Index, or -1 if the location is not present
+ * @remarks This function requires the location list to have
+ * first been locked with a call to LockLoc().
+ */
+static int locGetOrderFromNum(uint32 nLoc) {
+ LpMpalLocation loc = GLOBALS._lpmlLocations;
+
+ for (int i = 0; i < GLOBALS._nLocations; i++, loc++)
+ if (loc->_nObj == nLoc)
+ return i;
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a message within the messages array
+ * @param nMsg Message number to search for
+ * @returns Index, or -1 if the message is not present
+ * @remarks This function requires the message list to have
+ * first been locked with a call to LockMsg()
+ */
+static int msgGetOrderFromNum(uint32 nMsg) {
+ LpMpalMsg msg = GLOBALS._lpmmMsgs;
+
+ for (int i = 0; i < GLOBALS._nMsgs; i++, msg++) {
+ if (msg->_wNum == nMsg)
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * Find the index of an item within the items array
+ * @param nItem Item number to search for
+ * @returns Index, or -1 if the item is not present
+ * @remarks This function requires the item list to have
+ * first been locked with a call to LockItems()
+ */
+static int itemGetOrderFromNum(uint32 nItem) {
+ LpMpalItem item = GLOBALS._lpmiItems;
+
+ for (int i = 0; i < GLOBALS._nItems; i++, item++) {
+ if (item->_nObj == nItem)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a script within the scripts array
+ * @param nScript Script number to search for
+ * @returns Index, or -1 if the script is not present
+ * @remarks This function requires the script list to have
+ * first been locked with a call to LockScripts()
+ */
+static int scriptGetOrderFromNum(uint32 nScript) {
+ LpMpalScript script = GLOBALS._lpmsScripts;
+
+ for (int i = 0; i < GLOBALS._nScripts; i++, script++) {
+ if (script->_nObj == nScript)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a dialog within the dialogs array
+ * @param nDialog Dialog number to search for
+ * @returns Index, or -1 if the dialog is not present
+ * @remarks This function requires the dialog list to have
+ * first been locked with a call to LockDialogs()
+ */
+static int dialogGetOrderFromNum(uint32 nDialog) {
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs;
+
+ for (int i = 0; i < GLOBALS._nDialogs; i++, dialog++) {
+ if (dialog->_nObj == nDialog)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Duplicates a message
+ * @param nMsgOrd Index of the message inside the messages array
+ * @returns Pointer to the duplicated message.
+ * @remarks Remember to free the duplicated message when done with it.
+ */
+static char *DuplicateMessage(uint32 nMsgOrd) {
+ const char *origmsg;
+ char *clonemsg;
+
+ if (nMsgOrd == (uint32)-1)
+ return NULL;
+
+ origmsg = (const char *)globalLock(GLOBALS._lpmmMsgs[nMsgOrd]._hText);
+
+ int j = 0;
+ while (origmsg[j] != '\0' || origmsg[j + 1] != '\0')
+ j++;
+ j += 2;
+
+ clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, j);
+ if (clonemsg == NULL)
+ return NULL;
+
+ memcpy(clonemsg, origmsg, j);
+ globalUnlock(GLOBALS._lpmmMsgs[nMsgOrd]._hText);
+
+ return clonemsg;
+}
+
+
+/**
+ * Duplicate a sentence of a dialog
+ * @param nDlgOrd Index of the dialog in the dialogs array
+ * @param nPeriod Sentence number to be duplicated.
+ * @returns Pointer to the duplicated phrase. Remember to free it
+ * when done with it.
+ */
+static char *duplicateDialogPeriod(uint32 nPeriod) {
+ const char *origmsg;
+ char *clonemsg;
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ for (int j = 0; dialog->_periods[j] != NULL; j++) {
+ if (dialog->_periodNums[j] == nPeriod) {
+ // Found the phrase, it should be duplicated
+ origmsg = (const char *)globalLock(dialog->_periods[j]);
+
+ // Calculate the length and allocate memory
+ int i = 0;
+ while (origmsg[i] != '\0')
+ i++;
+
+ clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, i + 1);
+ if (clonemsg == NULL)
+ return NULL;
+
+ memcpy(clonemsg, origmsg, i);
+
+ globalUnlock(dialog->_periods[j]);
+
+ return clonemsg;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Load a resource from the MPR file
+ *
+ * @param dwId ID of the resource to load
+ * @returns Handle to the loaded resource
+ */
+MpalHandle resLoad(uint32 dwId) {
+ MpalHandle h;
+ char head[4];
+ uint32 nBytesRead;
+ uint32 nSizeComp, nSizeDecomp;
+ byte *temp, *buf;
+
+ for (int i = 0; i < GLOBALS._nResources; i++)
+ if (GLOBALS._lpResources[i * 2] == dwId) {
+ GLOBALS._hMpr.seek(GLOBALS._lpResources[i * 2 + 1]);
+ nBytesRead = GLOBALS._hMpr.read(head, 4);
+ if (nBytesRead != 4)
+ return NULL;
+ if (head[0] != 'R' || head[1] != 'E' || head[2] != 'S' || head[3] != 'D')
+ return NULL;
+
+ nSizeDecomp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return NULL;
+
+ nSizeComp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return NULL;
+
+ h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, nSizeDecomp + (nSizeDecomp / 1024) * 16);
+ buf = (byte *)globalLock(h);
+ temp = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, nSizeComp);
+
+ nBytesRead = GLOBALS._hMpr.read(temp, nSizeComp);
+ if (nBytesRead != nSizeComp)
+ return NULL;
+
+ lzo1x_decompress(temp, nSizeComp, buf, &nBytesRead);
+ if (nBytesRead != nSizeDecomp)
+ return NULL;
+
+ globalDestroy(temp);
+ globalUnlock(h);
+ return h;
+ }
+
+ return NULL;
+}
+
+static uint32 *getSelectList(uint32 i) {
+ uint32 *sl;
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Count how many are active selects
+ int num = 0;
+ for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._curActive)
+ num++;
+ }
+
+ // If there are 0, it's a mistake
+ if (num == 0)
+ return NULL;
+
+ sl = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1));
+ if (sl == NULL)
+ return NULL;
+
+ // Copy all the data inside the active select list
+ int k = 0;
+ for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._curActive)
+ sl[k++] = dialog->_choice[i]._select[j]._dwData;
+ }
+
+ sl[k] = (uint32)NULL;
+ return sl;
+}
+
+static uint32 *GetItemList(uint32 nLoc) {
+ uint32 *il;
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ uint32 num = 0;
+ for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) {
+ if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc)
+ num++;
+ }
+
+ il = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1));
+ if (il == NULL)
+ return NULL;
+
+ v = GLOBALS._lpmvVars;
+ uint32 j = 0;
+ for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) {
+ if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc) {
+ sscanf(v->_lpszVarName, "Location.%u", &il[j]);
+ j++;
+ }
+ }
+
+ il[j] = (uint32)NULL;
+ return il;
+}
+
+static LpItem getItemData(uint32 nOrdItem) {
+ LpMpalItem curitem = GLOBALS._lpmiItems + nOrdItem;
+ LpItem ret;
+ MpalHandle hDat;
+ char *dat;
+ char *patlength;
+
+ // Zeroing out the allocated memory is required!!!
+ ret = (LpItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(Item));
+ if (ret == NULL)
+ return NULL;
+ ret->_speed = 150;
+
+ hDat = resLoad(curitem->_dwRes);
+ dat = (char *)globalLock(hDat);
+
+ if (dat[0] == 'D' && dat[1] == 'A' && dat[2] == 'T') {
+ int i = dat[3]; // For version 1.0!!
+ dat += 4;
+
+ if (i >= 0x10) { // From 1.0, there's a destination point for each object
+ ret->_destX = (int16)READ_LE_UINT16(dat);
+ ret->_destY = (int16)READ_LE_UINT16(dat + 2);
+ dat += 4;
+ }
+
+ if (i >= 0x11) { // From 1.1, there's animation speed
+ ret->_speed = READ_LE_UINT16(dat);
+ dat += 2;
+ } else
+ ret->_speed = 150;
+ }
+
+ ret->_numframe = *dat++;
+ ret->_numpattern = *dat++;
+ ret->_destZ = *dat++;
+
+ // Upload the left & top co-ordinates of each frame
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_frameslocations[i].left = (int16)READ_LE_UINT16(dat);
+ ret->_frameslocations[i].top = (int16)READ_LE_UINT16(dat + 2);
+ dat += 4;
+ }
+
+ // Upload the size of each frame and calculate the right & bottom
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_frameslocations[i].right = (int16)READ_LE_UINT16(dat) + ret->_frameslocations[i].left;
+ ret->_frameslocations[i].bottom = (int16)READ_LE_UINT16(dat + 2) + ret->_frameslocations[i].top;
+ dat += 4;
+ }
+
+ // Upload the bounding boxes of each frame
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_bbox[i].left = (int16)READ_LE_UINT16(dat);
+ ret->_bbox[i].top = (int16)READ_LE_UINT16(dat + 2);
+ ret->_bbox[i].right = (int16)READ_LE_UINT16(dat + 4);
+ ret->_bbox[i].bottom = (int16)READ_LE_UINT16(dat + 6);
+ dat += 8;
+ }
+
+ // Load the animation pattern
+ patlength = dat;
+ dat += ret->_numpattern;
+
+ for (int i = 1; i < ret->_numpattern; i++) {
+ for (int j = 0; j < patlength[i]; j++)
+ ret->_pattern[i][j] = dat[j];
+ ret->_pattern[i][(int)patlength[i]] = 255; // Terminate pattern
+ dat += patlength[i];
+ }
+
+ // Upload the individual frames of animations
+ for (int i = 1; i < ret->_numframe; i++) {
+ uint32 dim = (uint32)(ret->_frameslocations[i].right - ret->_frameslocations[i].left) *
+ (uint32)(ret->_frameslocations[i].bottom - ret->_frameslocations[i].top);
+ ret->_frames[i] = (char *)globalAlloc(GMEM_FIXED, dim);
+
+ if (ret->_frames[i] == NULL)
+ return NULL;
+ memcpy(ret->_frames[i], dat, dim);
+ dat += dim;
+ }
+
+ // Check if we've got to the end of the file
+ int i = READ_LE_UINT16(dat);
+ if (i != 0xABCD)
+ return NULL;
+
+ globalUnlock(hDat);
+ globalFree(hDat);
+
+ return ret;
+}
+
+
+/**
+ * Thread that calls a custom function. It is used in scripts, so that each script
+ * function is executed without delaying the others.
+ *
+ * @param param pointer to a pointer to the structure that defines the call.
+ * @remarks The passed structure is freed when the process finishes.
+ */
+void CustomThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ LpCfCall p;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->p = *(LpCfCall *)param;
+
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->p->_nCf], _ctx->p->_arg1, _ctx->p->_arg2, _ctx->p->_arg3, _ctx->p->_arg4);
+
+ globalFree(_ctx->p);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Main process for running a script.
+ *
+ * @param param Pointer to a pointer to a structure containing the script data.
+ * @remarks The passed structure is freed when the process finishes.
+ */
+void ScriptThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ uint i, j, k;
+ uint32 dwStartTime;
+ uint32 dwCurTime;
+ uint32 dwId;
+ int numHandles;
+ LpCfCall p;
+ CORO_END_CONTEXT(_ctx);
+
+ static uint32 cfHandles[MAX_COMMANDS_PER_MOMENT];
+ LpMpalScript s = *(const LpMpalScript *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->dwStartTime = g_vm->getTime();
+ _ctx->numHandles = 0;
+
+// debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Moments: %u\n", s->_nMoments);
+ for (_ctx->i = 0; _ctx->i < s->_nMoments; _ctx->i++) {
+ // Sleep for the required time
+ if (s->_moment[_ctx->i]._dwTime == -1) {
+ CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, _ctx->numHandles, cfHandles, true, CORO_INFINITE);
+ _ctx->dwStartTime = g_vm->getTime();
+ } else {
+ _ctx->dwCurTime = g_vm->getTime();
+ if (_ctx->dwCurTime < _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100)) {
+ // debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Sleeping %lums\n",_ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime*100) - _ctx->dwCurTime);
+ CORO_INVOKE_1(CoroScheduler.sleep, _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100) - _ctx->dwCurTime);
+ }
+ }
+
+ _ctx->numHandles = 0;
+ for (_ctx->j = 0; _ctx->j < s->_moment[_ctx->i]._nCmds; _ctx->j++) {
+ _ctx->k = s->_moment[_ctx->i]._cmdNum[_ctx->j];
+
+ if (s->_command[_ctx->k]._type == 1) {
+ _ctx->p = (LpCfCall)globalAlloc(GMEM_FIXED, sizeof(CfCall));
+ if (_ctx->p == NULL) {
+ GLOBALS._mpalError = 1;
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ _ctx->p->_nCf = s->_command[_ctx->k]._nCf;
+ _ctx->p->_arg1 = s->_command[_ctx->k]._arg1;
+ _ctx->p->_arg2 = s->_command[_ctx->k]._arg2;
+ _ctx->p->_arg3 = s->_command[_ctx->k]._arg3;
+ _ctx->p->_arg4 = s->_command[_ctx->k]._arg4;
+
+ // !!! New process management
+ if ((cfHandles[_ctx->numHandles++] = CoroScheduler.createProcess(CustomThread, &_ctx->p, sizeof(LpCfCall))) == 0) {
+ GLOBALS._mpalError = 1;
+
+ CORO_KILL_SELF();
+ return;
+ }
+ } else if (s->_command[_ctx->k]._type == 2) {
+ lockVar();
+ varSetValue(
+ s->_command[_ctx->k]._lpszVarName,
+ evaluateExpression(s->_command[_ctx->k]._expr)
+ );
+ unlockVar();
+
+ } else {
+ GLOBALS._mpalError = 1;
+ globalFree(s);
+
+ CORO_KILL_SELF();
+ return;
+ }
+ }
+ }
+
+ globalFree(s);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Thread that performs an action on an item. the thread always executes the action,
+ * so it should create a new item in which the action is the one required.
+ * Furthermore, the expression is not checked, but it is always performed the action.
+ *
+ * @param param Pointer to a pointer to a structure containing the action.
+ */
+void ActionThread(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ int j, k;
+ LpMpalItem item;
+
+ ~CoroContextTag() {
+ if (item)
+ globalDestroy(item);
+ }
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // The ActionThread owns the data block pointed to, so we need to make sure it's
+ // freed when the process exits
+ _ctx->item = *(LpMpalItem *)param;
+
+ GLOBALS._mpalError = 0;
+ for (_ctx->j = 0; _ctx->j < _ctx->item->_action[_ctx->item->_dwRes]._nCmds; _ctx->j++) {
+ _ctx->k = _ctx->item->_action[_ctx->item->_dwRes]._cmdNum[_ctx->j];
+
+ if (_ctx->item->_command[_ctx->k]._type == 1) {
+ // Custom function
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Call=%s params=%d,%d,%d,%d",
+ CoroScheduler.getCurrentPID(), GLOBALS._lplpFunctionStrings[_ctx->item->_command[_ctx->k]._nCf].c_str(),
+ _ctx->item->_command[_ctx->k]._arg1, _ctx->item->_command[_ctx->k]._arg2,
+ _ctx->item->_command[_ctx->k]._arg3, _ctx->item->_command[_ctx->k]._arg4
+ );
+
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->item->_command[_ctx->k]._nCf],
+ _ctx->item->_command[_ctx->k]._arg1,
+ _ctx->item->_command[_ctx->k]._arg2,
+ _ctx->item->_command[_ctx->k]._arg3,
+ _ctx->item->_command[_ctx->k]._arg4
+
+ );
+ } else if (_ctx->item->_command[_ctx->k]._type == 2) {
+ // Variable assign
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Variable=%s",
+ CoroScheduler.getCurrentPID(), _ctx->item->_command[_ctx->k]._lpszVarName);
+
+ lockVar();
+ varSetValue(_ctx->item->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->item->_command[_ctx->k]._expr));
+ unlockVar();
+
+ } else {
+ GLOBALS._mpalError = 1;
+ break;
+ }
+ }
+
+ globalDestroy(_ctx->item);
+ _ctx->item = NULL;
+
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", CoroScheduler.getCurrentPID());
+
+ CORO_END_CODE;
+}
+
+/**
+ * This thread monitors a created action to detect when it ends.
+ * @remarks Since actions can spawn sub-actions, this needs to be a
+ * separate thread to determine when the outer action is done
+ */
+void ShutUpActionThread(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ int slotNumber;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ GLOBALS._bExecutingAction = false;
+
+ if (g_vm->_initialLoadSlotNumber != -1) {
+ _ctx->slotNumber = g_vm->_initialLoadSlotNumber;
+ g_vm->_initialLoadSlotNumber = -1;
+
+ CORO_INVOKE_1(g_vm->loadState, _ctx->slotNumber);
+ }
+
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Polls one location (starting point of a process)
+ *
+ * @param param Pointer to an index in the array of polling locations.
+ */
+void LocationPollThread(CORO_PARAM, const void *param) {
+ typedef struct {
+ uint32 _nItem, _nAction;
+
+ uint16 _wTime;
+ byte _perc;
+ MpalHandle _when;
+ byte _nCmds;
+ uint16 _cmdNum[MAX_COMMANDS_PER_ACTION];
+ uint32 _dwLastTime;
+ } MYACTION;
+
+ typedef struct {
+ uint32 _nItem;
+ uint32 _hThread;
+ } MYTHREAD;
+
+ CORO_BEGIN_CONTEXT;
+ uint32 *il;
+ int i, j, k;
+ int numitems;
+ int nRealItems;
+ LpMpalItem curItem, newItem;
+ int nIdleActions;
+ uint32 curTime;
+ uint32 dwSleepTime;
+ uint32 dwId;
+ int ord;
+ bool delayExpired;
+ bool expired;
+
+ MYACTION *myActions;
+ MYTHREAD *myThreads;
+
+ ~CoroContextTag() {
+ // Free data blocks
+ if (myThreads)
+ globalDestroy(myThreads);
+ if (myActions)
+ globalDestroy(myActions);
+ }
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 id = *((const uint32 *)param);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Initialize data pointers
+ _ctx->myActions = NULL;
+ _ctx->myThreads = NULL;
+
+ // To begin with, we need to request the item list from the location
+ _ctx->il = mpalQueryItemList(GLOBALS._nPollingLocations[id]);
+
+ // Count the items
+ for (_ctx->numitems = 0; _ctx->il[_ctx->numitems] != 0; _ctx->numitems++)
+ ;
+
+ // We look for items without idle actions, and eliminate them from the list
+ lockItems();
+ _ctx->nIdleActions = 0;
+ _ctx->nRealItems = 0;
+ for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) {
+ _ctx->ord = itemGetOrderFromNum(_ctx->il[_ctx->i]);
+
+ if (_ctx->ord == -1)
+ continue;
+
+ _ctx->curItem = GLOBALS._lpmiItems + _ctx->ord;
+
+ _ctx->k = 0;
+ for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) {
+ if (_ctx->curItem->_action[_ctx->j]._num == 0xFF)
+ _ctx->k++;
+ }
+
+ _ctx->nIdleActions += _ctx->k;
+
+ if (_ctx->k == 0)
+ // We can remove this item from the list
+ _ctx->il[_ctx->i] = (uint32)NULL;
+ else
+ _ctx->nRealItems++;
+ }
+ unlockItems();
+
+ // If there is nothing left, we can exit
+ if (_ctx->nRealItems == 0) {
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+ _ctx->myThreads = (MYTHREAD *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nRealItems * sizeof(MYTHREAD));
+ if (_ctx->myThreads == NULL) {
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+
+ // We have established that there is at least one item that contains idle actions.
+ // Now we created the mirrored copies of the idle actions.
+ _ctx->myActions = (MYACTION *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nIdleActions * sizeof(MYACTION));
+ if (_ctx->myActions == NULL) {
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+ lockItems();
+ _ctx->k = 0;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) {
+ if (_ctx->il[_ctx->i] == 0)
+ continue;
+
+ _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->il[_ctx->i]);
+
+ for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) {
+ if (_ctx->curItem->_action[_ctx->j]._num == 0xFF) {
+ _ctx->myActions[_ctx->k]._nItem = _ctx->il[_ctx->i];
+ _ctx->myActions[_ctx->k]._nAction = _ctx->j;
+
+ _ctx->myActions[_ctx->k]._wTime = _ctx->curItem->_action[_ctx->j]._wTime;
+ _ctx->myActions[_ctx->k]._perc = _ctx->curItem->_action[_ctx->j]._perc;
+ _ctx->myActions[_ctx->k]._when = _ctx->curItem->_action[_ctx->j]._when;
+ _ctx->myActions[_ctx->k]._nCmds = _ctx->curItem->_action[_ctx->j]._nCmds;
+ memcpy(_ctx->myActions[_ctx->k]._cmdNum, _ctx->curItem->_action[_ctx->j]._cmdNum,
+ MAX_COMMANDS_PER_ACTION * sizeof(uint16));
+
+ _ctx->myActions[_ctx->k]._dwLastTime = g_vm->getTime();
+ _ctx->k++;
+ }
+ }
+ }
+
+ unlockItems();
+
+ // We don't need the item list anymore
+ globalDestroy(_ctx->il);
+
+
+ // Here's the main loop
+ while (1) {
+ // Searching for idle actions requiring time to execute
+ _ctx->curTime = g_vm->getTime();
+ _ctx->dwSleepTime = (uint32)-1L;
+
+ for (_ctx->k = 0;_ctx->k<_ctx->nIdleActions;_ctx->k++) {
+ if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) {
+ _ctx->dwSleepTime = 0;
+ break;
+ } else
+ _ctx->dwSleepTime = MIN(_ctx->dwSleepTime, _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime - _ctx->curTime);
+ }
+
+ // We fall alseep, but always checking that the event is set when prompted for closure
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, GLOBALS._hEndPollingLocations[id], _ctx->dwSleepTime, &_ctx->expired);
+
+ //if (_ctx->k == WAIT_OBJECT_0)
+ if (!_ctx->expired)
+ break;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem != 0) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 0, &_ctx->delayExpired);
+
+ // if result == WAIT_OBJECT_0)
+ if (!_ctx->delayExpired)
+ _ctx->myThreads[_ctx->i]._nItem = 0;
+ }
+ }
+
+ _ctx->curTime = g_vm->getTime();
+
+ // Loop through all the necessary idle actions
+ for (_ctx->k = 0; _ctx->k < _ctx->nIdleActions; _ctx->k++) {
+ if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) {
+ _ctx->myActions[_ctx->k]._dwLastTime += _ctx->myActions[_ctx->k]._wTime;
+
+ // It's time to check to see if fortune is on the side of the idle action
+ byte randomVal = (byte)g_vm->_randomSource.getRandomNumber(99);
+ if (randomVal < _ctx->myActions[_ctx->k]._perc) {
+ // Check if there is an action running on the item
+ if ((GLOBALS._bExecutingAction) && (GLOBALS._nExecutingAction == _ctx->myActions[_ctx->k]._nItem))
+ continue;
+
+ // Check to see if there already another idle funning running on the item
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem == _ctx->myActions[_ctx->k]._nItem)
+ break;
+ }
+
+ if (_ctx->i < _ctx->nRealItems)
+ continue;
+
+ // Ok, we are the only ones :)
+ lockItems();
+ _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->myActions[_ctx->k]._nItem);
+
+ // Check if there is a WhenExecute expression
+ _ctx->j=_ctx->myActions[_ctx->k]._nAction;
+ if (_ctx->curItem->_action[_ctx->j]._when != NULL) {
+ if (!evaluateExpression(_ctx->curItem->_action[_ctx->j]._when)) {
+ unlockItems();
+ continue;
+ }
+ }
+
+ // Ok, we can perform the action. For convenience, we do it in a new process
+ _ctx->newItem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem));
+ if (_ctx->newItem == false) {
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->myActions);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ memcpy(_ctx->newItem,_ctx->curItem, sizeof(MpalItem));
+ unlockItems();
+
+ // We copy the action in #0
+ //_ctx->newItem->Action[0].nCmds = _ctx->curItem->Action[_ctx->j].nCmds;
+ //memcpy(_ctx->newItem->Action[0].CmdNum,_ctx->curItem->Action[_ctx->j].CmdNum,_ctx->newItem->Action[0].nCmds*sizeof(_ctx->newItem->Action[0].CmdNum[0]));
+ _ctx->newItem->_dwRes = _ctx->j;
+
+ // We will create an action, and will provide the necessary details
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem == 0)
+ break;
+ }
+
+ _ctx->myThreads[_ctx->i]._nItem = _ctx->myActions[_ctx->k]._nItem;
+
+ // Create the process
+ if ((_ctx->myThreads[_ctx->i]._hThread = CoroScheduler.createProcess(ActionThread, &_ctx->newItem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE) {
+ //if ((_ctx->myThreads[_ctx->i]._hThread = (void*)_beginthread(ActionThread, 10240, (void *)_ctx->newItem)) == (void*)-1)
+ globalDestroy(_ctx->newItem);
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->myActions);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // Skip all idle actions of the same item
+ }
+ }
+ }
+ }
+
+
+ // Set idle skip on
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[200], 0, 0, 0, 0);
+
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem != 0) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 5000, &_ctx->delayExpired);
+
+ //if (result != WAIT_OBJECT_0)
+ //if (_ctx->delayExpired)
+ // TerminateThread(_ctx->MyThreads[_ctx->i].hThread, 0);
+
+ CoroScheduler.killMatchingProcess(_ctx->myThreads[_ctx->i]._hThread);
+ }
+ }
+
+ // Set idle skip off
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[201], 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Wait for the end of the dialog execution thread, and then restore global
+ * variables indicating that the dialogue has finished.
+ *
+ * @param param Pointer to a handle to the dialog
+ * @remarks This additional process is used, instead of clearing variables
+ * within the same dialog thread, because due to the recursive nature of a dialog,
+ * it would be difficult to know within it when the dialog is actually ending.
+ */
+void ShutUpDialogThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ GLOBALS._bExecutingDialog = false;
+ GLOBALS._nExecutingDialog = 0;
+ GLOBALS._nExecutingChoice = 0;
+
+ CoroScheduler.setEvent(GLOBALS._hAskChoice);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+void doChoice(CORO_PARAM, uint32 nChoice);
+
+
+/**
+ * Executes a group of the current dialog. Can 'be the Starting point of a process.
+ * @parm nGroup Number of the group to perform
+ */
+void GroupThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ LpMpalDialog dialog;
+ int i, j, k;
+ int type;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 nGroup = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Lock the _ctx->dialog
+ lockDialogs();
+
+ // Find the pointer to the current _ctx->dialog
+ _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Search inside the group requesting the _ctx->dialog
+ for (_ctx->i = 0; _ctx->dialog->_group[_ctx->i]._num != 0; _ctx->i++) {
+ if (_ctx->dialog->_group[_ctx->i]._num == nGroup) {
+ // Cycle through executing the commands of the group
+ for (_ctx->j = 0; _ctx->j < _ctx->dialog->_group[_ctx->i]._nCmds; _ctx->j++) {
+ _ctx->k = _ctx->dialog->_group[_ctx->i]._cmdNum[_ctx->j];
+
+ _ctx->type = _ctx->dialog->_command[_ctx->k]._type;
+ if (_ctx->type == 1) {
+ // Call custom function
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->dialog->_command[_ctx->k]._nCf],
+ _ctx->dialog->_command[_ctx->k]._arg1,
+ _ctx->dialog->_command[_ctx->k]._arg2,
+ _ctx->dialog->_command[_ctx->k]._arg3,
+ _ctx->dialog->_command[_ctx->k]._arg4
+ );
+
+ } else if (_ctx->type == 2) {
+ // Set a variable
+ lockVar();
+ varSetValue(_ctx->dialog->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->dialog->_command[_ctx->k]._expr));
+ unlockVar();
+
+ } else if (_ctx->type == 3) {
+ // DoChoice: call the chosen function
+ CORO_INVOKE_1(doChoice, (uint32)_ctx->dialog->_command[_ctx->k]._nChoice);
+
+ } else {
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+ }
+
+ // The gruop is finished, so we can return to the calling function.
+ // If the group was the first called, then the process will automatically
+ // end. Otherwise it returns to the caller method
+
+ return;
+ }
+ }
+
+ // If we are here, it means that we have not found the requested group
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Make a choice in the current dialog.
+ *
+ * @param nChoice Number of choice to perform
+ */
+void doChoice(CORO_PARAM, uint32 nChoice) {
+ CORO_BEGIN_CONTEXT;
+ LpMpalDialog dialog;
+ int i, j, k;
+ uint32 nGroup;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Lock the dialogs
+ lockDialogs();
+
+ // Get a pointer to the current dialog
+ _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Search the choice between those required in the dialog
+ for (_ctx->i = 0; _ctx->dialog->_choice[_ctx->i]._nChoice != 0; _ctx->i++) {
+ if (_ctx->dialog->_choice[_ctx->i]._nChoice == nChoice)
+ break;
+ }
+
+ // If nothing has been found, exit with an error
+ if (_ctx->dialog->_choice[_ctx->i]._nChoice == 0) {
+ // If we're here, we did not find the required choice
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // We've found the requested choice. Remember what in global variables
+ GLOBALS._nExecutingChoice = _ctx->i;
+
+ while (1) {
+ GLOBALS._nExecutingChoice = _ctx->i;
+
+ _ctx->k = 0;
+ // Calculate the expression of each selection, to see if they're active or inactive
+ for (_ctx->j = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._dwData != 0; _ctx->j++) {
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when == NULL) {
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1;
+ _ctx->k++;
+ } else if (evaluateExpression(_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when)) {
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1;
+ _ctx->k++;
+ } else
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 0;
+ }
+
+ // If there are no choices activated, then the dialog is finished.
+ if (_ctx->k == 0) {
+ unlockDialogs();
+ break;
+ }
+
+ // There are choices available to the user, so wait for them to make one
+ CoroScheduler.resetEvent(GLOBALS._hDoneChoice);
+ CoroScheduler.setEvent(GLOBALS._hAskChoice);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hDoneChoice, CORO_INFINITE);
+
+ // Now that the choice has been made, we can run the groups associated with the choice tbontbtitq
+ _ctx->j = GLOBALS._nSelectedChoice;
+ for (_ctx->k = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k] != 0; _ctx->k++) {
+ _ctx->nGroup = _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k];
+ CORO_INVOKE_1(GroupThread, &_ctx->nGroup);
+ }
+
+ // Control attribute
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 0)) {
+ // Bit 0 set: the end of the choice
+ unlockDialogs();
+ break;
+ }
+
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 1)) {
+ // Bit 1 set: the end of the dialog
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // End of choic ewithout attributes. We must do it again
+ }
+
+ // If we're here, we found an end choice. Return to the caller group
+ return;
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Perform an action on a certain item.
+ *
+ * @param nAction Action number
+ * @param ordItem Index of the item in the items list
+ * @param dwParam Any parameter for the action.
+ * @returns Id of the process that was launched to perform the action, or
+ * CORO_INVALID_PID_VALUE if the action was not defined, or the item was inactive.
+ * @remarks You can get the index of an item from its number by using
+ * the itemGetOrderFromNum() function. The items list must first be locked
+ * by calling LockItem().
+ */
+static uint32 doAction(uint32 nAction, uint32 ordItem, uint32 dwParam) {
+ LpMpalItem item = GLOBALS._lpmiItems;
+ LpMpalItem newitem;
+
+ item+=ordItem;
+ Common::String buf = Common::String::format("Status.%u", item->_nObj);
+ if (varGetValue(buf.c_str()) <= 0)
+ return CORO_INVALID_PID_VALUE;
+
+ for (int i = 0; i < item->_nActions; i++) {
+ if (item->_action[i]._num != nAction)
+ continue;
+
+ if (item->_action[i]._wParm != dwParam)
+ continue;
+
+ if (item->_action[i]._when != NULL) {
+ if (!evaluateExpression(item->_action[i]._when))
+ continue;
+ }
+
+ // Now we find the right action to be performed
+ // Duplicate the item and copy the current action in #i into #0
+ newitem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem));
+ if (newitem == NULL)
+ return CORO_INVALID_PID_VALUE;
+
+ // In the new version number of the action in writing dwRes
+ Common::copy((byte *)item, (byte *)item + sizeof(MpalItem), (byte *)newitem);
+
+ //newitem->_action[0]._nCmds=item->_action[i]._nCmds;
+ //memcpy(newitem->_action[0]._cmdNum, item->_action[i]._cmdNum, newitem->Action[0].nCmds * sizeof(newitem->_action[0]._cmdNum[0]));
+
+ newitem->_dwRes = i;
+
+ // And finally we can laucnh the process that will execute the action,
+ // and a second process to free up the memory when the action is finished.
+
+ // !!! New process management
+ uint32 h;
+ if ((h = CoroScheduler.createProcess(ActionThread, &newitem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ if (CoroScheduler.createProcess(ShutUpActionThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ GLOBALS._nExecutingAction = item->_nObj;
+ GLOBALS._bExecutingAction = true;
+
+ return h;
+ }
+
+ return CORO_INVALID_PID_VALUE;
+}
+
+/**
+ * Shows a dialog in a separate process.
+ *
+ * @param nDlgOrd The index of the dialog in the dialog list
+ * @param nGroup Number of the group to perform
+ * @returns The process Id of the process running the dialog
+ * or CORO_INVALID_PID_VALUE on error
+ * @remarks The dialogue runs in a thread created on purpose,
+ * so that must inform through an event and when 'necessary to you make a choice.
+ * The data on the choices may be obtained through various queries.
+ */
+static uint32 doDialog(uint32 nDlgOrd, uint32 nGroup) {
+ // Store the running dialog in a global variable
+ GLOBALS._nExecutingDialog = nDlgOrd;
+
+ // Enables the flag to indicate that there is' a running dialogue
+ GLOBALS._bExecutingDialog = true;
+
+ CoroScheduler.resetEvent(GLOBALS._hAskChoice);
+ CoroScheduler.resetEvent(GLOBALS._hDoneChoice);
+
+ // Create a thread that performs the dialogue group
+
+ // Create the process
+ uint32 h;
+ if ((h = CoroScheduler.createProcess(GroupThread, &nGroup, sizeof(uint32))) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ // Create a thread that waits until the end of the dialog process, and will restore the global variables
+ if (CoroScheduler.createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE) {
+ // Something went wrong, so kill the previously started dialog process
+ CoroScheduler.killMatchingProcess(h);
+ return CORO_INVALID_PID_VALUE;
+ }
+
+ return h;
+}
+
+
+/**
+ * Takes note of the selection chosen by the user, and warns the process that was running
+ * the box that it can continue.
+ *
+ * @param nChoice Number of choice that was in progress
+ * @param dwData Since combined with select selection
+ * @returns True if everything is OK, false on failure
+ */
+bool doSelection(uint32 i, uint32 dwData) {
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+ int j;
+
+ for (j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._dwData == dwData && dialog->_choice[i]._select[j]._curActive != 0)
+ break;
+ }
+
+ if (dialog->_choice[i]._select[j]._dwData == 0)
+ return false;
+
+ GLOBALS._nSelectedChoice = j;
+ CoroScheduler.setEvent(GLOBALS._hDoneChoice);
+ return true;
+}
+
+
+/**
+ * @defgroup Exported functions
+ */
+//@{
+
+/**
+ * Initializes the MPAL library and opens the .MPC file, which will be used for all queries.
+ *
+ * @param lpszMpcFileName Name of the MPC file
+ * @param lpszMprFileName Name of the MPR file
+ * @param lplpcfArray Array of pointers to custom functions.
+ * @returns True if everything is OK, false on failure
+ */
+bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName,
+ LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings) {
+ Common::File hMpc;
+ byte buf[5];
+ uint32 nBytesRead;
+ bool bCompress;
+ uint32 dwSizeDecomp, dwSizeComp;
+ byte *cmpbuf;
+
+ // Save the array of custom functions
+ GLOBALS._lplpFunctions = lplpcfArray;
+ GLOBALS._lplpFunctionStrings = lpcfStrings;
+
+ // OPen the MPC file for reading
+ if (!hMpc.open(lpszMpcFileName))
+ return false;
+
+ // Read and check the header
+ nBytesRead = hMpc.read(buf, 5);
+ if (nBytesRead != 5)
+ return false;
+
+ if (buf[0] != 'M' || buf[1] != 'P' || buf[2] != 'C' || buf[3] != 0x20)
+ return false;
+
+ bCompress = buf[4];
+
+ // Reads the size of the uncompressed file, and allocate memory
+ dwSizeDecomp = hMpc.readUint32LE();
+ if (hMpc.err())
+ return false;
+
+ byte *lpMpcImage = (byte *)globalAlloc(GMEM_FIXED, dwSizeDecomp + 16);
+ if (lpMpcImage == NULL)
+ return false;
+
+ if (bCompress) {
+ // Get the compressed size and read the data in
+ dwSizeComp = hMpc.readUint32LE();
+ if (hMpc.err())
+ return false;
+
+ cmpbuf = (byte *)globalAlloc(GMEM_FIXED, dwSizeComp);
+ if (cmpbuf == NULL)
+ return false;
+
+ nBytesRead = hMpc.read(cmpbuf, dwSizeComp);
+ if (nBytesRead != dwSizeComp)
+ return false;
+
+ // Decompress the data
+ lzo1x_decompress(cmpbuf, dwSizeComp, lpMpcImage, &nBytesRead);
+ if (nBytesRead != dwSizeDecomp)
+ return false;
+
+ globalDestroy(cmpbuf);
+ } else {
+ // If the file is not compressed, we directly read in the data
+ nBytesRead = hMpc.read(lpMpcImage, dwSizeDecomp);
+ if (nBytesRead != dwSizeDecomp)
+ return false;
+ }
+
+ // Close the file
+ hMpc.close();
+
+ // Process the data
+ if (parseMpc(lpMpcImage) == false)
+ return false;
+
+ globalDestroy(lpMpcImage);
+
+ // Open the MPR file
+ if (!GLOBALS._hMpr.open(lpszMprFileName))
+ return false;
+
+ // Seek to the end of the file to read overall information
+ GLOBALS._hMpr.seek(-12, SEEK_END);
+
+ dwSizeComp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ GLOBALS._nResources = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ nBytesRead = GLOBALS._hMpr.read(buf, 4);
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ if (buf[0] !='E' || buf[1] != 'N' || buf[2] != 'D' || buf[3] != '0')
+ return false;
+
+ // Move to the start of the resources header
+ GLOBALS._hMpr.seek(-(12 + (int)dwSizeComp), SEEK_END);
+
+ GLOBALS._lpResources = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, GLOBALS._nResources * 8);
+ if (GLOBALS._lpResources == NULL)
+ return false;
+
+ cmpbuf = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, dwSizeComp);
+ if (cmpbuf == NULL)
+ return false;
+
+ nBytesRead = GLOBALS._hMpr.read(cmpbuf, dwSizeComp);
+ if (nBytesRead != dwSizeComp)
+ return false;
+
+ lzo1x_decompress((const byte *)cmpbuf, dwSizeComp, (byte *)GLOBALS._lpResources, (uint32 *)&nBytesRead);
+ if (nBytesRead != (uint32)GLOBALS._nResources * 8)
+ return false;
+
+ globalDestroy(cmpbuf);
+
+ // Reset back to the start of the file, leaving it open
+ GLOBALS._hMpr.seek(0, SEEK_SET);
+
+ // There is no action or dialog running by default
+ GLOBALS._bExecutingAction = false;
+ GLOBALS._bExecutingDialog = false;
+
+ // There's no polling location
+ Common::fill(GLOBALS._nPollingLocations, GLOBALS._nPollingLocations + MAXPOLLINGLOCATIONS, 0);
+
+ // Create the event that will be used to co-ordinate making choices and choices finishing
+ GLOBALS._hAskChoice = CoroScheduler.createEvent(true, false);
+ GLOBALS._hDoneChoice = CoroScheduler.createEvent(true, false);
+
+ return true;
+}
+
+/**
+ * Frees resources allocated by the MPAL subsystem
+ */
+void mpalFree() {
+ // Free the resource list
+ globalDestroy(GLOBALS._lpResources);
+}
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns numeric results.
+ */
+uint32 mpalQueryDWORD(uint16 wQueryType, ...) {
+ Common::String buf;
+ uint32 dwRet = 0;
+ char *n;
+
+ va_list v;
+ va_start(v, wQueryType);
+
+ GLOBALS._mpalError = OK;
+
+ if (wQueryType == MPQ_VERSION) {
+
+ /*
+ * uint32 mpalQuery(MPQ_VERSION);
+ */
+ dwRet = HEX_VERSION;
+
+ } else if (wQueryType == MPQ_GLOBAL_VAR) {
+ /*
+ * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName);
+ */
+ lockVar();
+ dwRet = (uint32)varGetValue(GETARG(char *));
+ unlockVar();
+
+ } else if (wQueryType == MPQ_MESSAGE) {
+ /*
+ * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg);
+ */
+ error("mpalQuery(MPQ_MESSAGE, uint32 nMsg) used incorrect method variant");
+
+
+ } else if (wQueryType == MPQ_ITEM_PATTERN) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem);
+ */
+ lockVar();
+ buf = Common::String::format("Pattern.%u", GETARG(uint32));
+ dwRet = (uint32)varGetValue(buf.c_str());
+ unlockVar();
+
+ } else if (wQueryType == MPQ_LOCATION_SIZE) {
+ /*
+ * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord);
+ */
+ lockLocations();
+ int x = locGetOrderFromNum(GETARG(uint32));
+ int y = GETARG(uint32);
+ if (x != -1) {
+ if (y == MPQ_X)
+ dwRet = GLOBALS._lpmlLocations[x]._dwXlen;
+ else if (y == MPQ_Y)
+ dwRet = GLOBALS._lpmlLocations[x]._dwYlen;
+ else
+ GLOBALS._mpalError = 1;
+ } else
+ GLOBALS._mpalError = 1;
+
+ unlockLocations();
+
+ } else if (wQueryType == MPQ_LOCATION_IMAGE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc);
+ */
+ error("mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc) used incorrect variant");
+
+ } else if (wQueryType == MPQ_RESOURCE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes);
+ */
+ error("mpalQuery(MPQ_RESOURCE, uint32 dwRes) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_LIST) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc);
+ */
+ error("mpalQuery(MPQ_ITEM_LIST, uint32 nLoc) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_DATA) {
+ /*
+ * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_DATA, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) {
+ /*
+ * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ dwRet = (uint32)false;
+ else
+ dwRet = (uint32)true;
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_ITEM_NAME) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char * lpszName);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ n = GETARG(char *);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ n[0]='\0';
+ else {
+ lockItems();
+ int y = itemGetOrderFromNum(x);
+ memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE);
+ unlockItems();
+ }
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_DIALOG_PERIOD) {
+ /*
+ * char *mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod);
+ */
+ error("mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTLIST) {
+ /*
+ * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice);
+ */
+ error("mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTION) {
+ /*
+ * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData);
+ */
+ lockDialogs();
+ int x = GETARG(uint32);
+ int y = GETARG(uint32);
+ dwRet = (uint32)doSelection(x, y);
+
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DO_ACTION) {
+ /*
+ * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam);
+ */
+ lockItems();
+ lockVar();
+ int x = GETARG(uint32);
+ int z = GETARG(uint32);
+ int y = itemGetOrderFromNum(z);
+ if (y != -1) {
+ dwRet = doAction(x, y, GETARG(uint32));
+ } else {
+ dwRet = CORO_INVALID_PID_VALUE;
+ GLOBALS._mpalError = 1;
+ }
+
+ unlockVar();
+ unlockItems();
+
+ } else if (wQueryType == MPQ_DO_DIALOG) {
+ /*
+ * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup);
+ */
+ if (!GLOBALS._bExecutingDialog) {
+ lockDialogs();
+
+ int x = dialogGetOrderFromNum(GETARG(uint32));
+ int y = GETARG(uint32);
+ dwRet = doDialog(x, y);
+ unlockDialogs();
+ }
+ } else {
+ /*
+ * DEFAULT -> ERROR
+ */
+ GLOBALS._mpalError = 1;
+ }
+
+ va_end(v);
+ return dwRet;
+}
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns a pointer or handle.
+ */
+MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...) {
+ char *n;
+ Common::String buf;
+ va_list v;
+ va_start(v, wQueryType);
+ void *hRet = NULL;
+
+ GLOBALS._mpalError = OK;
+
+ if (wQueryType == MPQ_VERSION) {
+ /*
+ * uint32 mpalQuery(MPQ_VERSION);
+ */
+ error("mpalQuery(MPQ_VERSION) used incorrect variant");
+
+ } else if (wQueryType == MPQ_GLOBAL_VAR) {
+ /*
+ * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName);
+ */
+ error("mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName) used incorrect variant");
+
+ } else if (wQueryType == MPQ_MESSAGE) {
+ /*
+ * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg);
+ */
+ LockMsg();
+ hRet = DuplicateMessage(msgGetOrderFromNum(GETARG(uint32)));
+ UnlockMsg();
+
+ } else if (wQueryType == MPQ_ITEM_PATTERN) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_LOCATION_SIZE) {
+ /*
+ * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord);
+ */
+ error("mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord) used incorrect variant");
+
+ } else if (wQueryType == MPQ_LOCATION_IMAGE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc);
+ */
+ lockLocations();
+ int x = locGetOrderFromNum(GETARG(uint32));
+ hRet = resLoad(GLOBALS._lpmlLocations[x]._dwPicRes);
+ unlockLocations();
+
+ } else if (wQueryType == MPQ_RESOURCE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes);
+ */
+ hRet = resLoad(GETARG(uint32));
+
+ } else if (wQueryType == MPQ_ITEM_LIST) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc);
+ */
+ lockVar();
+ hRet = GetItemList(GETARG(uint32));
+ lockVar();
+
+ } else if (wQueryType == MPQ_ITEM_DATA) {
+ /*
+ * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem);
+ */
+ lockItems();
+ hRet = getItemData(itemGetOrderFromNum(GETARG(uint32)));
+ unlockItems();
+
+ } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) {
+ /*
+ * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_NAME) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char *lpszName);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ n = GETARG(char *);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ n[0] = '\0';
+ else {
+ lockItems();
+ int y = itemGetOrderFromNum(x);
+ memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE);
+ unlockItems();
+ }
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_DIALOG_PERIOD) {
+ /*
+ * char * mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod);
+ */
+ lockDialogs();
+ int y = GETARG(uint32);
+ hRet = duplicateDialogPeriod(y);
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTLIST) {
+ /*
+ * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice);
+ */
+ lockDialogs();
+ hRet = getSelectList(GETARG(uint32));
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTION) {
+ /*
+ * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData);
+ */
+ error("mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DO_ACTION) {
+ /*
+ * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam);
+ */
+ error("mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DO_DIALOG) {
+ /*
+ * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup);
+ */
+ error("mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup) used incorrect variant");
+ } else {
+ /*
+ * DEFAULT -> ERROR
+ */
+ GLOBALS._mpalError = 1;
+ }
+
+ va_end(v);
+ return hRet;
+}
+
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that needs to run within a co-routine context.
+ */
+void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...) {
+ CORO_BEGIN_CONTEXT;
+ uint32 dwRet;
+ CORO_END_CONTEXT(_ctx);
+
+ va_list v;
+ va_start(v, dwRet);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hAskChoice, CORO_INFINITE);
+
+ // WORKAROUND: Introduce a single frame delay so that if there are multiple actions running,
+ // they all have time to be signalled before resetting the event. This fixes a problem where
+ // if you try to use the 'shrimp' on the parrot a second time after trying to first use it
+ // whilst the parrot was talking, the cursor wouldn't be re-enabled afterwards
+ CORO_SLEEP(1);
+
+ CoroScheduler.resetEvent(GLOBALS._hAskChoice);
+
+ if (GLOBALS._bExecutingDialog)
+ *dwRet = (uint32)GLOBALS._nExecutingChoice;
+ else
+ *dwRet = (uint32)((int)-1);
+ } else {
+ error("mpalQueryCORO called with unsupported query type");
+ }
+
+ CORO_END_CODE;
+
+ va_end(v);
+}
+
+/**
+ * Returns the current MPAL error code
+ *
+ * @returns Error code
+ */
+uint32 mpalGetError() {
+ return GLOBALS._mpalError;
+}
+
+/**
+ * Execute a script. The script runs on multitasking by a thread.
+ *
+ * @param nScript Script number to run
+ * @returns TRUE if the script 'was launched, FALSE on failure
+ */
+bool mpalExecuteScript(int nScript) {
+ LpMpalScript s;
+
+ LockScripts();
+ int n = scriptGetOrderFromNum(nScript);
+ s = (LpMpalScript)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalScript));
+ if (s == NULL)
+ return false;
+
+ memcpy(s, GLOBALS._lpmsScripts + n, sizeof(MpalScript));
+ unlockScripts();
+
+ // !!! New process management
+ if (CoroScheduler.createProcess(ScriptThread, &s, sizeof(LpMpalScript)) == CORO_INVALID_PID_VALUE)
+ return false;
+
+ return true;
+}
+
+/**
+ * Install a custom routine That will be called by MPAL every time the pattern
+ * of an item has been changed.
+ *
+ * @param lpiifCustom Custom function to install
+ */
+void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCus) {
+ GLOBALS._lpiifCustom = lpiifCus;
+}
+
+/**
+ * Process the idle actions of the items on one location.
+ *
+ * @param nLoc Number of the location whose items must be processed
+ * for idle actions.
+ * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
+ * @remarks The maximum number of locations that can be polled
+ * simultaneously is defined defined by MAXPOLLINGFUNCIONS
+ */
+bool mpalStartIdlePoll(int nLoc) {
+ for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) {
+ if (GLOBALS._nPollingLocations[i] == (uint32)nLoc)
+ return false;
+ }
+
+ for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) {
+ if (GLOBALS._nPollingLocations[i] == 0) {
+ GLOBALS._nPollingLocations[i] = nLoc;
+
+ GLOBALS._hEndPollingLocations[i] = CoroScheduler.createEvent(true, false);
+// !!! New process management
+ if ((GLOBALS._pollingThreads[i] = CoroScheduler.createProcess(LocationPollThread, &i, sizeof(uint32))) == CORO_INVALID_PID_VALUE)
+// if ((GLOBALS.hEndPollingLocations[i] = (void*)_beginthread(LocationPollThread, 10240, (void *)i))= = (void*)-1)
+ return false;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Stop processing the idle actions of the items on one location.
+ *
+ * @param nLo Number of the location
+ * @returns TRUE if all OK, FALSE if the specified location was not
+ * in the process of polling
+ */
+void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ for (_ctx->i = 0; _ctx->i < MAXPOLLINGLOCATIONS; _ctx->i++) {
+ if (GLOBALS._nPollingLocations[_ctx->i] == (uint32)nLoc) {
+ CoroScheduler.setEvent(GLOBALS._hEndPollingLocations[_ctx->i]);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._pollingThreads[_ctx->i], CORO_INFINITE);
+
+ CoroScheduler.closeEvent(GLOBALS._hEndPollingLocations[_ctx->i]);
+ GLOBALS._nPollingLocations[_ctx->i] = 0;
+
+ if (result)
+ *result = true;
+ return;
+ }
+ }
+
+ if (result)
+ *result = false;
+
+ CORO_END_CODE;
+}
+
+/**
+ * Retrieve the length of a save state
+ *
+ * @returns Length in bytes
+ */
+int mpalGetSaveStateSize() {
+ return GLOBALS._nVars * sizeof(MpalVar) + 4;
+}
+
+/**
+ * Store the save state into a buffer. The buffer must be
+ * length at least the size specified with mpalGetSaveStateSize
+ *
+ * @param buf Buffer where to store the state
+ */
+void mpalSaveState(byte *buf) {
+ lockVar();
+ WRITE_LE_UINT32(buf, GLOBALS._nVars);
+ memcpy(buf + 4, (byte *)GLOBALS._lpmvVars, GLOBALS._nVars * sizeof(MpalVar));
+ unlockVar();
+}
+
+
+/**
+ * Load a save state from a buffer.
+ *
+ * @param buf Buffer where to store the state
+ * @returns Length of the state buffer in bytes
+ */
+int mpalLoadState(byte *buf) {
+ // We must destroy and recreate all the variables
+ globalFree(GLOBALS._hVars);
+
+ GLOBALS._nVars = READ_LE_UINT32(buf);
+
+ GLOBALS._hVars = globalAllocate(GMEM_ZEROINIT | GMEM_MOVEABLE, GLOBALS._nVars * sizeof(MpalVar));
+ lockVar();
+ memcpy((byte *)GLOBALS._lpmvVars, buf + 4, GLOBALS._nVars * sizeof(MpalVar));
+ unlockVar();
+
+ return GLOBALS._nVars * sizeof(MpalVar) + 4;
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/mpal.h b/engines/tony/mpal/mpal.h
new file mode 100644
index 0000000000..c5f505063f
--- /dev/null
+++ b/engines/tony/mpal/mpal.h
@@ -0,0 +1,516 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+
+/****************************************************************************\
+* General Introduction
+\****************************************************************************/
+
+/*
+ * MPAL (MultiPurpose Adventure Language) is a high level language
+ * for the definition of adventure. Through the use of MPAL you can describe
+ * storyboard the adventure, and then use it with any user interface.
+ * In fact, unlike many other similar products, MPAL is not programmed through
+ * the whole adventure, but are defined only the locations, objects, as they may
+ * interact with each other, etc.. thus making MPAL useful for any type of adventure.
+ */
+
+/****************************************************************************\
+* Structure
+\****************************************************************************/
+
+/*
+ * MPAL consists of two main files: MPAL.DLL and MPAL.H
+ * The first is the DLL that contains the code to interface with MPAL
+ * adventures, the second is the header that defines the prototypes
+ * functions. MPAL is compiled for Win32, and it can therefore be used with
+ * any compiler that supports Win32 DLL (Watcom C++, Visual C++,
+ * Delphi, etc.), and therefore compatible with both Windows 95 and Windows NT.
+ *
+ * To use the DLL, and 'obviously need to create a library for symbols to export.
+ *
+ */
+
+
+/****************************************************************************\
+* Custom Functions
+\****************************************************************************/
+
+/*
+ * A custom function and a function specified by the program that uses the
+ * library, to perform the particular code. The custom functions are
+ * retrieved from the library as specified in the source MPAL, and in particular
+ * in defining the behavior of an item with some action.
+ *
+ * To use the custom functions, you need to prepare an array of
+ * pointers to functions (such as using the type casting LPCUSTOMFUNCTION,
+ * (defined below), and pass it as second parameter to mpalInit (). Note you
+ * must specify the size of the array, as elements of pointers and which do not
+ * contain the same: the library will call it only those functions specified in
+ * the source MPAL. It can be useful, for debugging reasons, do not bet
+ * the shares of arrays used to debugging function, to avoid unpleasant crash,
+ * if it has been made an error in source and / or some oversight in the code.
+ *
+ */
+
+#ifndef TONY_MPAL_H
+#define TONY_MPAL_H
+
+#include "common/scummsys.h"
+#include "common/coroutines.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Macro definitions and structures
+\****************************************************************************/
+
+// OK value for the error codes
+#define OK 0
+
+#define MAXFRAMES 400 // frame animation of an object
+#define MAXPATTERN 40 // pattern of animation of an object
+#define MAXPOLLINGLOCATIONS 64
+
+/**
+ * Macro for use with queries that may refer to X and Y co-ordinates
+ */
+enum QueryCoordinates {
+ MPQ_X,
+ MPQ_Y
+};
+
+/**
+ * Query can be used with mpalQuery methods. In practice corresponds all claims
+ * that can do at the library
+ */
+enum QueryTypes {
+ // General Query
+ MPQ_VERSION = 10,
+
+ MPQ_GLOBAL_VAR = 50,
+ MPQ_RESOURCE,
+ MPQ_MESSAGE,
+
+ // Query on leases
+ MPQ_LOCATION_IMAGE = 100,
+ MPQ_LOCATION_SIZE,
+
+ // Queries about items
+ MPQ_ITEM_LIST = 200,
+ MPQ_ITEM_DATA,
+ MPQ_ITEM_PATTERN,
+ MPQ_ITEM_NAME,
+ MPQ_ITEM_IS_ACTIVE,
+
+ // Query dialog
+ MPQ_DIALOG_PERIOD = 300,
+ MPQ_DIALOG_WAITFORCHOICE,
+ MPQ_DIALOG_SELECTLIST,
+ MPQ_DIALOG_SELECTION,
+
+ // Query execution
+ MPQ_DO_ACTION = 400,
+ MPQ_DO_DIALOG
+};
+
+/**
+ * Framework to manage the animation of an item
+ */
+typedef struct {
+ char *_frames[MAXFRAMES];
+ Common::Rect _frameslocations[MAXFRAMES];
+ Common::Rect _bbox[MAXFRAMES];
+ short _pattern[MAXPATTERN][MAXFRAMES];
+ short _speed;
+ char _numframe;
+ char _numpattern;
+ char _curframe;
+ char _curpattern;
+ short _destX, _destY;
+ signed char _destZ;
+ short _objectID;
+} Item;
+typedef Item *LpItem;
+
+
+/**
+ * Define a custom function, to use the language MPAL to perform various controls as a result of an action
+ */
+typedef void (*LPCUSTOMFUNCTION)(CORO_PARAM, uint32, uint32, uint32, uint32);
+typedef LPCUSTOMFUNCTION *LPLPCUSTOMFUNCTION;
+
+/**
+ *
+ * Define an IRQ of an item that is called when the pattern changes or the status of an item
+ */
+typedef void (*LPITEMIRQFUNCTION)(uint32, int, int);
+typedef LPITEMIRQFUNCTION* LPLPITEMIRQFUNCTION;
+
+/**
+ * @defgroup Macrofunctions query
+ *
+ * The following are defines used for simplifying calling the mpalQuery variants
+ */
+//@{
+
+/**
+ * Gets the current version of MPAL
+ *
+ * @returns Version number (0x1232 = 1.2.3b)
+ */
+#define mpalQueryVersion() \
+ (uint16)mpalQueryDWORD(MPQ_VERSION)
+
+/**
+ * Gets the numerical value of a global variable
+ *
+ * @param lpszVarName Variable name (ASCIIZ)
+ * @returns Global variable value
+ * @remarks This query was implemented for debugging. The program,
+ * if well designed, should not need to access variables from
+ * within the library.
+ */
+#define mpalQueryGlobalVar(lpszVarName) \
+ mpalQueryDWORD(MPQ_GLOBAL_VAR, (const char *)(lpszVarName))
+
+
+/**
+ * Provides access to a resource inside the .MPC file
+ *
+ * @param dwResId Resource Id
+ * @returns Handle to a memory area containing the resource, ready for use.
+ */
+#define mpalQueryResource(dwResId) \
+ mpalQueryHANDLE(MPQ_RESOURCE, (uint32)(dwResId))
+
+
+/**
+ * Returns a message.
+ *
+ * @param nMsg Message number
+ * @returns ASCIIZ message
+ * @remarks The returned pointer must be freed via the memory manager
+* after use. The message will be in ASCIIZ format.
+*/
+#define mpalQueryMessage(nMsg) \
+ (char *)mpalQueryHANDLE(MPQ_MESSAGE, (uint32)(nMsg))
+
+
+/**
+ * Provides a location image
+ * @return Returns a picture handle
+ */
+#define mpalQueryLocationImage(nLoc) \
+ mpalQueryHANDLE(MPQ_LOCATION_IMAGE, (uint32)(nLoc))
+
+
+/**
+ * Request the x or y size of a location in pixels
+ *
+ * @param nLoc Location number
+ * @param dwCoord MPQ_X or MPQ_Y coordinate to retrieve
+ * @returns Size
+ */
+#define mpalQueryLocationSize(nLoc, dwCoord) \
+ mpalQueryDWORD(MPQ_LOCATION_SIZE, (uint32)(nLoc), (uint32)(dwCoord))
+
+
+/**
+ * Provides the list of objects in a location.
+ *
+ * @param nLoc Location number
+ * @returns List of objects (accessible by Item [0], Item [1], etc.)
+ */
+// TODO: Determine if this is endian safe
+#define mpalQueryItemList(nLoc) \
+ (uint32 *)mpalQueryHANDLE(MPQ_ITEM_LIST, (uint32)(nLoc))
+
+
+/**
+ * Provides information on an item
+ *
+ * @param nItem Item number
+ * @returns Structure filled with requested information
+ */
+#define mpalQueryItemData(nItem) \
+ (LpItem)mpalQueryHANDLE(MPQ_ITEM_DATA, (uint32)(nItem))
+
+
+/**
+ * Provides the current pattern of an item
+ *
+ * @param nItem Item number
+ * @returns Number of animation patterns to be executed.
+ * @remarks By default, the pattern of 0 indicates that we should do nothing.
+ */
+#define mpalQueryItemPattern(nItem) \
+ mpalQueryDWORD(MPQ_ITEM_PATTERN, (uint32)(nItem))
+
+
+/**
+ * Returns true if an item is active
+ *
+ * @param nItem Item number
+ * @returns TRUE if the item is active, FALSE otherwise
+ */
+#define mpalQueryItemIsActive(nItem) \
+ (bool)mpalQueryDWORD(MPQ_ITEM_IS_ACTIVE, (uint32)(nItem))
+
+
+/**
+ * Returns the name of an item
+ *
+ * @param nItem Item number
+ * @param lpszName Pointer to a buffer of at least 33 bytes
+ * that will be filled with the name
+ * @remarks If the item is not active (ie. if its status or number
+ * is less than or equal to 0), the string will be empty.
+ */
+#define mpalQueryItemName(nItem, lpszName) \
+ mpalQueryHANDLE(MPQ_ITEM_NAME, (uint32)(nItem), (char *)(lpszName))
+
+
+/**
+ * Returns a sentence of dialog.
+ *
+ * @param nDialog Dialog number
+ * @param nPeriod Number of words
+ * @returns A pointer to the string of words, or NULL on failure.
+ * @remarks The string must be freed after use using the memory manager.
+ * Unlike normal messages, the sentences of dialogue are formed by a single
+ * string terminated with 0.
+ */
+#define mpalQueryDialogPeriod(nPeriod) \
+ (char *)mpalQueryHANDLE(MPQ_DIALOG_PERIOD, (uint32)(nPeriod))
+
+
+/**
+ * Wait until the moment in which the need is signaled to make a choice by the user.
+ * @returns Number of choice to be made, or -1 if the dialogue is finished.
+ */
+#define mpalQueryDialogWaitForChoice(dwRet) \
+ CORO_INVOKE_2(mpalQueryCORO, MPQ_DIALOG_WAITFORCHOICE, dwRet)
+
+/**
+ * Requires a list of various options for some choice within the current dialog.
+ *
+ * @param nChoice Choice number
+ * @returns A pointer to an array containing the data matched to each option.
+ * @remarks The figure 'a uint32 specified in the source to which MPAL
+ * You can assign meaning that the more' suits.
+ * The pointer msut be freed after use using the memory memory.
+ */
+#define mpalQueryDialogSelectList(nChoice) \
+ (uint32 *)mpalQueryHANDLE(MPQ_DIALOG_SELECTLIST, (uint32)(nChoice))
+
+/**
+ * Warns the library that the user has selected, in a certain choice of the current dialog,
+ * corresponding option at a certain given.
+ *
+ * @param nChoice Choice number of the choice that was in progress
+ * @param dwData Option that was selected by the user.
+ * @returns TRUE if all OK, FALSE on failure.
+ * @remarks After execution of this query, MPAL continue
+ * Groups according to the execution of the dialogue. And necessary so the game
+ * remains on hold again for another chosen by mpalQueryDialogWaitForChoice ().
+ */
+#define mpalQueryDialogSelection(nChoice, dwData) \
+ (bool)mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
+
+#define mpalQueryDialogSelectionDWORD(nChoice, dwData) \
+ mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
+
+/**
+ * Warns the library an action was performed on a Object.
+ * The library will call custom functions, if necessary.
+ *
+ * @param nAction Action number
+ * @param nItem Item number
+ * @param dwParam Action parameter
+ * @returns Handle to the thread that is performing the action, or CORO_INVALID_PID_VALUE
+ * if the action is not defined for the item, or the item is inactive.
+ * @remarks The parameter is used primarily to implement actions
+ * as "U.S." involving two objects together. The action will be executed only
+ * if the item is active, ie if its status is a positive number greater than 0.
+ */
+#define mpalQueryDoAction(nAction, nItem, dwParam) \
+ mpalQueryDWORD(MPQ_DO_ACTION, (uint32)(nAction), (uint32)(nItem), (uint32)(dwParam))
+
+/**
+ * Warns the library a dialogue was required.
+ *
+ * @param nDialog Dialog number
+ * @param nGroup Group number to use
+ * @returns Handle to the thread that is running the box, or
+ * CORO_INVALID_PID_VALUE if the dialogue does not exist.
+ */
+#define mpalQueryDoDialog(nDialog, nGroup) \
+ mpalQueryDWORD(MPQ_DO_DIALOG, (uint32)(nDialog), (uint32)(nGroup))
+
+/**
+ * @defgroup Functions exported to the main game
+ */
+//@{
+
+/**
+ * Initializes the MPAL library, and opens an .MPC file, which will be 'used for all queries
+ * @param lpszMpcFileName Name of the .MPC file, including extension
+ * @param lpszMprFileName Name of the .MPR file, including extension
+ * @param lplpcfArray Array of pointers to custom functions
+ * @returns TRUE if all OK, FALSE on failure
+ */
+bool mpalInit(const char *lpszFileName, const char *lpszMprFileName,
+ LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings);
+
+/**
+ * Frees resources allocated by the MPAL subsystem
+ */
+void mpalFree();
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns numeric results.
+ */
+uint32 mpalQueryDWORD(uint16 wQueryType, ...);
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns a pointer or handle.
+ */
+MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...);
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that needs to run within a co-routine context.
+ */
+void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...);
+
+/**
+ * Execute a script. The script runs on multitasking by a thread.
+ *
+ * @param nScript Script number to run
+ * @returns TRUE if the script 'was launched, FALSE on failure
+ */
+bool mpalExecuteScript(int nScript);
+
+/**
+ * Returns the current MPAL error code
+ *
+ * @returns Error code
+ */
+uint32 mpalGetError();
+
+/**
+ * Install a custom routine That will be called by MPAL every time the pattern
+ * of an item has been changed.
+ *
+ * @param lpiifCustom Custom function to install
+ */
+void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCustom);
+
+/**
+ * Process the idle actions of the items on one location.
+ *
+ * @param nLoc Number of the location whose items must be processed
+ * for idle actions.
+ * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
+ * @remarks The maximum number of locations that can be polled
+ * simultaneously is defined defined by MAXPOLLINGFUNCIONS
+ */
+bool mpalStartIdlePoll(int nLoc);
+
+/**
+ * Stop processing the idle actions of the items on one location.
+ *
+ * @param nLo Number of the location
+ * @returns TRUE if all OK, FALSE if the specified location was not
+ * in the process of polling
+ */
+void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result);
+
+
+/**
+ * Load a save state from a buffer.
+ *
+ * @param buf Buffer where to store the state
+ * @returns Length of the state buffer in bytes
+ */
+int mpalLoadState(byte *buf);
+
+/**
+ * Store the save state into a buffer. The buffer must be
+ * length at least the size specified with mpalGetSaveStateSize
+ *
+ * @param buf Buffer where to store the state
+ */
+void mpalSaveState(byte *buf);
+
+/**
+ * Retrieve the length of a save state
+ *
+ * @returns Length in bytes
+ */
+int mpalGetSaveStateSize();
+
+/**
+ * Locks the variables for access
+ */
+void lockVar();
+
+/**
+ * Unlocks variables after use
+ */
+void unlockVar();
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/mpaldll.h b/engines/tony/mpal/mpaldll.h
new file mode 100644
index 0000000000..8897096f51
--- /dev/null
+++ b/engines/tony/mpal/mpaldll.h
@@ -0,0 +1,251 @@
+/* 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.
+ *
+ *
+ */
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef __MPALDLL_H
+#define __MPALDLL_H
+
+#include "common/file.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/loadmpc.h"
+#include "tony/mpal/expr.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Defines
+\****************************************************************************/
+
+#define HEX_VERSION 0x0170
+
+#define MAX_ACTIONS_PER_ITEM 40
+#define MAX_COMMANDS_PER_ITEM 128
+#define MAX_COMMANDS_PER_ACTION 128
+#define MAX_DESCRIBE_SIZE 64
+
+#define MAX_MOMENTS_PER_SCRIPT 256
+#define MAX_COMMANDS_PER_SCRIPT 256
+#define MAX_COMMANDS_PER_MOMENT 32
+
+#define MAX_GROUPS_PER_DIALOG 128
+#define MAX_COMMANDS_PER_DIALOG 480
+#define MAX_COMMANDS_PER_GROUP 64
+#define MAX_CHOICES_PER_DIALOG 64
+#define MAX_SELECTS_PER_CHOICE 64
+#define MAX_PLAYGROUPS_PER_SELECT 9
+#define MAX_PERIODS_PER_DIALOG 400
+
+#define NEED_LOCK_MSGS
+
+/****************************************************************************\
+* Structures
+\****************************************************************************/
+
+#include "common/pack-start.h"
+
+/**
+ * MPAL global variables
+ */
+struct MpalVar {
+ uint32 _dwVal; // Variable value
+ char _lpszVarName[33]; // Variable name
+} PACKED_STRUCT;
+typedef MpalVar *LpMpalVar;
+
+/**
+ * MPAL Messages
+ */
+struct MpalMsg {
+ MpalHandle _hText; // Handle to the message text
+ uint16 _wNum; // Message number
+} PACKED_STRUCT;
+typedef MpalMsg *LpMpalMsg;
+
+/**
+ * MPAL Locations
+ */
+struct MpalLocation {
+ uint32 _nObj; // Location number
+ uint32 _dwXlen, _dwYlen; // Dimensions
+ uint32 _dwPicRes; // Resource that contains the image
+} PACKED_STRUCT;
+typedef MpalLocation *LpMpalLocation;
+
+/**
+ * All the data for a command, ie. tags used by OnAction in the item, the time
+ * in the script, and in the group dialog.
+ */
+struct Command {
+ /*
+ * Types of commands that are recognized
+ *
+ * #1 -> Custom function call (ITEM, SCRIPT, DIALOG)
+ * #2 -> Variable assignment (ITEM, SCRIPT, DIALOG)
+ * #3 -> Making a choice (DIALOG)
+ *
+ */
+ byte _type; // Type of control
+
+ union {
+ int32 _nCf; // Custom function call [#1]
+ char *_lpszVarName; // Variable name [#2]
+ int32 _nChoice; // Number of choice you make [#3]
+ };
+
+ union {
+ int32 _arg1; // Argument for custom function [#1]
+ MpalHandle _expr; // Expression to assign to a variable [#2]
+ };
+
+ int32 _arg2, _arg3, _arg4; // Arguments for custom function [#1]
+} PACKED_STRUCT;
+
+
+/**
+ * MPAL dialog
+ */
+struct MpalDialog {
+ uint32 _nObj; // Dialog number
+
+ struct Command _command[MAX_COMMANDS_PER_DIALOG];
+
+ struct {
+ uint16 _num;
+ byte _nCmds;
+ uint16 _cmdNum[MAX_COMMANDS_PER_GROUP];
+
+ } _group[MAX_GROUPS_PER_DIALOG];
+
+ struct {
+ // The last choice has nChoice == 0
+ uint16 _nChoice;
+
+ // The select number (we're pretty stingy with RAM). The last select has dwData == 0
+ struct {
+ MpalHandle _when;
+ uint32 _dwData;
+ uint16 _wPlayGroup[MAX_PLAYGROUPS_PER_SELECT];
+
+ // Bit 0=endchoice Bit 1=enddialog
+ byte _attr;
+
+ // Modified at run-time: 0 if the select is currently disabled,
+ // and 1 if currently active
+ byte _curActive;
+ } _select[MAX_SELECTS_PER_CHOICE];
+
+ } _choice[MAX_CHOICES_PER_DIALOG];
+
+ uint16 _periodNums[MAX_PERIODS_PER_DIALOG];
+ MpalHandle _periods[MAX_PERIODS_PER_DIALOG];
+
+} PACKED_STRUCT;
+typedef MpalDialog *LpMpalDialog;
+
+/**
+ * MPAL Item
+ */
+struct ItemAction {
+ byte _num; // Action number
+ uint16 _wTime; // If idle, the time which must pass
+ byte _perc; // Percentage of the idle run
+ MpalHandle _when; // Expression to compute. If != 0, then
+ // action can be done
+ uint16 _wParm; // Parameter for action
+
+ byte _nCmds; // Number of commands to be executed
+ uint32 _cmdNum[MAX_COMMANDS_PER_ACTION]; // Commands to execute
+} PACKED_STRUCT;
+
+struct MpalItem {
+ uint32 _nObj; // Item number
+
+ byte _lpszDescribe[MAX_DESCRIBE_SIZE]; // Name
+ byte _nActions; // Number of managed actions
+ uint32 _dwRes; // Resource that contains frames and patterns
+
+ struct Command _command[MAX_COMMANDS_PER_ITEM];
+
+ // Pointer to array of structures containing various managed activities. In practice, of
+ // every action we know what commands to run, including those defined in structures above
+ struct ItemAction *_action;
+
+} PACKED_STRUCT;
+typedef MpalItem *LpMpalItem;
+
+/**
+ * MPAL Script
+ */
+struct MpalScript {
+ uint32 _nObj;
+ uint32 _nMoments;
+
+ struct Command _command[MAX_COMMANDS_PER_SCRIPT];
+
+ struct {
+ int32 _dwTime;
+ byte _nCmds;
+ uint32 _cmdNum[MAX_COMMANDS_PER_MOMENT];
+
+ } _moment[MAX_MOMENTS_PER_SCRIPT];
+
+} PACKED_STRUCT;
+typedef MpalScript *LpMpalScript;
+
+#include "common/pack-end.h"
+
+/****************************************************************************\
+* Function prototypes
+\****************************************************************************/
+
+/**
+ * Returns the current value of a global variable
+ *
+ * @param lpszVarName Name of the variable
+ * @returns Current value
+ * @remarks Before using this method, you must call LockVar() to
+ * lock the global variablves for use. Then afterwards, you will
+ * need to remember to call UnlockVar()
+ */
+extern int32 varGetValue(const char *lpszVarName);
+
+
+/**
+ * Sets the value of a MPAL global variable
+ * @param lpszVarName Name of the variable
+ * @param val Value to set
+ */
+extern void varSetValue(const char *lpszVarName, int32 val);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/mpalutils.cpp b/engines/tony/mpal/mpalutils.cpp
new file mode 100644
index 0000000000..92d4af37fc
--- /dev/null
+++ b/engines/tony/mpal/mpalutils.cpp
@@ -0,0 +1,115 @@
+/* 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 "tony/mpal/mpalutils.h"
+#include "tony/tony.h"
+#include "common/memstream.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* RMRes methods
+\****************************************************************************/
+
+/**
+ * Constructor
+ * @param resId MPAL resource to open
+ */
+RMRes::RMRes(uint32 resID) {
+ _h = g_vm->_resUpdate.queryResource(resID);
+ if (_h == NULL)
+ _h = mpalQueryResource(resID);
+ if (_h != NULL)
+ _buf = (byte *)globalLock(_h);
+}
+
+/**
+ * Destructor
+ */
+RMRes::~RMRes() {
+ if (_h != NULL) {
+ globalUnlock(_h);
+ globalFree(_h);
+ }
+}
+
+/**
+ * Returns a pointer to the resource
+ */
+const byte *RMRes::dataPointer() {
+ return _buf;
+}
+
+/**
+ * Returns a pointer to the resource
+ */
+RMRes::operator const byte *() {
+ return dataPointer();
+}
+
+/**
+ * Returns the size of the resource
+ */
+unsigned int RMRes::size() {
+ return globalSize(_h);
+}
+
+Common::SeekableReadStream *RMRes::getReadStream() {
+ return new Common::MemoryReadStream(_buf, size());
+}
+
+bool RMRes::isValid() {
+ return _h != NULL;
+}
+
+/****************************************************************************\
+* RMResRaw methods
+\****************************************************************************/
+
+RMResRaw::RMResRaw(uint32 resID) : RMRes(resID) {
+}
+
+RMResRaw::~RMResRaw() {
+}
+
+const byte *RMResRaw::dataPointer() {
+ return _buf + 8;
+}
+
+RMResRaw::operator const byte *() {
+ return dataPointer();
+}
+
+int RMResRaw::width() {
+ return READ_LE_UINT16(_buf + 4);
+}
+
+int RMResRaw::height() {
+ return READ_LE_UINT16(_buf + 6);
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/mpalutils.h b/engines/tony/mpal/mpalutils.h
new file mode 100644
index 0000000000..629e157e29
--- /dev/null
+++ b/engines/tony/mpal/mpalutils.h
@@ -0,0 +1,74 @@
+/* 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 TONY_MPAL_MPALUTILS
+#define TONY_MPAL_MPALUTILS
+
+#include "common/scummsys.h"
+#include "tony/mpal/memory.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Tony {
+
+namespace MPAL {
+
+class RMRes {
+protected:
+ MpalHandle _h;
+ byte *_buf;
+
+public:
+ RMRes(uint32 resID);
+ virtual ~RMRes();
+
+ // Attributes
+ unsigned int size();
+ const byte *dataPointer();
+ bool isValid();
+
+ // Casting for access to data
+ operator const byte*();
+
+ Common::SeekableReadStream *getReadStream();
+};
+
+class RMResRaw : public RMRes {
+public:
+ RMResRaw(uint32 resID);
+ virtual ~RMResRaw();
+
+ const byte *dataPointer();
+ operator const byte*();
+
+ int width();
+ int height();
+};
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/resid.h b/engines/tony/resid.h
new file mode 100644
index 0000000000..f4d2c9a4fa
--- /dev/null
+++ b/engines/tony/resid.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+// From 10500 onwards there are .OGG for inventory and scrap
+#ifndef TONY_RESID_H
+#define TONY_RESID_H
+
+
+#define RES_I_INTERFACE 10300
+#define RES_I_INTERPAL 10301
+#define RES_I_INTERPPAL 10302
+#define RES_I_INTERP1 10303
+#define RES_I_INTERP2 10304
+#define RES_I_INTERP3 10305
+#define RES_I_INTERP4 10306
+#define RES_I_INTERP5 10307
+
+#define RES_I_DLGTEXT 10350
+#define RES_I_DLGTEXTLINE 10351
+#define RES_I_DLGTEXTPAL 10352
+
+#define RES_I_MINIINTER 10360
+
+#define RES_P_PAL 10410
+#define RES_P_GO 10400
+#define RES_P_TAKE 10401
+#define RES_P_USE 10402
+#define RES_P_EXAM 10403
+#define RES_P_TALK 10404
+
+#define RES_P_PAP1 10420
+#define RES_P_PAP2 10421
+#define RES_P_PAP3 10422
+#define RES_P_PAP4 10423
+#define RES_P_FRMAP 10424
+
+#define RES_F_PAL 10700
+#define RES_F_PARL 10701
+#define RES_F_OBJ 10702
+#define RES_F_MACC 10703
+#define RES_F_CREDITS 10704
+#define RES_F_CPAL 10705
+
+#define RES_W_CIRCLE 10800
+
+#endif
diff --git a/engines/tony/sound.cpp b/engines/tony/sound.cpp
new file mode 100644
index 0000000000..2c2c280eb2
--- /dev/null
+++ b/engines/tony/sound.cpp
@@ -0,0 +1,688 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/adpcm.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/wave.h"
+#include "common/textconsole.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* FPSOUND Methods
+\****************************************************************************/
+
+/**
+ * Default constructor. Initializes the attributes.
+ *
+ */
+FPSound::FPSound() {
+ _bSoundSupported = false;
+}
+
+/**
+ * Initializes the object, and prepare everything you need to create streams and sound effects.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+bool FPSound::init() {
+ _bSoundSupported = g_system->getMixer()->isReady();
+ return _bSoundSupported;
+}
+
+/**
+ * Destroy the object and free the memory
+ *
+ */
+
+FPSound::~FPSound() {
+}
+
+/**
+ * Allocates an object of type FPStream, and return its pointer
+ *
+ * @param lplpStream Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSound::createStream(FPStream **lplpStream) {
+ (*lplpStream) = new FPStream(_bSoundSupported);
+
+ return (*lplpStream != NULL);
+}
+
+/**
+ * Allocates an object of type FpSfx, and return its pointer
+ *
+ * @param lplpSfx Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSound::createSfx(FPSfx **lplpSfx) {
+ (*lplpSfx) = new FPSfx(_bSoundSupported);
+
+ return (*lplpSfx != NULL);
+}
+
+/**
+ * Set the general volume
+ *
+ * @param dwVolume Volume to set (0-63)
+ */
+
+void FPSound::setMasterVolume(int dwVolume) {
+ if (!_bSoundSupported)
+ return;
+
+ g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(dwVolume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Get the general volume
+ *
+ * @param lpdwVolume Variable that will contain the volume (0-63)
+ */
+
+void FPSound::getMasterVolume(int *lpdwVolume) {
+ if (!_bSoundSupported)
+ return;
+
+ *lpdwVolume = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume;
+}
+
+/**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateSfx()
+ *
+ */
+
+FPSfx::FPSfx(bool bSoundOn) {
+ _bSoundSupported = bSoundOn;
+ _bFileLoaded = false;
+ _lastVolume = 63;
+ _hEndOfBuffer = CoroScheduler.createEvent(true, false);
+ _bIsVoice = false;
+ _loopStream = 0;
+ _rewindableStream = 0;
+ _bPaused = false;
+
+ g_vm->_activeSfx.push_back(this);
+}
+
+/**
+ * Default Destructor.
+ *
+ * @remarks It is also stops the sound effect that may be
+ * currently played, and free the memory it uses.
+ *
+ */
+
+FPSfx::~FPSfx() {
+ if (!_bSoundSupported)
+ return;
+
+ g_system->getMixer()->stopHandle(_handle);
+ g_vm->_activeSfx.remove(this);
+
+ if (_loopStream)
+ delete _loopStream; // _rewindableStream is deleted by deleting _loopStream
+ else
+ delete _rewindableStream;
+
+ // Free the buffer end event
+ CoroScheduler.closeEvent(_hEndOfBuffer);
+}
+
+/**
+ * Releases the memory used by the object.
+ *
+ * @remarks Must be called when the object is no longer used and
+ * **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+void FPSfx::release() {
+ delete this;
+}
+
+bool FPSfx::loadWave(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ _rewindableStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+
+ if (!_rewindableStream)
+ return false;
+
+ _bFileLoaded = true;
+ setVolume(_lastVolume);
+ return true;
+}
+
+bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) {
+ if (!_bSoundSupported)
+ return true;
+
+ uint32 size = vdbFP.readUint32LE();
+ uint32 rate = vdbFP.readUint32LE();
+ _bIsVoice = true;
+
+ _rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1);
+
+ _bFileLoaded = true;
+ setVolume(62);
+ return true;
+}
+
+/**
+ * Opens a file and loads a sound effect.
+ *
+ * @param lpszFileName Sfx filename
+ * @param dwCodec CODEC used to uncompress the samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) {
+ if (!_bSoundSupported)
+ return true;
+
+ Common::File file;
+ if (!file.open(lpszFileName)) {
+ warning("FPSfx::LoadFile(): Cannot open sfx file!");
+ return false;
+ }
+
+ if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) {
+ warning("FPSfx::LoadFile(): Invalid ADP header!");
+ return false;
+ }
+
+ uint32 rate = file.readUint32LE();
+ uint32 channels = file.readUint32LE();
+
+ Common::SeekableReadStream *buffer = file.readStream(file.size() - file.pos());
+
+ if (dwCodec == FPCODEC_ADPCM) {
+ _rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels);
+ } else {
+ byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
+
+ if (channels == 2)
+ flags |= Audio::FLAG_STEREO;
+
+ _rewindableStream = Audio::makeRawStream(buffer, rate, flags, DisposeAfterUse::YES);
+ }
+
+ _bFileLoaded = true;
+ return true;
+}
+
+/**
+ * Play the Sfx in memory.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::play() {
+ stop(); // sanity check
+
+ if (_bFileLoaded) {
+ CoroScheduler.resetEvent(_hEndOfBuffer);
+
+ _rewindableStream->rewind();
+
+ Audio::AudioStream *stream = _rewindableStream;
+
+ if (_bLoop) {
+ if (!_loopStream)
+ _loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0);
+
+ stream = _loopStream;
+ }
+
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
+ Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+ setVolume(_lastVolume);
+
+ if (_bPaused)
+ g_system->getMixer()->pauseHandle(_handle, true);
+ }
+
+ return true;
+}
+
+/**
+ * Stops a Sfx.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::stop() {
+ if (_bFileLoaded) {
+ g_system->getMixer()->stopHandle(_handle);
+ _bPaused = false;
+ }
+
+ return true;
+}
+
+/**
+ * Enables or disables the Sfx loop.
+ *
+ * @param _bLoop True to enable the loop, False to disable
+ *
+ * @remarks The loop must be activated BEFORE the sfx starts
+ * playing. Any changes made during the play will have
+ * no effect until the sfx is stopped then played again.
+ */
+
+void FPSfx::setLoop(bool bLop) {
+ _bLoop = bLop;
+}
+
+/**
+ * Pauses a Sfx.
+ *
+ */
+
+void FPSfx::pause(bool bPause) {
+ if (_bFileLoaded) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle) && (bPause ^ _bPaused))
+ g_system->getMixer()->pauseHandle(_handle, bPause);
+
+ _bPaused = bPause;
+ }
+}
+
+/**
+ * Change the volume of Sfx
+ *
+ * @param dwVolume Volume to be set (0-63)
+ *
+ */
+
+void FPSfx::setVolume(int dwVolume) {
+ if (dwVolume > 63)
+ dwVolume = 63;
+
+ if (dwVolume < 0)
+ dwVolume = 0;
+
+ _lastVolume = dwVolume;
+
+ if (_bIsVoice) {
+ if (!GLOBALS._bCfgDubbing)
+ dwVolume = 0;
+ else {
+ dwVolume -= (10 - GLOBALS._nCfgDubbingVolume) * 2;
+ if (dwVolume < 0)
+ dwVolume = 0;
+ }
+ } else {
+ if (!GLOBALS._bCfgSFX)
+ dwVolume = 0;
+ else {
+ dwVolume -= (10 - GLOBALS._nCfgSFXVolume) * 2;
+ if (dwVolume < 0)
+ dwVolume = 0;
+ }
+ }
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Gets the Sfx volume
+ *
+ * @param lpdwVolume Will contain the current Sfx volume
+ *
+ */
+
+void FPSfx::getVolume(int *lpdwVolume) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ *lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
+ else
+ *lpdwVolume = 0;
+}
+
+/**
+ * Returns true if the underlying sound has ended
+ */
+
+bool FPSfx::endOfBuffer() const {
+ return !g_system->getMixer()->isSoundHandleActive(_handle) && (!_rewindableStream || _rewindableStream->endOfData());
+}
+
+/**
+ * Continually checks to see if active sounds have finished playing
+ * Sets the event signalling the sound has ended
+ */
+void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::List<FPSfx *>::iterator i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ for (;;) {
+ // Check each active sound
+ for (_ctx->i = g_vm->_activeSfx.begin(); _ctx->i != g_vm->_activeSfx.end(); ++_ctx->i) {
+ FPSfx *sfx = *_ctx->i;
+ if (sfx->endOfBuffer())
+ CoroScheduler.setEvent(sfx->_hEndOfBuffer);
+ }
+
+ // Delay until the next check is done
+ CORO_INVOKE_1(CoroScheduler.sleep, 50);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateStream()
+ */
+FPStream::FPStream(bool bSoundOn) {
+ _bSoundSupported = bSoundOn;
+ _bFileLoaded = false;
+ _bPaused = false;
+ _bLoop = false;
+ _bDoFadeOut = false;
+ _bSyncExit = false;
+ _dwBufferSize = _dwSize = 0;
+ _lastVolume = 0;
+ _syncToPlay = NULL;
+ _loopStream = NULL;
+ _rewindableStream = NULL;
+}
+
+/**
+ * Default destructor.
+ *
+ * @remarks It calls CloseFile() if needed.
+ */
+
+FPStream::~FPStream() {
+ if (!_bSoundSupported)
+ return;
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ stop();
+
+ if (_bFileLoaded)
+ unloadFile();
+
+ _syncToPlay = NULL;
+}
+
+/**
+ * Releases the memory object.
+ *
+ * @remarks Must be called when the object is no longer used
+ * and **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+void FPStream::release() {
+ delete this;
+}
+
+/**
+ * Opens a file stream
+ *
+ * @param fileName Filename to be opened
+ * @param dwCodec CODEC to be used to uncompress samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+bool FPStream::loadFile(const Common::String &fileName, uint32 dwCodType, int nBufSize) {
+ if (!_bSoundSupported)
+ return true;
+
+ if (_bFileLoaded)
+ unloadFile();
+
+ // Save the codec type
+ _dwCodec = dwCodType;
+
+ // Open the file stream for reading
+ if (!_file.open(fileName)) {
+ // Fallback: try with an extra '0' prefix
+ if (!_file.open("0" + fileName))
+ return false;
+ }
+
+ // Save the size of the stream
+ _dwSize = _file.size();
+
+ switch (_dwCodec) {
+ case FPCODEC_RAW:
+ _rewindableStream = Audio::makeRawStream(&_file, 44100, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO, DisposeAfterUse::NO);
+ break;
+
+ case FPCODEC_ADPCM:
+ _rewindableStream = Audio::makeADPCMStream(&_file, DisposeAfterUse::NO, 0, Audio::kADPCMDVI, 44100, 2);
+ break;
+
+ default:
+ _file.close();
+ return false;
+ }
+
+ // All done
+ _bFileLoaded = true;
+ _bPaused = false;
+
+ setVolume(63);
+
+ return true;
+}
+
+/**
+ * Closes a file stream (opened or not).
+ *
+ * @returns For safety, the destructor calls unloadFile() if it has not
+ * been mentioned explicitly.
+ *
+ * @remarks It is necessary to call this function to free the
+ * memory used by the stream.
+ */
+bool FPStream::unloadFile() {
+ if (!_bSoundSupported || !_bFileLoaded)
+ return true;
+
+ assert(!g_system->getMixer()->isSoundHandleActive(_handle));
+
+ // Closes the file handle stream
+ delete _loopStream;
+ delete _rewindableStream;
+ _loopStream = NULL;
+ _rewindableStream = NULL;
+ _file.close();
+
+ // Flag that the file is no longer in memory
+ _bFileLoaded = false;
+
+ return true;
+}
+
+/**
+ * Play the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPStream::play() {
+ if (!_bSoundSupported || !_bFileLoaded)
+ return false;
+
+ stop();
+
+ _rewindableStream->rewind();
+
+ Audio::AudioStream *stream = _rewindableStream;
+
+ if (_bLoop) {
+ if (!_loopStream)
+ _loopStream = new Audio::LoopingAudioStream(_rewindableStream, 0, DisposeAfterUse::NO);
+
+ stream = _loopStream;
+ }
+
+ // FIXME: Should this be kMusicSoundType or KPlainSoundType?
+ g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+ setVolume(_lastVolume);
+ _bPaused = false;
+
+ return true;
+}
+
+/**
+ * Closes the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ *
+ */
+
+bool FPStream::stop() {
+ if (!_bSoundSupported)
+ return true;
+
+ if (!_bFileLoaded)
+ return false;
+
+ if (!g_system->getMixer()->isSoundHandleActive(_handle))
+ return false;
+
+ g_system->getMixer()->stopHandle(_handle);
+
+ _bPaused = false;
+
+ return true;
+}
+
+void FPStream::waitForSync(FPStream *toplay) {
+ // FIXME: The idea here is that you wait for this stream to reach
+ // a buffer which is a multiple of nBufSize/nSync, and then the
+ // thread stops it and immediately starts the 'toplay' stream.
+
+ stop();
+ toplay->play();
+}
+
+/**
+ * Unables or disables stream loop.
+ *
+ * @param _bLoop True enable loop, False disables it
+ *
+ * @remarks The loop must be activated BEFORE the stream starts
+ * playing. Any changes made during the play will have no
+ * effect until the stream is stopped then played again.
+ */
+void FPStream::setLoop(bool loop) {
+ _bLoop = loop;
+}
+
+/**
+ * Pause sound effect
+ *
+ * @param bPause True enables pause, False disables it
+ */
+void FPStream::pause(bool bPause) {
+ if (!_bFileLoaded)
+ return;
+
+ if (bPause == _bPaused)
+ return;
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->pauseHandle(_handle, bPause);
+
+ _bPaused = bPause;
+
+ // Trick to reset the volume after a possible new sound configuration
+ setVolume(_lastVolume);
+}
+
+/**
+ * Change the volume of the stream
+ *
+ * @param dwVolume Volume to be set (0-63)
+ *
+ */
+
+void FPStream::setVolume(int dwVolume) {
+ if (dwVolume > 63)
+ dwVolume = 63;
+
+ if (dwVolume < 0)
+ dwVolume = 0;
+
+ _lastVolume = dwVolume;
+
+ if (!GLOBALS._bCfgMusic)
+ dwVolume = 0;
+ else {
+ dwVolume -= (10 - GLOBALS._nCfgMusicVolume) * 2;
+ if (dwVolume < 0)
+ dwVolume = 0;
+ }
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Gets the volume of the stream
+ *
+ * @param lpdwVolume Variable that will contain the current volume
+ *
+ */
+
+void FPStream::getVolume(int *lpdwVolume) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ *lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
+ else
+ *lpdwVolume = 0;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/sound.h b/engines/tony/sound.h
new file mode 100644
index 0000000000..c859f781f4
--- /dev/null
+++ b/engines/tony/sound.h
@@ -0,0 +1,377 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_SOUND_H
+#define TONY_SOUND_H
+
+#include "audio/mixer.h"
+#include "common/file.h"
+#include "tony/gfxcore.h"
+#include "tony/loc.h"
+#include "tony/utils.h"
+
+namespace Audio {
+class RewindableAudioStream;
+}
+
+namespace Tony {
+
+class FPStream;
+class FPSfx;
+
+enum SoundCodecs {
+ FPCODEC_RAW,
+ FPCODEC_ADPCM
+};
+
+/**
+ * Sound driver For Tony Tough
+ */
+
+class FPSound {
+private:
+ bool _bSoundSupported;
+
+public:
+ /**
+ * Default constructor. Initializes the attributes.
+ *
+ */
+
+ FPSound();
+
+ /**
+ * Destroy the object and free the memory
+ *
+ */
+
+ ~FPSound();
+
+ /**
+ * Initializes the object, and prepare everything you need to create streams and sound effects.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool init();
+
+ /**
+ * Allocates an object of type FPStream, and return its pointer
+ *
+ * @param lplpStream Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool createStream(FPStream **lplpStream);
+
+ /**
+ * Allocates an object of type FpSfx, and return its pointer
+ *
+ * @param lplpSfx Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool createSfx(FPSfx **lplpSfx);
+
+ /**
+ * Set the general volume
+ *
+ * @param dwVolume Volume to set (0-63)
+ */
+
+ void setMasterVolume(int dwVolume);
+
+ /**
+ * Get the general volume
+ *
+ * @param lpdwVolume Variable that will contain the volume (0-63)
+ */
+
+ void getMasterVolume(int *lpdwVolume);
+};
+
+class FPSfx {
+private:
+ bool _bSoundSupported; // True if the sound is active
+ bool _bFileLoaded; // True is a file is opened
+ bool _bLoop; // True is sound effect should loop
+ int _lastVolume;
+
+ bool _bIsVoice;
+ bool _bPaused;
+
+ Audio::AudioStream *_loopStream;
+ Audio::RewindableAudioStream *_rewindableStream;
+ Audio::SoundHandle _handle;
+
+public:
+ uint32 _hEndOfBuffer;
+
+ /**
+ * Check process for whether sounds have finished playing
+ */
+ static void soundCheckProcess(CORO_PARAM, const void *param);
+
+ /**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateSfx()
+ *
+ */
+
+ FPSfx(bool bSoundOn);
+
+ /**
+ * Default Destructor.
+ *
+ * @remarks It is also stops the sound effect that may be
+ * currently played, and free the memory it uses.
+ *
+ */
+
+ ~FPSfx();
+
+ /**
+ * Releases the memory used by the object.
+ *
+ * @remarks Must be called when the object is no longer used and
+ * **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+ void release();
+
+ /**
+ * Opens a file and loads a sound effect.
+ *
+ * @param lpszFileName Sfx filename
+ * @param dwCodec CODEC used to uncompress the samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool loadFile(const char *lpszFileName, uint32 dwCodec = FPCODEC_RAW);
+ bool loadWave(Common::SeekableReadStream *stream);
+ bool loadVoiceFromVDB(Common::File &vdbFP);
+
+ /**
+ * Play the Sfx in memory.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool play();
+
+ /**
+ * Stops a Sfx.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool stop();
+
+ /**
+ * Pauses a Sfx.
+ *
+ */
+
+ void pause(bool bPause);
+
+ /**
+ * Enables or disables the Sfx loop.
+ *
+ * @param bLoop True to enable the loop, False to disable
+ *
+ * @remarks The loop must be activated BEFORE the sfx starts
+ * playing. Any changes made during the play will have
+ * no effect until the sfx is stopped then played again.
+ */
+
+ void setLoop(bool bLoop);
+
+ /**
+ * Change the volume of Sfx
+ *
+ * @param dwVolume Volume to be set (0-63)
+ *
+ */
+
+ void setVolume(int dwVolume);
+
+ /**
+ * Gets the Sfx volume
+ *
+ * @param lpdwVolume Will contain the current Sfx volume
+ *
+ */
+
+ void getVolume(int *lpdwVolume);
+
+ /**
+ * Returns true if the underlying sound has ended
+ */
+ bool endOfBuffer() const;
+};
+
+class FPStream {
+private:
+ uint32 _dwBufferSize; // Buffer size (bytes)
+ uint32 _dwSize; // Stream size (bytes)
+ uint32 _dwCodec; // CODEC used
+
+ Common::File _file; // File handle used for the stream
+
+ bool _bSoundSupported; // True if the sound is active
+ bool _bFileLoaded; // True if the file is open
+ bool _bLoop; // True if the stream should loop
+ bool _bDoFadeOut; // True if fade out is required
+ bool _bSyncExit;
+ bool _bPaused;
+ int _lastVolume;
+ FPStream *_syncToPlay;
+
+ Audio::AudioStream *_loopStream;
+ Audio::RewindableAudioStream *_rewindableStream;
+ Audio::SoundHandle _handle;
+
+public:
+
+ /**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateStream()
+ */
+
+ FPStream(bool bSoundOn);
+
+ /**
+ * Default destructor.
+ *
+ * @remarks It calls CloseFile() if needed.
+ */
+
+ ~FPStream();
+
+ /**
+ * Releases the memory object.
+ *
+ * @remarks Must be called when the object is no longer used
+ * and **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+ void release();
+
+ /**
+ * Opens a file stream
+ *
+ * @param fileName Filename to be opened
+ * @param dwCodec CODEC to be used to uncompress samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool loadFile(const Common::String &fileName, uint32 dwCodec = FPCODEC_RAW, int nSync = 2000);
+
+ /**
+ * Closes a file stream (opened or not).
+ *
+ * @returns For safety, the destructor calls unloadFile() if it has not
+ * been mentioned explicitly.
+ *
+ * @remarks It is necessary to call this function to free the
+ * memory used by the stream.
+ */
+
+ bool unloadFile();
+
+ /**
+ * Play the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool play();
+ void playFast();
+
+ /**
+ * Closes the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+ bool stop();
+ void waitForSync(FPStream *toplay);
+
+ /**
+ * Pause sound effect
+ *
+ * @param bPause True enables pause, False disables it
+ */
+
+ void pause(bool bPause);
+
+ /**
+ * Unables or disables stream loop.
+ *
+ * @param bLoop True enable loop, False disables it
+ *
+ * @remarks The loop must be activated BEFORE the stream starts
+ * playing. Any changes made during the play will have no
+ * effect until the stream is stopped then played again.
+ */
+
+ void setLoop(bool bLoop);
+
+ /**
+ * Change the volume of the stream
+ *
+ * @param dwVolume Volume to be set (0-63)
+ */
+
+ void setVolume(int dwVolume);
+
+ /**
+ * Gets the volume of the stream
+ *
+ * @param lpdwVolume Variable that will contain the current volume
+ *
+ */
+
+ void getVolume(int *lpdwVolume);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/tony.cpp b/engines/tony/tony.cpp
new file mode 100644
index 0000000000..4ffb84ced8
--- /dev/null
+++ b/engines/tony/tony.cpp
@@ -0,0 +1,795 @@
+/* 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/scummsys.h"
+#include "common/algorithm.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/installshield_cab.h"
+#include "tony/tony.h"
+#include "tony/custom.h"
+#include "tony/debugger.h"
+#include "tony/game.h"
+#include "tony/mpal/mpal.h"
+
+namespace Tony {
+
+TonyEngine *g_vm;
+
+TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Engine(syst),
+ _gameDescription(gameDesc), _randomSource("tony") {
+ g_vm = this;
+ _loadSlotNumber = -1;
+
+ // Set the up the debugger
+ _debugger = new Debugger();
+ DebugMan.addDebugChannel(kTonyDebugAnimations, "animations", "Animations debugging");
+ DebugMan.addDebugChannel(kTonyDebugActions, "actions", "Actions debugging");
+ DebugMan.addDebugChannel(kTonyDebugSound, "sound", "Sound debugging");
+ DebugMan.addDebugChannel(kTonyDebugMusic, "music", "Music debugging");
+
+ // Add folders to the search directory list
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Voices");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Roasted");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Music");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "Music/utilsfx");
+
+ // Set up load slot number
+ _initialLoadSlotNumber = -1;
+ if (ConfMan.hasKey("save_slot")) {
+ int slotNumber = ConfMan.getInt("save_slot");
+ if (slotNumber >= 0 && slotNumber <= 99)
+ _initialLoadSlotNumber = slotNumber;
+ }
+
+ // Load the ScummVM sound settings
+ syncSoundSettings();
+
+ _hEndOfFrame = 0;
+ for (int i = 0; i < 6; i++)
+ _stream[i] = NULL;
+ for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
+ _sfx[i] = NULL;
+ _utilSfx[i] = NULL;
+ }
+ _bPaused = false;
+ _bDrawLocation = false;
+ _startTime = 0;
+ _curThumbnail = NULL;
+ _bQuitNow = false;
+ _bTimeFreezed = false;
+ _nTimeFreezed = 0;
+}
+
+TonyEngine::~TonyEngine() {
+ // Close the voice database
+ closeVoiceDatabase();
+
+ // Reset the coroutine scheduler
+ CoroScheduler.reset();
+ CoroScheduler.setResourceCallback(NULL);
+
+ delete _debugger;
+}
+
+/**
+ * Run the game
+ */
+Common::Error TonyEngine::run() {
+ Common::ErrorCode result = init();
+ if (result != Common::kNoError)
+ return result;
+
+ play();
+ close();
+
+ return Common::kNoError;
+}
+
+/**
+ * Initialize the game
+ */
+Common::ErrorCode TonyEngine::init() {
+ // Load DAT file (used by font manager)
+ if (!loadTonyDat())
+ return Common::kUnknownError;
+
+ if (isCompressed()) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
+ if (!stream)
+ error("Failed to open data1.cab");
+
+ Common::Archive *cabinet = Common::makeInstallShieldArchive(stream);
+ if (!cabinet)
+ error("Failed to parse data1.cab");
+
+ SearchMan.add("data1.cab", cabinet);
+ }
+
+ _hEndOfFrame = CoroScheduler.createEvent(false, false);
+
+ _bPaused = false;
+ _bDrawLocation = true;
+ _startTime = g_system->getMillis();
+
+ // Init static class fields
+ RMText::initStatics();
+ RMTony::initStatics();
+
+ // Reset the scheduler
+ CoroScheduler.reset();
+
+ // Initialize the graphics window
+ _window.init();
+
+ // Initialize the function list
+ Common::fill(_funcList, _funcList + 300, (LPCUSTOMFUNCTION)NULL);
+ initCustomFunctionMap();
+
+ // Initializes MPAL system, passing the custom functions list
+ Common::File f;
+ if (!f.open("ROASTED.MPC"))
+ return Common::kReadingFailed;
+ f.close();
+
+ if (!mpalInit("ROASTED.MPC", "ROASTED.MPR", _funcList, _funcListStrings))
+ return Common::kUnknownError;
+
+ // Initialize the update resources
+ _resUpdate.init("ROASTED.MPU");
+
+ // Initialize the music
+ initMusic();
+
+ // Initialize the voices database
+ if (!openVoiceDatabase())
+ return Common::kReadingFailed;
+
+ // Initialize the boxes
+ _theBoxes.init();
+
+ // Link to the custom graphics engine
+ _theEngine.initCustomDll();
+ _theEngine.init();
+
+ // Allocate space for thumbnails when saving the game
+ _curThumbnail = new uint16[160 * 120];
+
+ _bQuitNow = false;
+
+ return Common::kNoError;
+}
+
+bool TonyEngine::loadTonyDat() {
+ Common::String msg;
+ Common::File in;
+
+ in.open("tony.dat");
+
+ if (!in.isOpen()) {
+ msg = "You're missing the 'tony.dat' file. Get it from the ScummVM website";
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+ return false;
+ }
+
+ // Read header
+ char buf[4+1];
+ in.read(buf, 4);
+ buf[4] = '\0';
+
+ if (strcmp(buf, "TONY")) {
+ msg = "File 'tony.dat' is corrupt. Get it from the ScummVM website";
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+ return false;
+ }
+
+ int majVer = in.readByte();
+ int minVer = in.readByte();
+
+ if ((majVer != TONY_DAT_VER_MAJ) || (minVer != TONY_DAT_VER_MIN)) {
+ msg = Common::String::format("File 'tony.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", TONY_DAT_VER_MAJ, TONY_DAT_VER_MIN, majVer, minVer);
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+
+ return false;
+ }
+
+ int expectedLangVariant = -1;
+ switch (g_vm->getLanguage()) {
+ case Common::IT_ITA:
+ case Common::EN_ANY:
+ expectedLangVariant = 0;
+ break;
+ case Common::PL_POL:
+ expectedLangVariant = 1;
+ break;
+ case Common::RU_RUS:
+ expectedLangVariant = 2;
+ break;
+ case Common::CZ_CZE:
+ expectedLangVariant = 3;
+ break;
+ case Common::FR_FRA:
+ expectedLangVariant = 4;
+ break;
+ case Common::DE_DEU:
+ expectedLangVariant = 5;
+ break;
+ default:
+ warning("Unhandled language, falling back to English/Italian fonts.");
+ expectedLangVariant = 0;
+ break;
+ }
+
+ int numVariant = in.readUint16BE();
+ if (expectedLangVariant > numVariant) {
+ msg = Common::String::format("Font variant not present in 'tony.dat'. Get it from the ScummVM website");
+ GUIErrorMessage(msg);
+ warning("%s", msg.c_str());
+
+ return false;
+ }
+
+ in.seek(in.pos() + (2 * 256 * 8 * expectedLangVariant));
+ for (int i = 0; i < 256; i++) {
+ _cTableDialog[i] = in.readSint16BE();
+ _lTableDialog[i] = in.readSint16BE();
+ _cTableMacc[i] = in.readSint16BE();
+ _lTableMacc[i] = in.readSint16BE();
+ _cTableCred[i] = in.readSint16BE();
+ _lTableCred[i] = in.readSint16BE();
+ _cTableObj[i] = in.readSint16BE();
+ _lTableObj[i] = in.readSint16BE();
+ }
+
+ return true;
+}
+
+void TonyEngine::initCustomFunctionMap() {
+ INIT_CUSTOM_FUNCTION(_funcList, _funcListStrings);
+}
+
+/**
+ * Display an error message
+ */
+void TonyEngine::GUIError(const Common::String &msg) {
+ GUIErrorMessage(msg);
+}
+
+void TonyEngine::playMusic(int nChannel, const Common::String &fname, int nFX, bool bLoop, int nSync) {
+ if (nChannel < 4) {
+ if (GLOBALS._flipflop)
+ nChannel = nChannel + 1;
+ }
+
+ switch (nFX) {
+ case 0:
+ case 1:
+ case 2:
+ _stream[nChannel]->stop();
+ _stream[nChannel]->unloadFile();
+ break;
+
+ case 22:
+ break;
+ }
+
+ if (nFX == 22) { // Sync a tempo
+ GLOBALS._curChannel = nChannel;
+ GLOBALS._nextLoop = bLoop;
+ GLOBALS._nextSync = nSync;
+ GLOBALS._nextMusic = fname;
+
+ if (GLOBALS._flipflop)
+ GLOBALS._nextChannel = nChannel - 1;
+ else
+ GLOBALS._nextChannel = nChannel + 1;
+
+ uint32 hThread = CoroScheduler.createProcess(doNextMusic, NULL, 0);
+ assert(hThread != CORO_INVALID_PID_VALUE);
+
+ } else if (nFX == 44) { // Change the channel and let the first finish
+ if (GLOBALS._flipflop)
+ GLOBALS._nextChannel = nChannel - 1;
+ else
+ GLOBALS._nextChannel = nChannel + 1;
+
+ _stream[GLOBALS._nextChannel]->stop();
+ _stream[GLOBALS._nextChannel]->unloadFile();
+
+ if (!getIsDemo()) {
+ if (!_stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync))
+ g_vm->abortGame();
+ } else {
+ _stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync);
+ }
+
+ _stream[GLOBALS._nextChannel]->setLoop(bLoop);
+ _stream[GLOBALS._nextChannel]->play();
+
+ GLOBALS._flipflop = 1 - GLOBALS._flipflop;
+ } else {
+ if (!getIsDemo()) {
+ if (!_stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync))
+ g_vm->abortGame();
+ } else {
+ _stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync);
+ }
+
+ _stream[nChannel]->setLoop(bLoop);
+ _stream[nChannel]->play();
+ }
+}
+
+void TonyEngine::doNextMusic(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::String fn;
+ CORO_END_CONTEXT(_ctx);
+
+ FPStream **streams = g_vm->_stream;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!g_vm->getIsDemo()) {
+ if (!streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync))
+ g_vm->abortGame();
+ } else {
+ streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync);
+ }
+
+ streams[GLOBALS._nextChannel]->setLoop(GLOBALS._nextLoop);
+ //streams[GLOBALS._nextChannel]->prefetch();
+
+ streams[GLOBALS._curChannel]->waitForSync(streams[GLOBALS._nextChannel]);
+
+ streams[GLOBALS._curChannel]->unloadFile();
+
+ GLOBALS._flipflop = 1 - GLOBALS._flipflop;
+
+ CORO_END_CODE;
+}
+
+void TonyEngine::playSFX(int nChannel, int nFX) {
+ if (_sfx[nChannel] == NULL)
+ return;
+
+ switch (nFX) {
+ case 0:
+ _sfx[nChannel]->setLoop(false);
+ break;
+
+ case 1:
+ _sfx[nChannel]->setLoop(true);
+ break;
+ }
+
+ _sfx[nChannel]->play();
+}
+
+void TonyEngine::stopMusic(int nChannel) {
+ if (nChannel < 4)
+ _stream[nChannel + GLOBALS._flipflop]->stop();
+ else
+ _stream[nChannel]->stop();
+}
+
+void TonyEngine::stopSFX(int nChannel) {
+ _sfx[nChannel]->stop();
+}
+
+void TonyEngine::playUtilSFX(int nChannel, int nFX) {
+ if (_utilSfx[nChannel] == NULL)
+ return;
+
+ switch (nFX) {
+ case 0:
+ _utilSfx[nChannel]->setLoop(false);
+ break;
+
+ case 1:
+ _utilSfx[nChannel]->setLoop(true);
+ break;
+ }
+
+ _utilSfx[nChannel]->setVolume(52);
+ _utilSfx[nChannel]->play();
+}
+
+void TonyEngine::stopUtilSFX(int nChannel) {
+ _utilSfx[nChannel]->stop();
+}
+
+void TonyEngine::preloadSFX(int nChannel, const char *fn) {
+ if (_sfx[nChannel] != NULL) {
+ _sfx[nChannel]->stop();
+ _sfx[nChannel]->release();
+ _sfx[nChannel] = NULL;
+ }
+
+ _theSound.createSfx(&_sfx[nChannel]);
+
+ _sfx[nChannel]->loadFile(fn, FPCODEC_ADPCM);
+}
+
+FPSfx *TonyEngine::createSFX(Common::SeekableReadStream *stream) {
+ FPSfx *sfx;
+
+ _theSound.createSfx(&sfx);
+ sfx->loadWave(stream);
+ return sfx;
+}
+
+void TonyEngine::preloadUtilSFX(int nChannel, const char *fn) {
+ if (_utilSfx[nChannel] != NULL) {
+ _utilSfx[nChannel]->stop();
+ _utilSfx[nChannel]->release();
+ _utilSfx[nChannel] = NULL;
+ }
+
+ _theSound.createSfx(&_utilSfx[nChannel]);
+
+ _utilSfx[nChannel]->loadFile(fn, FPCODEC_ADPCM);
+ _utilSfx[nChannel]->setVolume(63);
+}
+
+void TonyEngine::unloadAllSFX() {
+ for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
+ if (_sfx[i] != NULL) {
+ _sfx[i]->stop();
+ _sfx[i]->release();
+ _sfx[i] = NULL;
+ }
+ }
+}
+
+void TonyEngine::unloadAllUtilSFX() {
+ for (int i = 0; i < MAX_SFX_CHANNELS; i++) {
+ if (_utilSfx[i] != NULL) {
+ _utilSfx[i]->stop();
+ _utilSfx[i]->release();
+ _utilSfx[i] = NULL;
+ }
+ }
+}
+
+void TonyEngine::initMusic() {
+ int i;
+
+ _theSound.init();
+ _theSound.setMasterVolume(63);
+
+ for (i = 0; i < 6; i++)
+ _theSound.createStream(&_stream[i]);
+
+ for (i = 0; i < MAX_SFX_CHANNELS; i++) {
+ _sfx[i] = _utilSfx[i] = NULL;
+ }
+
+ // Preload sound effects
+ preloadUtilSFX(0, "U01.ADP"); // Reversed!!
+ preloadUtilSFX(1, "U02.ADP");
+
+ // Start check processes for sound
+ CoroScheduler.createProcess(FPSfx::soundCheckProcess, NULL);
+}
+
+void TonyEngine::closeMusic() {
+ for (int i = 0; i < 6; i++) {
+ _stream[i]->stop();
+ _stream[i]->unloadFile();
+ _stream[i]->release();
+ }
+
+ unloadAllSFX();
+ unloadAllUtilSFX();
+}
+
+void TonyEngine::pauseSound(bool bPause) {
+ _theEngine.pauseSound(bPause);
+
+ for (uint i = 0; i < 6; i++)
+ if (_stream[i])
+ _stream[i]->pause(bPause);
+
+ for (uint i = 0; i < MAX_SFX_CHANNELS; i++) {
+ if (_sfx[i])
+ _sfx[i]->pause(bPause);
+ if (_utilSfx[i])
+ _utilSfx[i]->pause(bPause);
+ }
+}
+
+void TonyEngine::setMusicVolume(int nChannel, int volume) {
+ _stream[nChannel + GLOBALS._flipflop]->setVolume(volume);
+}
+
+int TonyEngine::getMusicVolume(int nChannel) {
+ int volume;
+ _stream[nChannel + GLOBALS._flipflop]->getVolume(&volume);
+ return volume;
+}
+
+Common::String TonyEngine::getSaveStateFileName(int n) {
+ return Common::String::format("tony.%03d", n);
+}
+
+void TonyEngine::autoSave(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ Common::String buf;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ grabThumbnail();
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
+ _ctx->buf = getSaveStateFileName(0);
+ _theEngine.saveState(_ctx->buf, (byte *)_curThumbnail, "Autosave");
+
+ CORO_END_CODE;
+}
+
+void TonyEngine::saveState(int n, const char *name) {
+ Common::String buf = getSaveStateFileName(n);
+ _theEngine.saveState(buf.c_str(), (byte *)_curThumbnail, name);
+}
+
+void TonyEngine::loadState(CORO_PARAM, int n) {
+ CORO_BEGIN_CONTEXT;
+ Common::String buf;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->buf = getSaveStateFileName(n);
+ CORO_INVOKE_1(_theEngine.loadState, _ctx->buf.c_str());
+
+ CORO_END_CODE;
+}
+
+bool TonyEngine::openVoiceDatabase() {
+ char id[4];
+ uint32 numfiles;
+
+ // Open the voices database
+ if (!_vdbFP.open("voices.vdb"))
+ return false;
+
+ _vdbFP.seek(-8, SEEK_END);
+ numfiles = _vdbFP.readUint32LE();
+ _vdbFP.read(id, 4);
+
+ if (id[0] != 'V' || id[1] != 'D' || id[2] != 'B' || id[3] != '1') {
+ _vdbFP.close();
+ return false;
+ }
+
+ // Read in the index
+ _vdbFP.seek(-8 - (numfiles * VOICE_HEADER_SIZE), SEEK_END);
+
+ for (uint32 i = 0; i < numfiles; ++i) {
+ VoiceHeader vh;
+ vh._offset = _vdbFP.readUint32LE();
+ vh._code = _vdbFP.readUint32LE();
+ vh._parts = _vdbFP.readUint32LE();
+
+ _voices.push_back(vh);
+ }
+
+ return true;
+}
+
+void TonyEngine::closeVoiceDatabase() {
+ if (_vdbFP.isOpen())
+ _vdbFP.close();
+
+ if (_voices.size() > 0)
+ _voices.clear();
+}
+
+void TonyEngine::grabThumbnail() {
+ _window.grabThumbnail(_curThumbnail);
+}
+
+uint16 *TonyEngine::getThumbnail() {
+ return _curThumbnail;
+}
+
+void TonyEngine::quitGame() {
+ _bQuitNow = true;
+}
+
+void TonyEngine::openInitLoadMenu(CORO_PARAM) {
+ _theEngine.openOptionScreen(coroParam, 1);
+}
+
+void TonyEngine::openInitOptions(CORO_PARAM) {
+ _theEngine.openOptionScreen(coroParam, 2);
+}
+
+void TonyEngine::abortGame() {
+ _bQuitNow = true;
+}
+
+/**
+ * Main process for playing the game.
+ *
+ * @remarks This needs to be in a separate process, since there are some things that can briefly
+ * block the execution of process. For now, all ScummVm event handling is dispatched to within the context of this
+ * process. If it ever proves a problem, we may have to look into whether it's feasible to have it still remain
+ * in the outer 'main' process.
+ */
+void TonyEngine::playProcess(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::String fn;
+ CORO_END_CONTEXT(_ctx);
+
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Game loop. We rely on the outer main process to detect if a shutdown is required,
+ // and kill the scheudler and all the processes, including this one
+ for (;;) {
+ // If a savegame needs to be loaded, then do so
+ if (g_vm->_loadSlotNumber != -1 && GLOBALS._gfxEngine != NULL) {
+ _ctx->fn = getSaveStateFileName(g_vm->_loadSlotNumber);
+ CORO_INVOKE_1(GLOBALS._gfxEngine->loadState, _ctx->fn);
+ g_vm->_loadSlotNumber = -1;
+ }
+
+ // Wait for the next frame
+ CORO_INVOKE_1(CoroScheduler.sleep, 50);
+
+ // Call the engine to handle the next frame
+ CORO_INVOKE_1(g_vm->_theEngine.doFrame, g_vm->_bDrawLocation);
+
+ // Warns that a frame is finished
+ CoroScheduler.pulseEvent(g_vm->_hEndOfFrame);
+
+ // Handle drawing the frame
+ if (!g_vm->_bPaused) {
+ if (!g_vm->_theEngine._bWiping)
+ g_vm->_window.getNewFrame(g_vm->_theEngine, NULL);
+ else
+ g_vm->_window.getNewFrame(g_vm->_theEngine, &g_vm->_theEngine._rcWipeEllipse);
+ }
+
+ // Paint the frame onto the screen
+ g_vm->_window.repaint();
+
+ // Signal the ScummVM debugger
+ g_vm->_debugger->onFrame();
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Play the game
+ */
+void TonyEngine::play() {
+ // Create the game player process
+ CoroScheduler.createProcess(playProcess, NULL);
+
+ // Loop through calling the scheduler until it's time for the game to quit
+ while (!shouldQuit() && !_bQuitNow) {
+ // Delay for a brief amount
+ g_system->delayMillis(10);
+
+ // Call any scheduled processes
+ CoroScheduler.schedule();
+ }
+}
+
+void TonyEngine::close() {
+ closeMusic();
+ CoroScheduler.closeEvent(_hEndOfFrame);
+ _theBoxes.close();
+ _theEngine.close();
+ _window.close();
+ mpalFree();
+ freeMpc();
+ delete[] _curThumbnail;
+}
+
+void TonyEngine::freezeTime() {
+ _bTimeFreezed = true;
+ _nTimeFreezed = getTime() - _startTime;
+}
+
+void TonyEngine::unfreezeTime() {
+ _bTimeFreezed = false;
+}
+
+/**
+ * Returns the millisecond timer
+ */
+uint32 TonyEngine::getTime() {
+ return g_system->getMillis();
+}
+
+bool TonyEngine::canLoadGameStateCurrently() {
+ return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave();
+}
+bool TonyEngine::canSaveGameStateCurrently() {
+ return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave();
+}
+
+Common::Error TonyEngine::loadGameState(int slot) {
+ _loadSlotNumber = slot;
+ return Common::kNoError;
+}
+
+Common::Error TonyEngine::saveGameState(int slot, const Common::String &desc) {
+ if (!GLOBALS._gfxEngine)
+ return Common::kUnknownError;
+
+ RMGfxTargetBuffer &bigBuf = *GLOBALS._gfxEngine;
+ RMSnapshot s;
+ s.grabScreenshot(bigBuf, 4, _curThumbnail);
+
+ GLOBALS._gfxEngine->saveState(getSaveStateFileName(slot), (byte *)_curThumbnail, desc);
+ return Common::kNoError;
+}
+
+void TonyEngine::syncSoundSettings() {
+ Engine::syncSoundSettings();
+
+ GLOBALS._bCfgDubbing = !ConfMan.getBool("mute") && !ConfMan.getBool("speech_mute");
+ GLOBALS._bCfgSFX = !ConfMan.getBool("mute") && !ConfMan.getBool("sfx_mute");
+ GLOBALS._bCfgMusic = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute");
+
+ GLOBALS._nCfgDubbingVolume = ConfMan.getInt("speech_volume") * 10 / 256;
+ GLOBALS._nCfgSFXVolume = ConfMan.getInt("sfx_volume") * 10 / 256;
+ GLOBALS._nCfgMusicVolume = ConfMan.getInt("music_volume") * 10 / 256;
+
+ GLOBALS._bShowSubtitles = ConfMan.getBool("subtitles");
+ GLOBALS._nCfgTextSpeed = ConfMan.getInt("talkspeed") * 10 / 256;
+}
+
+void TonyEngine::saveSoundSettings() {
+ ConfMan.setBool("speech_mute", GLOBALS._bCfgDubbing);
+ ConfMan.setBool("sfx_mute", GLOBALS._bCfgSFX);
+ ConfMan.setBool("music_mute", GLOBALS._bCfgMusic);
+
+ ConfMan.setInt("speech_volume", GLOBALS._nCfgDubbingVolume * 256 / 10);
+ ConfMan.setInt("sfx_volume", GLOBALS._nCfgSFXVolume * 256 / 10);
+ ConfMan.setInt("music_volume", GLOBALS._nCfgMusicVolume * 256 / 10);
+
+ ConfMan.setBool("subtitles", GLOBALS._bShowSubtitles);
+ ConfMan.setInt("talkspeed", GLOBALS._nCfgTextSpeed * 256 / 10);
+}
+
+void TonyEngine::showLocation() {
+ _bDrawLocation = true;
+}
+
+void TonyEngine::hideLocation() {
+ _bDrawLocation = false;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/tony.h b/engines/tony/tony.h
new file mode 100644
index 0000000000..22090dfe51
--- /dev/null
+++ b/engines/tony/tony.h
@@ -0,0 +1,243 @@
+/* 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 TONY_H
+#define TONY_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/array.h"
+#include "common/coroutines.h"
+#include "common/error.h"
+#include "common/random.h"
+#include "common/util.h"
+#include "engines/engine.h"
+
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/memory.h"
+#include "tony/debugger.h"
+#include "tony/gfxengine.h"
+#include "tony/loc.h"
+#include "tony/utils.h"
+#include "tony/window.h"
+#include "tony/globals.h"
+
+/**
+ * This is the namespace of the Tony engine.
+ *
+ * Status of this engine: In Development
+ *
+ * Games using this engine:
+ * - Tony Tough
+ */
+namespace Tony {
+
+using namespace MPAL;
+
+class Globals;
+
+enum {
+ kTonyDebugAnimations = 1 << 0,
+ kTonyDebugActions = 1 << 1,
+ kTonyDebugSound = 1 << 2,
+ kTonyDebugMusic = 1 << 3,
+ kTonyDebugMPAL = 1 << 4
+};
+
+#define DEBUG_BASIC 1
+#define DEBUG_INTERMEDIATE 2
+#define DEBUG_DETAILED 3
+
+struct TonyGameDescription;
+
+#define MAX_SFX_CHANNELS 32
+#define TONY_DAT_VER_MAJ 0
+#define TONY_DAT_VER_MIN 1
+
+struct VoiceHeader {
+ int _offset;
+ int _code;
+ int _parts;
+};
+#define VOICE_HEADER_SIZE 12
+
+class TonyEngine : public Engine {
+private:
+ Common::ErrorCode init();
+ bool loadTonyDat();
+ void initMusic();
+ void closeMusic();
+ bool openVoiceDatabase();
+ void closeVoiceDatabase();
+ void initCustomFunctionMap();
+ static void playProcess(CORO_PARAM, const void *param);
+ static void doNextMusic(CORO_PARAM, const void *param);
+
+protected:
+ // Engine APIs
+ virtual Common::Error run();
+ virtual bool hasFeature(EngineFeature f) const;
+public:
+ LPCUSTOMFUNCTION _funcList[300];
+ Common::String _funcListStrings[300];
+ Common::RandomSource _randomSource;
+ RMResUpdate _resUpdate;
+ uint32 _hEndOfFrame;
+ Common::File _vdbFP;
+ Common::Array<VoiceHeader> _voices;
+ FPSound _theSound;
+ Common::List<FPSfx *> _activeSfx;
+ Globals _globals;
+ Debugger *_debugger;
+
+ int16 _cTableDialog[256];
+ int16 _lTableDialog[256];
+ int16 _cTableMacc[256];
+ int16 _lTableMacc[256];
+ int16 _cTableCred[256];
+ int16 _lTableCred[256];
+ int16 _cTableObj[256];
+ int16 _lTableObj[256];
+
+ enum DataDir {
+ DD_BASE = 1,
+ DD_SAVE,
+ DD_SHOTS,
+ DD_MUSIC,
+ DD_LAYER,
+ DD_UTILSFX,
+ DD_VOICES,
+ DD_BASE2
+ };
+
+ FPStream *_stream[6];
+ FPSfx *_sfx[MAX_SFX_CHANNELS];
+ FPSfx *_utilSfx[MAX_SFX_CHANNELS];
+ bool _bPaused;
+ bool _bDrawLocation;
+ int _startTime;
+ uint16 *_curThumbnail;
+ int _initialLoadSlotNumber;
+ int _loadSlotNumber;
+
+ // Bounding box list manager
+ RMGameBoxes _theBoxes;
+ RMWindow _window;
+ RMGfxEngine _theEngine;
+
+ bool _bQuitNow;
+ bool _bTimeFreezed;
+ int _nTimeFreezed;
+public:
+ TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc);
+ virtual ~TonyEngine();
+
+ const TonyGameDescription *_gameDescription;
+ uint32 getFeatures() const;
+ Common::Language getLanguage() const;
+ uint16 getVersion() const;
+ bool getIsDemo() const;
+ bool isCompressed() const;
+ RMGfxEngine *getEngine() {
+ return &_theEngine;
+ }
+ void GUIError(const Common::String &msg);
+
+ virtual bool canLoadGameStateCurrently();
+ virtual bool canSaveGameStateCurrently();
+ Common::Error loadGameState(int slot);
+ Common::Error saveGameState(int slot, const Common::String &desc);
+
+ void play();
+ void close();
+ void abortGame();
+
+ void getDataDirectory(DataDir dir, char *path);
+
+ void showLocation();
+ void hideLocation();
+
+ /**
+ * Reads the time
+ */
+ uint32 getTime();
+ void freezeTime();
+ void unfreezeTime();
+
+ // Music
+ // ******
+ void playMusic(int nChannel, const Common::String &fn, int nFX, bool bLoop, int nSync);
+ void stopMusic(int nChannel);
+
+ void playSFX(int nSfx, int nFX = 0);
+ void stopSFX(int nSfx);
+
+ void playUtilSFX(int nSfx, int nFX = 0);
+ void stopUtilSFX(int nSfx);
+
+ FPSfx *createSFX(Common::SeekableReadStream *stream);
+
+ void preloadSFX(int nSfx, const char *fn);
+ void unloadAllSFX();
+
+ void preloadUtilSFX(int nSfx, const char *fn);
+ void unloadAllUtilSFX();
+
+ /**
+ * Stop all the audio
+ */
+ void pauseSound(bool bPause);
+
+ void setMusicVolume(int nChannel, int volume);
+ int getMusicVolume(int nChannel);
+
+ /**
+ * Handle saving
+ */
+ void autoSave(CORO_PARAM);
+ void saveState(int n, const char *name);
+ void loadState(CORO_PARAM, int n);
+ static Common::String getSaveStateFileName(int n);
+
+ /**
+ * Get a thumbnail
+ */
+ void grabThumbnail();
+ uint16 *getThumbnail();
+
+ void quitGame();
+
+ void openInitLoadMenu(CORO_PARAM);
+ void openInitOptions(CORO_PARAM);
+
+ virtual void syncSoundSettings();
+ void saveSoundSettings();
+};
+
+// Global reference to the TonyEngine object
+extern TonyEngine *g_vm;
+
+#define GLOBALS g_vm->_globals
+
+} // End of namespace Tony
+
+#endif /* TONY_H */
diff --git a/engines/tony/tonychar.cpp b/engines/tony/tonychar.cpp
new file mode 100644
index 0000000000..c7fa1e4a7b
--- /dev/null
+++ b/engines/tony/tonychar.cpp
@@ -0,0 +1,1945 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/mpal/memory.h"
+#include "tony/mpal/mpalutils.h"
+#include "tony/game.h"
+#include "tony/tonychar.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+bool RMTony::_bAction = false;
+
+void RMTony::initStatics() {
+ _bAction = false;
+}
+
+RMTony::RMTony() {
+ _bShow = false;
+ _bShowShadow = false;
+ _bBodyFront = false;
+ _bActionPending = false;
+ _actionItem = NULL;
+ _action = 0;
+ _actionParm = 0;
+ _bShepherdess = false;
+ _bIsStaticTalk = false;
+ _bIsTalking = false;
+ _nPatB4Talking = 0;
+ _nTalkType = TALK_NORMAL;
+ _talkDirection = UP;
+ _nTimeLastStep = 0;
+ _hActionThread = CORO_INVALID_PID_VALUE;
+}
+
+void RMTony::waitEndOfAction(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ _bAction = false;
+
+ CORO_END_CODE;
+}
+
+RMGfxSourceBuffer *RMTony::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) {
+ RMGfxSourceBuffer8RLE *spr;
+
+ assert(_cm == CM_256);
+ spr = new RMGfxSourceBuffer8RLEByteAA;
+ spr->setAlphaBlendColor(1);
+ if (bPreRLE)
+ spr->setAlreadyCompressed();
+ return spr;
+}
+
+void RMTony::init() {
+ RMRes tony(0);
+ RMRes body(9999);
+
+ // Tony is shown by default
+ _bShow = _bShowShadow = true;
+
+ // No action pending
+ _bActionPending = false;
+ _bAction = false;
+
+ _bShepherdess = false;
+ _bIsTalking = false;
+ _bIsStaticTalk = false;
+
+ // Opens the buffer
+ Common::SeekableReadStream *ds = tony.getReadStream();
+
+ // Reads his details from the stream
+ readFromStream(*ds, true);
+
+ // Closes the buffer
+ delete ds;
+
+ // Reads Tony's body
+ ds = body.getReadStream();
+ _body.readFromStream(*ds, true);
+ delete ds;
+ _body.setPattern(0);
+
+ _nTimeLastStep = g_vm->getTime();
+}
+
+
+void RMTony::close() {
+ // Deallocation of missing item
+// _shadow.destroy();
+}
+
+void RMTony::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc) {
+ CORO_BEGIN_CONTEXT;
+ int time;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (!_nInList && _bShow)
+ bigBuf->addPrim(new RMGfxPrimitive(this));
+
+ setSpeed(GLOBALS._nCfgTonySpeed);
+
+ // Runs the normal character movement
+ _ctx->time = g_vm->getTime();
+
+ do {
+ _nTimeLastStep += (1000 / 40);
+ CORO_INVOKE_2(RMCharacter::doFrame, bigBuf, curLoc);
+ } while (_ctx->time > _nTimeLastStep + (1000 / 40));
+
+ // Check if we are at the end of a path
+ if (endOfPath() && _bActionPending) {
+ // Must perform the action on which we clicked
+ _bActionPending = false;
+ }
+
+ if (_bIsTalking || _bIsStaticTalk)
+ _body.doFrame(bigBuf, false);
+
+ CORO_END_CODE;
+}
+
+void RMTony::show() {
+ _bShow = true;
+ _bShowShadow = true;
+}
+
+void RMTony::hide(bool bShowShadow) {
+ _bShow = false;
+ _bShowShadow = bShowShadow;
+}
+
+
+void RMTony::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Call the Draw() of the parent class if Tony is visible
+ if (_bShow && _bDrawNow) {
+ if (_bBodyFront) {
+ prim->getDst().setEmpty();
+ prim->getDst().offset(-44, -134);
+ if (_bShepherdess)
+ prim->getDst().offset(1, 4);
+ CORO_INVOKE_2(RMCharacter::draw, bigBuf, prim);
+ }
+
+ if (_bIsTalking || _bIsStaticTalk) {
+ // Offest direction from scrolling
+ prim->getDst().setEmpty();
+ prim->getDst().offset(-_curScroll);
+ prim->getDst().offset(_pos);
+ prim->getDst().offset(-44, -134);
+ prim->getDst() += _nBodyOffset;
+ CORO_INVOKE_2(_body.draw, bigBuf, prim);
+ }
+
+ if (!_bBodyFront) {
+ prim->getDst().setEmpty();
+ prim->getDst().offset(-44, -134);
+ if (_bShepherdess)
+ prim->getDst().offset(0, 3);
+ CORO_INVOKE_2(RMCharacter::draw, bigBuf, prim);
+ }
+ }
+
+ CORO_END_CODE;
+}
+
+void RMTony::moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm) {
+ CORO_BEGIN_CONTEXT;
+ bool result;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Makes normal movement, but remember if you must then perform an action
+ if (item == NULL) {
+ _bActionPending = false;
+ _actionItem = NULL;
+ } else {
+ _actionItem = item;
+ _action = nAction;
+ _actionParm = nActionParm;
+ _bActionPending = true;
+ }
+
+ CORO_INVOKE_2(RMCharacter::move, dst, &_ctx->result);
+ if (!_ctx->result) {
+ _bActionPending = false;
+ _actionItem = NULL;
+ }
+
+ CORO_END_CODE;
+}
+
+
+void RMTony::executeAction(int nAction, int nActionItem, int nParm) {
+ uint32 pid;
+
+ if (nAction == TA_COMBINE) {
+ pid = mpalQueryDoAction(TA_COMBINE, nParm, nActionItem);
+
+ // If you failed the combine, we have RECEIVECOMBINE as a fallback
+ if (pid == CORO_INVALID_PID_VALUE) {
+ pid = mpalQueryDoAction(TA_RECEIVECOMBINE, nActionItem, nParm);
+
+ // If you failed with that, go with the generic
+ // @@@ CombineGive!
+ if (pid == CORO_INVALID_PID_VALUE) {
+ pid = mpalQueryDoAction(TA_COMBINE, nParm, 0);
+
+ if (pid == CORO_INVALID_PID_VALUE) {
+ pid = mpalQueryDoAction(TA_RECEIVECOMBINE, nActionItem, 0);
+ }
+ }
+ }
+ } else {
+ // Perform the action
+ pid = mpalQueryDoAction(nAction, nActionItem, 0);
+ }
+
+ if (pid != CORO_INVALID_PID_VALUE) {
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ } else if (nAction != TA_GOTO) {
+ if (nAction == TA_TALK) {
+ pid = mpalQueryDoAction(6, 1, 0);
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ } else if (nAction == TA_PERORATE) {
+ pid = mpalQueryDoAction(7, 1, 0);
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ } else {
+ pid = mpalQueryDoAction(5, 1, 0);
+ _bAction = true;
+ CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32));
+ _hActionThread = pid;
+ }
+ }
+}
+
+
+void RMTony::stopNoAction(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_bAction)
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hActionThread, CORO_INFINITE);
+
+ _bActionPending = false;
+ _actionItem = NULL;
+ CORO_INVOKE_0(stop);
+
+ CORO_END_CODE;
+}
+
+void RMTony::stop(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ uint32 pid;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (_actionItem != NULL) {
+ // Call MPAL to choose the direction
+ _ctx->pid = mpalQueryDoAction(21, _actionItem->mpalCode(), 0);
+
+ if (_ctx->pid == CORO_INVALID_PID_VALUE)
+ CORO_INVOKE_0(RMCharacter::stop);
+ else {
+ _bNeedToStop = false; // If we make the OnWhichDirection, we don't need at least after the Stop().
+ _bMoving = false;
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->pid, CORO_INFINITE); // @@@ Put an assert after 10 seconds
+ }
+ } else {
+ CORO_INVOKE_0(RMCharacter::stop);
+ }
+
+ if (!_bActionPending)
+ return;
+
+ _bActionPending = false;
+
+ executeAction(_action, _actionItem->mpalCode(), _actionParm);
+
+ _actionItem = NULL;
+
+ CORO_END_CODE;
+}
+
+
+int RMTony::getCurPattern() {
+ int nPatt = RMCharacter::getCurPattern();
+
+ if (!_bShepherdess)
+ return nPatt;
+
+ switch (nPatt) {
+ case PAT_PAST_STANDUP:
+ return PAT_STANDUP;
+ case PAT_PAST_STANDDOWN:
+ return PAT_STANDDOWN;
+ case PAT_PAST_STANDLEFT:
+ return PAT_STANDLEFT;
+ case PAT_PAST_STANDRIGHT:
+ return PAT_STANDRIGHT;
+ case PAT_PAST_WALKUP:
+ return PAT_WALKUP;
+ case PAT_PAST_WALKDOWN:
+ return PAT_WALKDOWN;
+ case PAT_PAST_WALKLEFT:
+ return PAT_WALKLEFT;
+ case PAT_PAST_WALKRIGHT:
+ return PAT_WALKRIGHT;
+ }
+
+ return nPatt;
+}
+
+void RMTony::setPattern(int nPatt, bool bPlayP0) {
+ if (_bShepherdess) {
+ switch (nPatt) {
+ case PAT_STANDUP:
+ nPatt = PAT_PAST_STANDUP;
+ break;
+ case PAT_STANDDOWN:
+ nPatt = PAT_PAST_STANDDOWN;
+ break;
+ case PAT_STANDLEFT:
+ nPatt = PAT_PAST_STANDLEFT;
+ break;
+ case PAT_STANDRIGHT:
+ nPatt = PAT_PAST_STANDRIGHT;
+ break;
+ case PAT_WALKUP:
+ nPatt = PAT_PAST_WALKUP;
+ break;
+ case PAT_WALKDOWN:
+ nPatt = PAT_PAST_WALKDOWN;
+ break;
+ case PAT_WALKLEFT:
+ nPatt = PAT_PAST_WALKLEFT;
+ break;
+ case PAT_WALKRIGHT:
+ nPatt = PAT_PAST_WALKRIGHT;
+ break;
+ }
+ }
+
+ RMCharacter::setPattern(nPatt, bPlayP0);
+}
+
+
+void RMTony::take(int nWhere, int nPart) {
+ if (nPart == 0) {
+ switch (getCurPattern()) {
+ case PAT_STANDDOWN:
+ assert(0); // Not while you're doing a StandDown
+ break;
+
+ case PAT_STANDUP:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_TAKEUP_UP1);
+ break;
+ case 1:
+ setPattern(PAT_TAKEUP_MID1);
+ break;
+ case 2:
+ setPattern(PAT_TAKEUP_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDRIGHT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_TAKERIGHT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_TAKERIGHT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_TAKERIGHT_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDLEFT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_TAKELEFT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_TAKELEFT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_TAKELEFT_DOWN1);
+ break;
+ }
+ break;
+ }
+ } else if (nPart == 1) {
+ setPattern(getCurPattern() + 1);
+ } else if (nPart == 2) {
+ switch (getCurPattern()) {
+ case PAT_TAKEUP_UP2:
+ case PAT_TAKEUP_MID2:
+ case PAT_TAKEUP_DOWN2:
+ setPattern(PAT_STANDUP);
+ break;
+
+ case PAT_TAKELEFT_UP2:
+ case PAT_TAKELEFT_MID2:
+ case PAT_TAKELEFT_DOWN2:
+ setPattern(PAT_STANDLEFT);
+ break;
+
+ case PAT_TAKERIGHT_UP2:
+ case PAT_TAKERIGHT_MID2:
+ case PAT_TAKERIGHT_DOWN2:
+ setPattern(PAT_STANDRIGHT);
+ break;
+ }
+ }
+}
+
+
+void RMTony::put(int nWhere, int nPart) {
+ if (nPart == 0) {
+ switch (getCurPattern()) {
+ case PAT_STANDDOWN:
+ break;
+
+ case PAT_STANDUP:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_PUTUP_UP1);
+ break;
+ case 1:
+ setPattern(PAT_PUTUP_MID1);
+ break;
+ case 2:
+ setPattern(PAT_PUTUP_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDRIGHT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_PUTRIGHT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_PUTRIGHT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_PUTRIGHT_DOWN1);
+ break;
+ }
+ break;
+
+ case PAT_STANDLEFT:
+ switch (nWhere) {
+ case 0:
+ setPattern(PAT_PUTLEFT_UP1);
+ break;
+ case 1:
+ setPattern(PAT_PUTLEFT_MID1);
+ break;
+ case 2:
+ setPattern(PAT_PUTLEFT_DOWN1);
+ break;
+ }
+ break;
+ }
+ } else if (nPart == 1) {
+ setPattern(getCurPattern() + 1);
+ } else if (nPart == 2) {
+ switch (getCurPattern()) {
+ case PAT_PUTUP_UP2:
+ case PAT_PUTUP_MID2:
+ case PAT_PUTUP_DOWN2:
+ setPattern(PAT_STANDUP);
+ break;
+
+ case PAT_PUTLEFT_UP2:
+ case PAT_PUTLEFT_MID2:
+ case PAT_PUTLEFT_DOWN2:
+ setPattern(PAT_STANDLEFT);
+ break;
+
+ case PAT_PUTRIGHT_UP2:
+ case PAT_PUTRIGHT_MID2:
+ case PAT_PUTRIGHT_DOWN2:
+ setPattern(PAT_STANDRIGHT);
+ break;
+ }
+ }
+}
+
+
+bool RMTony::startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat,
+ int &headLoopPat, int &bodyLoopPat) {
+ assert(!_bIsTalking);
+
+ _bIsTalking = true;
+ _nPatB4Talking = getCurPattern();
+ _nTalkType = nTalkType;
+
+ // Set the direction of speech ONLY if we are not in a static animation (since it would have already been done)
+ if (!_bIsStaticTalk) {
+ switch (_nPatB4Talking) {
+ case PAT_STANDDOWN:
+ _talkDirection = DOWN;
+ break;
+
+ case PAT_TAKELEFT_UP2:
+ case PAT_TAKELEFT_MID2:
+ case PAT_TAKELEFT_DOWN2:
+ case PAT_GETUPLEFT:
+ case PAT_STANDLEFT:
+ _talkDirection = LEFT;
+ break;
+
+ case PAT_TAKERIGHT_UP2:
+ case PAT_TAKERIGHT_MID2:
+ case PAT_TAKERIGHT_DOWN2:
+ case PAT_GETUPRIGHT:
+ case PAT_STANDRIGHT:
+ _talkDirection = RIGHT;
+ break;
+
+ case PAT_TAKEUP_UP2:
+ case PAT_TAKEUP_MID2:
+ case PAT_TAKEUP_DOWN2:
+ case PAT_STANDUP:
+ _talkDirection = UP;
+ break;
+ }
+
+ // Puts the body in front by default
+ _bBodyFront = true;
+ }
+
+ if (_bShepherdess) {
+ // Talking whilst a shepherdess
+ switch (_talkDirection) {
+ case UP:
+ setPattern(PAT_PAST_TALKUP);
+ break;
+
+ case DOWN:
+ setPattern(PAT_PAST_TALKDOWN);
+ break;
+
+ case LEFT:
+ setPattern(PAT_PAST_TALKLEFT);
+ break;
+
+ case RIGHT:
+ setPattern(PAT_PAST_TALKRIGHT);
+ break;
+ }
+ return false;
+ }
+
+ headStartPat = bodyStartPat = 0;
+ bodyLoopPat = 0;
+
+ switch (nTalkType) {
+ case TALK_NORMAL:
+ _bBodyFront = false;
+ headStartPat = 0;
+ bodyStartPat = 0;
+
+ switch (_talkDirection) {
+ case DOWN:
+ headLoopPat = PAT_TALK_DOWN;
+ bodyLoopPat = BPAT_STANDDOWN;
+ _nBodyOffset.set(4, 53);
+ break;
+
+ case LEFT:
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case RIGHT:
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case UP:
+ headLoopPat = PAT_TALK_UP;
+ bodyLoopPat = BPAT_STANDUP;
+ _nBodyOffset.set(6, 53);
+ break;
+ }
+ break;
+
+ case TALK_HIPS:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ _nBodyOffset.set(2, 42);
+ headStartPat = PAT_HEAD_UP;
+ bodyStartPat = BPAT_HIPSUP_START;
+ headLoopPat = PAT_TALK_UP;
+ bodyLoopPat = BPAT_HIPSUP_LOOP;
+ break;
+
+ case DOWN:
+ _nBodyOffset.set(2, 48);
+ headStartPat = PAT_HEAD_DOWN;
+ bodyStartPat = BPAT_HIPSDOWN_START;
+ headLoopPat = PAT_TALK_DOWN;
+ bodyLoopPat = BPAT_HIPSDOWN_LOOP;
+ break;
+
+ case LEFT:
+ _nBodyOffset.set(-3, 53);
+ headStartPat = PAT_HEAD_LEFT;
+ bodyStartPat = BPAT_HIPSLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_HIPSLEFT_LOOP;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(2, 53);
+ headStartPat = PAT_HEAD_RIGHT;
+ bodyStartPat = BPAT_HIPSRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_HIPSRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_SING:
+ _nBodyOffset.set(-10, 25);
+ headStartPat = PAT_HEAD_LEFT;
+ bodyStartPat = BPAT_SINGLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_SINGLEFT_LOOP;
+ break;
+
+ case TALK_LAUGH:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ case DOWN:
+ case LEFT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_LAUGHLEFT_LOOP;
+ bodyLoopPat = BPAT_LAUGHLEFT;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_LAUGHRIGHT_LOOP;
+ bodyLoopPat = BPAT_LAUGHRIGHT;
+ break;
+ }
+ break;
+
+ case TALK_LAUGH2:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ case DOWN:
+ case LEFT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_LAUGHLEFT_LOOP;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_LAUGHRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_LAUGHRIGHT_LOOP;
+ bodyLoopPat = BPAT_LAUGHRIGHT;
+ break;
+ }
+ break;
+
+ case TALK_INDICATE:
+ switch (_talkDirection) {
+ case UP:
+ case DOWN:
+ case LEFT:
+ _nBodyOffset.set(-4, 40);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_INDICATELEFT;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(5, 40);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_INDICATERIGHT;
+ break;
+ }
+ break;
+
+ case TALK_SCARED:
+ switch (_talkDirection) {
+ case UP:
+ _nBodyOffset.set(-4, -11);
+ headStartPat = PAT_HEAD_UP;
+ bodyStartPat = BPAT_SCAREDUP_START;
+ headLoopPat = PAT_TALK_UP;
+ bodyLoopPat = BPAT_SCAREDUP_LOOP;
+ break;
+
+ case DOWN:
+ _nBodyOffset.set(-5, 45);
+ headStartPat = PAT_SCAREDDOWN_START;
+ bodyStartPat = BPAT_SCAREDDOWN_START;
+ headLoopPat = PAT_SCAREDDOWN_LOOP;
+ bodyLoopPat = BPAT_SCAREDDOWN_LOOP;
+ break;
+
+ case RIGHT:
+ _nBodyOffset.set(-4, 41);
+ headStartPat = PAT_SCAREDRIGHT_START;
+ bodyStartPat = BPAT_SCAREDRIGHT_START;
+ headLoopPat = PAT_SCAREDRIGHT_LOOP;
+ bodyLoopPat = BPAT_SCAREDRIGHT_LOOP;
+ break;
+
+ case LEFT:
+ _nBodyOffset.set(-10, 41);
+ headStartPat = PAT_SCAREDLEFT_START;
+ bodyStartPat = BPAT_SCAREDLEFT_START;
+ headLoopPat = PAT_SCAREDLEFT_LOOP;
+ bodyLoopPat = BPAT_SCAREDLEFT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_SCARED2:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case UP:
+ bodyStartPat = BPAT_STANDUP;
+ bodyLoopPat = BPAT_STANDUP;
+ _nBodyOffset.set(6, 53);
+
+ headStartPat = PAT_HEAD_UP;
+ headLoopPat = PAT_TALK_UP;
+ break;
+
+ case DOWN:
+ bodyStartPat = BPAT_STANDDOWN;
+ bodyLoopPat = BPAT_STANDDOWN;
+ _nBodyOffset.set(4, 53);
+
+ headStartPat = PAT_SCAREDDOWN_START;
+ headLoopPat = PAT_SCAREDDOWN_LOOP;
+ break;
+
+ case RIGHT:
+ bodyStartPat = BPAT_STANDRIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDRIGHT_START;
+ headLoopPat = PAT_SCAREDRIGHT_LOOP;
+ break;
+
+ case LEFT:
+ bodyStartPat = BPAT_STANDLEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDLEFT_START;
+ headLoopPat = PAT_SCAREDLEFT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHGLASSES:
+ _nBodyOffset.set(4, 53);
+ headLoopPat = PAT_TALK_DOWN;
+ bodyLoopPat = BPAT_GLASS;
+ break;
+ case TALK_WITHWORM:
+ _nBodyOffset.set(9, 56);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WORM;
+ break;
+ case TALK_WITHHAMMER:
+ _nBodyOffset.set(6, 56);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_HAMMER;
+ break;
+ case TALK_WITHROPE:
+ _nBodyOffset.set(-3, 38);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_ROPE;
+ break;
+ case TALK_WITHSECRETARY:
+ _nBodyOffset.set(-17, 12);
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHSECRETARY;
+ break;
+
+ case TALK_WITHRABBIT:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-21, -5);
+ bodyStartPat = BPAT_WITHRABBITLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHRABBITLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -5);
+ bodyStartPat = BPAT_WITHRABBITRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHRABBITRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPE:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-61, -7);
+ bodyStartPat = BPAT_WITHRECIPELEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHRECIPELEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-5, -7);
+ bodyStartPat = BPAT_WITHRECIPERIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHRECIPERIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDS:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-34, -2);
+ bodyStartPat = BPAT_WITHCARDSLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHCARDSLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -2);
+ bodyStartPat = BPAT_WITHCARDSRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHCARDSRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMAN:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(-35, 2);
+ bodyStartPat = BPAT_WITHSNOWMANLEFT_START;
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_WITHSNOWMANLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-14, 2);
+ bodyStartPat = BPAT_WITHSNOWMANRIGHT_START;
+ headLoopPat = PAT_TALK_RIGHT;
+ bodyLoopPat = BPAT_WITHSNOWMANRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMANSTATIC:
+ case TALK_WITHRECIPESTATIC:
+ case TALK_WITHRABBITSTATIC:
+ case TALK_WITHCARDSSTATIC:
+ case TALK_WITH_NOTEBOOK:
+ case TALK_WITHMEGAPHONESTATIC:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ headLoopPat = PAT_TALK_LEFT;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headLoopPat = PAT_TALK_RIGHT;
+ break;
+ }
+ break;
+
+ // The beard is the only case in which the head is animated separately while the body is the standard
+ case TALK_WITHBEARDSTATIC:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ headLoopPat = PAT_TALKBEARD_LEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headLoopPat = PAT_TALKBEARD_RIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+ break;
+ }
+ break;
+
+ case TALK_DISGUSTED:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_DISGUSTEDLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_DISGUSTEDLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_DISGUSTEDRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_DISGUSTEDRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_SARCASTIC:
+ switch (_talkDirection) {
+ case LEFT:
+ case UP:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_SARCASTICLEFT_START;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_SARCASTICLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(6, 56);
+ headStartPat = PAT_SARCASTICRIGHT_START;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_SARCASTICRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_MACBETH1:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH1;
+ break;
+ case TALK_MACBETH2:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH2;
+ break;
+ case TALK_MACBETH3:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH3;
+ break;
+ case TALK_MACBETH4:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH4;
+ break;
+ case TALK_MACBETH5:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH5;
+ break;
+ case TALK_MACBETH6:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH6;
+ break;
+ case TALK_MACBETH7:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH7;
+ break;
+ case TALK_MACBETH8:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH8;
+ break;
+ case TALK_MACBETH9:
+ _nBodyOffset.set(-33, -1);
+ headLoopPat = PAT_TALK_LEFT;
+ bodyLoopPat = BPAT_MACBETH9;
+ break;
+
+ case TALK_SCAREDSTATIC:
+ _bBodyFront = false;
+ switch (_talkDirection) {
+ case DOWN:
+ bodyStartPat = BPAT_STANDDOWN;
+ bodyLoopPat = BPAT_STANDDOWN;
+ _nBodyOffset.set(4, 53);
+
+ headStartPat = PAT_SCAREDDOWN_STAND;
+ headLoopPat = PAT_SCAREDDOWN_LOOP;
+ break;
+
+ case RIGHT:
+ bodyStartPat = BPAT_STANDRIGHT;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDRIGHT_STAND;
+ headLoopPat = PAT_SCAREDRIGHT_LOOP;
+ break;
+
+ case LEFT:
+ bodyStartPat = BPAT_STANDLEFT;
+ bodyLoopPat = BPAT_STANDLEFT;
+ _nBodyOffset.set(6, 56);
+
+ headStartPat = PAT_SCAREDLEFT_STAND;
+ headLoopPat = PAT_SCAREDLEFT_LOOP;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ return true;
+}
+
+void RMTony::startTalk(CORO_PARAM, CharacterTalkType nTalkType) {
+ CORO_BEGIN_CONTEXT;
+ int headStartPat, bodyStartPat;
+ int headLoopPat, bodyLoopPat;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->headStartPat = _ctx->bodyStartPat = 0;
+ _ctx->headLoopPat = _ctx->bodyLoopPat = 0;
+
+ if (!startTalkCalculate(nTalkType, _ctx->headStartPat, _ctx->bodyStartPat,
+ _ctx->headLoopPat, _ctx->bodyLoopPat))
+ return;
+
+ // Perform the set pattern
+ if (_ctx->headStartPat != 0 || _ctx->bodyStartPat != 0) {
+ setPattern(_ctx->headStartPat);
+ _body.setPattern(_ctx->bodyStartPat);
+
+ if (_ctx->bodyStartPat != 0)
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ if (_ctx->headStartPat != 0)
+ CORO_INVOKE_0(waitForEndPattern);
+ }
+
+ setPattern(_ctx->headLoopPat);
+ if (_ctx->bodyLoopPat)
+ _body.setPattern(_ctx->bodyLoopPat);
+
+ CORO_END_CODE;
+}
+
+
+bool RMTony::endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic) {
+ bodyEndPat = 0;
+ headEndPat = 0;
+
+ switch (_talkDirection) {
+ case UP:
+ finalPat = PAT_STANDUP;
+ headStandPat = PAT_HEAD_UP;
+ break;
+
+ case DOWN:
+ finalPat = PAT_STANDDOWN;
+ headStandPat = PAT_HEAD_DOWN;
+ break;
+
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ headStandPat = PAT_HEAD_LEFT;
+ break;
+
+ case RIGHT:
+ finalPat = PAT_STANDRIGHT;
+ headStandPat = PAT_HEAD_RIGHT;
+ break;
+ }
+
+ if (_bShepherdess) {
+ setPattern(finalPat);
+ _bIsTalking = false;
+ return false;
+ }
+
+ bStatic = false;
+ switch (_nTalkType) {
+ case TALK_NORMAL:
+ bodyEndPat = 0;
+ break;
+
+ case TALK_HIPS:
+ switch (_talkDirection) {
+ case UP:
+ bodyEndPat = BPAT_HIPSUP_END;
+ break;
+
+ case DOWN:
+ bodyEndPat = BPAT_HIPSDOWN_END;
+ break;
+
+ case LEFT:
+ bodyEndPat = BPAT_HIPSLEFT_END;
+ break;
+
+ case RIGHT:
+ bodyEndPat = BPAT_HIPSRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_SING:
+ bodyEndPat = BPAT_SINGLEFT_END;
+ break;
+
+ case TALK_LAUGH:
+ case TALK_LAUGH2:
+ if (_talkDirection == LEFT)
+ headEndPat = PAT_LAUGHLEFT_END;
+ else if (_talkDirection == RIGHT)
+ headEndPat = PAT_LAUGHRIGHT_END;
+
+ bodyEndPat = 0;
+ break;
+
+ case TALK_DISGUSTED:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ headEndPat = PAT_DISGUSTEDLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headEndPat = PAT_DISGUSTEDRIGHT_END;
+ break;
+ }
+
+ bodyEndPat = 0;
+ break;
+
+ case TALK_SARCASTIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ headEndPat = PAT_SARCASTICLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ headEndPat = PAT_SARCASTICRIGHT_END;
+ break;
+ }
+
+ bodyEndPat = 0;
+ break;
+
+ case TALK_INDICATE:
+ break;
+
+ case TALK_SCARED:
+ switch (_talkDirection) {
+ case UP:
+ bodyEndPat = BPAT_SCAREDUP_END;
+ break;
+
+ case DOWN:
+ headEndPat = PAT_SCAREDDOWN_END;
+ bodyEndPat = BPAT_SCAREDDOWN_END;
+ break;
+
+ case RIGHT:
+ headEndPat = PAT_SCAREDRIGHT_END;
+ bodyEndPat = BPAT_SCAREDRIGHT_END;
+ break;
+
+ case LEFT:
+ headEndPat = PAT_SCAREDLEFT_END;
+ bodyEndPat = BPAT_SCAREDLEFT_END;
+ break;
+ }
+ break;
+
+ case TALK_SCARED2:
+ switch (_talkDirection) {
+ case UP:
+ bodyEndPat = 0;
+ break;
+
+ case DOWN:
+ headEndPat = PAT_SCAREDDOWN_END;
+ bodyEndPat = 0;
+ break;
+
+ case RIGHT:
+ headEndPat = PAT_SCAREDRIGHT_END;
+ bodyEndPat = 0;
+ break;
+
+ case LEFT:
+ headEndPat = PAT_SCAREDLEFT_END;
+ bodyEndPat = 0;
+ break;
+ }
+ break;
+
+ case TALK_WITHRABBIT:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHRABBITLEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHRABBITRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPE:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHRECIPELEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHRECIPERIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDS:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHCARDSLEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHCARDSRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMAN:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ bodyEndPat = BPAT_WITHSNOWMANLEFT_END;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ bodyEndPat = BPAT_WITHSNOWMANRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHWORM:
+ finalPat = PAT_WITHWORM;
+ break;
+ case TALK_WITHROPE:
+ finalPat = PAT_WITHROPE;
+ break;
+ case TALK_WITHSECRETARY:
+ finalPat = PAT_WITHSECRETARY;
+ break;
+ case TALK_WITHHAMMER:
+ finalPat = PAT_WITHHAMMER;
+ break;
+ case TALK_WITHGLASSES:
+ finalPat = PAT_WITHGLASSES;
+ break;
+
+ case TALK_MACBETH1:
+ case TALK_MACBETH2:
+ case TALK_MACBETH3:
+ case TALK_MACBETH4:
+ case TALK_MACBETH5:
+ case TALK_MACBETH6:
+ case TALK_MACBETH7:
+ case TALK_MACBETH8:
+ finalPat = 0;
+ break;
+
+ case TALK_SCAREDSTATIC:
+ switch (_talkDirection) {
+ case DOWN:
+ headStandPat = PAT_SCAREDDOWN_STAND;
+ bodyEndPat = 0;
+ break;
+
+ case RIGHT:
+ headStandPat = PAT_SCAREDRIGHT_STAND;
+ bodyEndPat = 0;
+ break;
+
+ case LEFT:
+ headStandPat = PAT_SCAREDLEFT_STAND;
+ bodyEndPat = 0;
+ break;
+
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void RMTony::endTalk(CORO_PARAM) {
+ CORO_BEGIN_CONTEXT;
+ int headStandPat, headEndPat;
+ int bodyEndPat, finalPat;
+ bool bStatic;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->headStandPat = _ctx->headEndPat = 0;
+ _ctx->bodyEndPat = _ctx->finalPat = 0;
+ _ctx->bStatic = false;
+
+ _ctx->bodyEndPat = 0;
+ _ctx->headEndPat = 0;
+
+ if (!endTalkCalculate(_ctx->headStandPat, _ctx->headEndPat, _ctx->bodyEndPat, _ctx->finalPat, _ctx->bStatic))
+ return;
+
+ // Handles the end of an animated and static, leaving everything unchanged
+ if (_bIsStaticTalk) {
+ if (_nTalkType == TALK_WITHBEARDSTATIC) {
+ setPattern(0);
+ if (_talkDirection == UP || _talkDirection == LEFT) {
+ _body.setPattern(BPAT_WITHBEARDLEFT_STATIC);
+ _nBodyOffset.set(-41, -14);
+ } else if (_talkDirection == DOWN || _talkDirection == RIGHT) {
+ _body.setPattern(BPAT_WITHBEARDRIGHT_STATIC);
+ _nBodyOffset.set(-26, -14);
+ }
+ } else {
+ setPattern(_ctx->headStandPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ }
+
+ _bIsTalking = false;
+ return;
+ }
+
+ // Set the pattern
+ if (_ctx->headEndPat != 0 && _ctx->bodyEndPat != 0) {
+ setPattern(_ctx->headEndPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+
+ _body.setPattern(_ctx->bodyEndPat);
+
+ CORO_INVOKE_0(waitForEndPattern);
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ } else if (_ctx->bodyEndPat != 0) {
+ setPattern(_ctx->headStandPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+
+ _body.setPattern(_ctx->bodyEndPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ } else if (_ctx->headEndPat != 0) {
+ CORO_INVOKE_0(_body.waitForEndPattern);
+
+ setPattern(_ctx->headEndPat);
+
+ CORO_INVOKE_0(waitForEndPattern);
+ } else {
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ }
+
+ if (_ctx->finalPat != 0) {
+ _body.setPattern(0);
+ setPattern(_ctx->finalPat);
+ }
+
+ _bIsTalking = false;
+
+ CORO_END_CODE;
+}
+
+void RMTony::startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat,
+ int &bodyStartPat, int &bodyLoopPat) {
+ int nPat = getCurPattern();
+
+ headLoopPat = -1;
+
+ switch (nPat) {
+ case PAT_STANDDOWN:
+ _talkDirection = DOWN;
+ headPat = PAT_HEAD_RIGHT;
+ break;
+
+ case PAT_TAKELEFT_UP2:
+ case PAT_TAKELEFT_MID2:
+ case PAT_TAKELEFT_DOWN2:
+ case PAT_GETUPLEFT:
+ case PAT_STANDLEFT:
+ _talkDirection = LEFT;
+ headPat = PAT_HEAD_LEFT;
+ break;
+
+ case PAT_TAKERIGHT_UP2:
+ case PAT_TAKERIGHT_MID2:
+ case PAT_TAKERIGHT_DOWN2:
+ case PAT_GETUPRIGHT:
+ case PAT_STANDRIGHT:
+ _talkDirection = RIGHT;
+ headPat = PAT_HEAD_RIGHT;
+ break;
+
+ case PAT_TAKEUP_UP2:
+ case PAT_TAKEUP_MID2:
+ case PAT_TAKEUP_DOWN2:
+ case PAT_STANDUP:
+ _talkDirection = UP;
+ headPat = PAT_HEAD_LEFT;
+ break;
+ }
+
+ _bBodyFront = true;
+
+ switch (nTalk) {
+ case TALK_WITHRABBITSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-21, -5);
+ bodyStartPat = BPAT_WITHRABBITLEFT_START;
+ bodyLoopPat = BPAT_WITHRABBITLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -5);
+ bodyStartPat = BPAT_WITHRABBITRIGHT_START;
+ bodyLoopPat = BPAT_WITHRABBITRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDSSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-34, -2);
+ bodyStartPat = BPAT_WITHCARDSLEFT_START;
+ bodyLoopPat = BPAT_WITHCARDSLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-4, -2);
+ bodyStartPat = BPAT_WITHCARDSRIGHT_START;
+ bodyLoopPat = BPAT_WITHCARDSRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-61, -7);
+ bodyStartPat = BPAT_WITHRECIPELEFT_START;
+ bodyLoopPat = BPAT_WITHRECIPELEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-5, -7);
+ bodyStartPat = BPAT_WITHRECIPERIGHT_START;
+ bodyLoopPat = BPAT_WITHRECIPERIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHSNOWMANSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-35, 2);
+ bodyStartPat = BPAT_WITHSNOWMANLEFT_START;
+ bodyLoopPat = BPAT_WITHSNOWMANLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-14, 2);
+ bodyStartPat = BPAT_WITHSNOWMANRIGHT_START;
+ bodyLoopPat = BPAT_WITHSNOWMANRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITH_NOTEBOOK:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-16, -9);
+ bodyStartPat = BPAT_WITHNOTEBOOKLEFT_START;
+ bodyLoopPat = BPAT_WITHNOTEBOOKLEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-6, -9);
+ bodyStartPat = BPAT_WITHNOTEBOOKRIGHT_START;
+ bodyLoopPat = BPAT_WITHNOTEBOOKRIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHMEGAPHONESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-41, -8);
+ bodyStartPat = BPAT_WITHMEGAPHONELEFT_START;
+ bodyLoopPat = BPAT_WITHMEGAPHONELEFT_LOOP;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-14, -8);
+ bodyStartPat = BPAT_WITHMEGAPHONERIGHT_START;
+ bodyLoopPat = BPAT_WITHMEGAPHONERIGHT_LOOP;
+ break;
+ }
+ break;
+
+ case TALK_WITHBEARDSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ _nBodyOffset.set(-41, -14);
+ bodyStartPat = BPAT_WITHBEARDLEFT_START;
+ bodyLoopPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_TALKBEARD_LEFT;
+ headPat = 0;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ _nBodyOffset.set(-26, -14);
+ bodyStartPat = BPAT_WITHBEARDRIGHT_START;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_TALKBEARD_RIGHT;
+ headPat = 0;
+ break;
+ }
+ break;
+
+ case TALK_SCAREDSTATIC:
+ switch (_talkDirection) {
+ case DOWN:
+ headPat = PAT_SCAREDDOWN_START;
+ bodyLoopPat = BPAT_STANDDOWN;
+ bodyStartPat = BPAT_STANDDOWN;
+ headLoopPat = PAT_SCAREDDOWN_STAND;
+ _nBodyOffset.set(4, 53);
+ break;
+
+ case LEFT:
+ headPat = PAT_SCAREDLEFT_START;
+ bodyLoopPat = BPAT_STANDLEFT;
+ bodyStartPat = BPAT_STANDLEFT;
+ headLoopPat = PAT_SCAREDLEFT_STAND;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ case RIGHT:
+ headPat = PAT_SCAREDRIGHT_START;
+ bodyLoopPat = BPAT_STANDRIGHT;
+ bodyStartPat = BPAT_STANDRIGHT;
+ headLoopPat = PAT_SCAREDRIGHT_STAND;
+ _nBodyOffset.set(6, 56);
+ break;
+
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void RMTony::startStatic(CORO_PARAM, CharacterTalkType nTalk) {
+ CORO_BEGIN_CONTEXT;
+ int headPat, headLoopPat;
+ int bodyStartPat, bodyLoopPat;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->headPat = _ctx->headLoopPat = 0;
+ _ctx->bodyStartPat = _ctx->bodyLoopPat = 0;
+
+ startStaticCalculate(nTalk, _ctx->headPat, _ctx->headLoopPat,
+ _ctx->bodyStartPat, _ctx->bodyLoopPat);
+
+ // e vai con i pattern
+ _bIsStaticTalk = true;
+
+ setPattern(_ctx->headPat);
+ _body.setPattern(_ctx->bodyStartPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ CORO_INVOKE_0(waitForEndPattern);
+
+ if (_ctx->headLoopPat != -1)
+ setPattern(_ctx->headLoopPat);
+ _body.setPattern(_ctx->bodyLoopPat);
+
+ CORO_END_CODE;
+}
+
+void RMTony::endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat) {
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ finalPat = PAT_STANDLEFT;
+ break;
+
+ case RIGHT:
+ case DOWN:
+ finalPat = PAT_STANDRIGHT;
+ break;
+ }
+
+ switch (nTalk) {
+ case TALK_WITHSNOWMANSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHSNOWMANLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHSNOWMANRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHRECIPESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHRECIPELEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHRECIPERIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHRABBITSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHRABBITLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHRABBITRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHCARDSSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHCARDSLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHCARDSRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITH_NOTEBOOK:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHNOTEBOOKLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHNOTEBOOKRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHMEGAPHONESTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHMEGAPHONELEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHMEGAPHONERIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_WITHBEARDSTATIC:
+ switch (_talkDirection) {
+ case UP:
+ case LEFT:
+ bodyEndPat = BPAT_WITHBEARDLEFT_END;
+ break;
+
+ case DOWN:
+ case RIGHT:
+ bodyEndPat = BPAT_WITHBEARDRIGHT_END;
+ break;
+ }
+ break;
+
+ case TALK_SCAREDSTATIC:
+ switch (_talkDirection) {
+ case LEFT:
+ headEndPat = PAT_SCAREDLEFT_END;
+ break;
+
+ case DOWN:
+ headEndPat = PAT_SCAREDDOWN_END;
+ break;
+
+ case RIGHT:
+ headEndPat = PAT_SCAREDRIGHT_END;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void RMTony::endStatic(CORO_PARAM, CharacterTalkType nTalk) {
+ CORO_BEGIN_CONTEXT;
+ int bodyEndPat;
+ int finalPat;
+ int headEndPat;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->bodyEndPat = 0;
+ _ctx->finalPat = 0;
+ _ctx->headEndPat = 0;
+
+ endStaticCalculate(nTalk, _ctx->bodyEndPat, _ctx->finalPat, _ctx->headEndPat);
+
+ if (_ctx->headEndPat != 0) {
+ setPattern(_ctx->headEndPat);
+
+ CORO_INVOKE_0(waitForEndPattern);
+ } else {
+ // Play please
+ _body.setPattern(_ctx->bodyEndPat);
+
+ CORO_INVOKE_0(_body.waitForEndPattern);
+ }
+
+ setPattern(_ctx->finalPat);
+ _body.setPattern(0);
+
+ _bIsStaticTalk = false;
+
+ CORO_END_CODE;
+}
+
+/**
+ * Waits until the end of a pattern
+ */
+void RMTony::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) {
+ RMCharacter::waitForEndPattern(coroParam, hCustomSkip);
+}
+
+/**
+ * Check if currently in an action
+ */
+bool RMTony::inAction() {
+ return (_bActionPending && _action != 0) | _bAction;
+}
+
+/**
+ * Check if there needs to be an update for scrolling movement
+ */
+bool RMTony::mustUpdateScrolling() {
+ return ((!inAction()) || (isMoving()));
+}
+
+/**
+ * Returns Tony's position
+ */
+RMPoint RMTony::position() {
+ return _pos;
+}
+
+/**
+ * Set the scrolling position
+ */
+void RMTony::setScrollPosition(const RMPoint &pt) {
+ RMCharacter::setScrollPosition(pt);
+}
+
+/**
+ * Tony disguises himself!
+ */
+void RMTony::setShepherdess(bool bIsPast) {
+ _bShepherdess = bIsPast;
+}
+
+int RMTony::getShepherdess() {
+ return _bShepherdess;
+}
+
+void RMTony::playSfx(int nSfx) {
+ RMItem::playSfx(nSfx);
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/tonychar.h b/engines/tony/tonychar.h
new file mode 100644
index 0000000000..d9f18f61ec
--- /dev/null
+++ b/engines/tony/tonychar.h
@@ -0,0 +1,482 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_TONYCHAR_H
+#define TONY_TONYCHAR_H
+
+#include "common/coroutines.h"
+#include "tony/loc.h"
+
+namespace Tony {
+
+class RMTony : public RMCharacter {
+private:
+ enum CharacterDirection {
+ UP, DOWN, LEFT, RIGHT
+ };
+
+public:
+ enum CharacterTalkType {
+ TALK_NORMAL,
+ TALK_HIPS,
+ TALK_SING,
+ TALK_LAUGH,
+ TALK_INDICATE,
+ TALK_SCARED,
+ TALK_SCARED2,
+ TALK_WITHGLASSES,
+ TALK_WITHHAMMER,
+ TALK_WITHWORM,
+ TALK_WITHROPE,
+ TALK_WITHRABBIT,
+ TALK_WITHRECIPE,
+ TALK_WITHCARDS,
+ TALK_WITHSNOWMAN,
+ TALK_WITHSNOWMANSTATIC,
+ TALK_WITHRABBITSTATIC,
+ TALK_WITHRECIPESTATIC,
+ TALK_WITHCARDSSTATIC,
+ TALK_WITH_NOTEBOOK,
+ TALK_WITHMEGAPHONESTATIC,
+ TALK_WITHBEARDSTATIC,
+ TALK_LAUGH2,
+ TALK_DISGUSTED,
+ TALK_SARCASTIC,
+ TALK_MACBETH1,
+ TALK_MACBETH2,
+ TALK_MACBETH3,
+ TALK_MACBETH4,
+ TALK_MACBETH5,
+ TALK_MACBETH6,
+ TALK_MACBETH7,
+ TALK_MACBETH8,
+ TALK_MACBETH9,
+ TALK_SCAREDSTATIC,
+ TALK_WITHSECRETARY
+ };
+
+private:
+ bool _bShow;
+ bool _bShowShadow;
+ bool _bBodyFront;
+ // Useless variable?
+ // RMGfxSourceBuffer8AB _shadow;
+ bool _bActionPending;
+ RMItem *_actionItem;
+ int _action;
+ int _actionParm;
+ static bool _bAction;
+
+ bool _bShepherdess;
+
+ bool _bIsStaticTalk;
+ bool _bIsTalking;
+ int _nPatB4Talking;
+ CharacterTalkType _nTalkType;
+ CharacterDirection _talkDirection;
+ RMPoint _nBodyOffset;
+
+ int _nTimeLastStep;
+
+ RMItem _body;
+ uint32 _hActionThread;
+
+protected:
+ /**
+ * Overload of the allocation allocation of sprites
+ */
+ virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE);
+
+ /**
+ * Watch thread which waits for the end of an action
+ */
+ static void waitEndOfAction(CORO_PARAM, const void *param);
+
+public:
+ enum CharacterPatterns {
+ PAT_TAKEUP_UP1 = 9,
+ PAT_TAKEUP_UP2,
+ PAT_TAKEUP_MID1,
+ PAT_TAKEUP_MID2,
+ PAT_TAKEUP_DOWN1,
+ PAT_TAKEUP_DOWN2,
+
+ PAT_TAKELEFT_UP1,
+ PAT_TAKELEFT_UP2,
+ PAT_TAKELEFT_MID1,
+ PAT_TAKELEFT_MID2,
+ PAT_TAKELEFT_DOWN1,
+ PAT_TAKELEFT_DOWN2,
+
+ PAT_TAKERIGHT_UP1,
+ PAT_TAKERIGHT_UP2,
+ PAT_TAKERIGHT_MID1,
+ PAT_TAKERIGHT_MID2,
+ PAT_TAKERIGHT_DOWN1,
+ PAT_TAKERIGHT_DOWN2,
+
+ PAT_GETUPLEFT,
+ PAT_ONTHEFLOORLEFT,
+ PAT_GETUPRIGHT,
+ PAT_ONTHEFLOORRIGHT,
+
+ // Sheperdess!
+ PAT_PAST_WALKUP,
+ PAT_PAST_WALKDOWN,
+ PAT_PAST_WALKLEFT,
+ PAT_PAST_WALKRIGHT,
+
+ PAT_PAST_STANDUP,
+ PAT_PAST_STANDDOWN,
+ PAT_PAST_STANDLEFT,
+ PAT_PAST_STANDRIGHT,
+
+ // Speech
+ PAT_TALK_UP,
+ PAT_TALK_DOWN,
+ PAT_TALK_LEFT,
+ PAT_TALK_RIGHT,
+
+ // Static head
+ PAT_HEAD_UP,
+ PAT_HEAD_DOWN,
+ PAT_HEAD_LEFT,
+ PAT_HEAD_RIGHT,
+
+ // Laugh
+ PAT_LAUGHLEFT_START,
+ PAT_LAUGHLEFT_LOOP,
+ PAT_LAUGHLEFT_END,
+ PAT_LAUGHRIGHT_START,
+ PAT_LAUGHRIGHT_LOOP,
+ PAT_LAUGHRIGHT_END,
+
+ // Speaking as a shepherdess
+ PAT_PAST_TALKUP,
+ PAT_PAST_TALKDOWN,
+ PAT_PAST_TALKLEFT,
+ PAT_PAST_TALKRIGHT,
+
+ // Fear
+ PAT_SCAREDLEFT_START,
+ PAT_SCAREDLEFT_LOOP,
+ PAT_SCAREDLEFT_END,
+ PAT_SCAREDRIGHT_START,
+ PAT_SCAREDRIGHT_LOOP,
+ PAT_SCAREDRIGHT_END,
+ PAT_SCAREDDOWN_START,
+ PAT_SCAREDDOWN_LOOP,
+ PAT_SCAREDDOWN_END,
+
+ // With objects: full body
+ PAT_WITHGLASSES,
+ PAT_WITHROPE,
+ PAT_WITHWORM,
+ PAT_WITHHAMMER,
+
+ // Sound the whistle
+ PAT_WHISTLERIGHT,
+
+ // Head with beard
+ PAT_TALKBEARD_LEFT,
+ PAT_TALKBEARD_RIGHT,
+
+ // Sniff
+ PAT_SNIFF_LEFT,
+ PAT_SNIFF_RIGHT,
+
+ // Disgusted
+ PAT_DISGUSTEDLEFT_START,
+ PAT_DISGUSTEDLEFT_LOOP,
+ PAT_DISGUSTEDLEFT_END,
+ PAT_DISGUSTEDRIGHT_START,
+ PAT_DISGUSTEDRIGHT_LOOP,
+ PAT_DISGUSTEDRIGHT_END,
+ PAT_SARCASTICLEFT_START,
+ PAT_SARCASTICLEFT_LOOP,
+ PAT_SARCASTICLEFT_END,
+ PAT_SARCASTICRIGHT_START,
+ PAT_SARCASTICRIGHT_LOOP,
+ PAT_SARCASTICRIGHT_END,
+
+ // Stand scared
+ PAT_SCAREDLEFT_STAND,
+ PAT_SCAREDRIGHT_STAND,
+ PAT_SCAREDDOWN_STAND,
+
+ PAT_PUTLEFT_UP1,
+ PAT_PUTLEFT_UP2,
+ PAT_PUTRIGHT_UP1,
+ PAT_PUTRIGHT_UP2,
+ PAT_PUTLEFT_MID1,
+ PAT_PUTLEFT_MID2,
+ PAT_PUTRIGHT_MID1,
+ PAT_PUTRIGHT_MID2,
+ PAT_PUTLEFT_DOWN1,
+ PAT_PUTLEFT_DOWN2,
+ PAT_PUTRIGHT_DOWN1,
+ PAT_PUTRIGHT_DOWN2,
+ PAT_PUTUP_UP1,
+ PAT_PUTUP_UP2,
+ PAT_PUTUP_MID1,
+ PAT_PUTUP_MID2,
+ PAT_PUTUP_DOWN1,
+ PAT_PUTUP_DOWN2,
+
+ PAT_WITHSECRETARY
+ };
+
+ enum CharacterBodyPatterns {
+ BPAT_STANDUP = 1,
+ BPAT_STANDDOWN,
+ BPAT_STANDLEFT,
+ BPAT_STANDRIGHT,
+
+ BPAT_HAMMER,
+ BPAT_SNOWMAN,
+ BPAT_WORM,
+ BPAT_GLASS,
+
+ BPAT_SINGLEFT_START,
+ BPAT_SINGLEFT_LOOP,
+ BPAT_SINGLEFT_END,
+
+ BPAT_HIPSLEFT_START,
+ BPAT_HIPSLEFT_LOOP,
+ BPAT_HIPSLEFT_END,
+ BPAT_HIPSRIGHT_START,
+ BPAT_HIPSRIGHT_LOOP,
+ BPAT_HIPSRIGHT_END,
+ BPAT_HIPSUP_START,
+ BPAT_HIPSUP_LOOP,
+ BPAT_HIPSUP_END,
+ BPAT_HIPSDOWN_START,
+ BPAT_HIPSDOWN_LOOP,
+ BPAT_HIPSDOWN_END,
+
+ BPAT_LAUGHLEFT,
+ BPAT_LAUGHRIGHT,
+
+ BPAT_INDICATELEFT,
+ BPAT_INDICATERIGHT,
+
+ BPAT_SCAREDDOWN_START,
+ BPAT_SCAREDDOWN_LOOP,
+ BPAT_SCAREDDOWN_END,
+ BPAT_SCAREDLEFT_START,
+ BPAT_SCAREDLEFT_LOOP,
+ BPAT_SCAREDLEFT_END,
+ BPAT_SCAREDRIGHT_START,
+ BPAT_SCAREDRIGHT_LOOP,
+ BPAT_SCAREDRIGHT_END,
+ BPAT_SCAREDUP_START,
+ BPAT_SCAREDUP_LOOP,
+ BPAT_SCAREDUP_END,
+
+ BPAT_ROPE,
+
+ BPAT_WITHRABBITLEFT_START,
+ BPAT_WITHRABBITLEFT_LOOP,
+ BPAT_WITHRABBITLEFT_END,
+ BPAT_WITHRABBITRIGHT_START,
+ BPAT_WITHRABBITRIGHT_LOOP,
+ BPAT_WITHRABBITRIGHT_END,
+
+ BPAT_WITHRECIPELEFT_START,
+ BPAT_WITHRECIPELEFT_LOOP,
+ BPAT_WITHRECIPELEFT_END,
+ BPAT_WITHRECIPERIGHT_START,
+ BPAT_WITHRECIPERIGHT_LOOP,
+ BPAT_WITHRECIPERIGHT_END,
+
+ BPAT_WITHCARDSLEFT_START,
+ BPAT_WITHCARDSLEFT_LOOP,
+ BPAT_WITHCARDSLEFT_END,
+ BPAT_WITHCARDSRIGHT_START,
+ BPAT_WITHCARDSRIGHT_LOOP,
+ BPAT_WITHCARDSRIGHT_END,
+
+ BPAT_WITHSNOWMANLEFT_START,
+ BPAT_WITHSNOWMANLEFT_LOOP,
+ BPAT_WITHSNOWMANLEFT_END,
+ BPAT_WITHSNOWMANRIGHT_START,
+ BPAT_WITHSNOWMANRIGHT_LOOP,
+ BPAT_WITHSNOWMANRIGHT_END,
+
+ BPAT_WITHNOTEBOOKLEFT_START,
+ BPAT_WITHNOTEBOOKLEFT_LOOP,
+ BPAT_WITHNOTEBOOKLEFT_END,
+ BPAT_WITHNOTEBOOKRIGHT_START,
+ BPAT_WITHNOTEBOOKRIGHT_LOOP,
+ BPAT_WITHNOTEBOOKRIGHT_END,
+
+ BPAT_WITHMEGAPHONELEFT_START,
+ BPAT_WITHMEGAPHONELEFT_LOOP,
+ BPAT_WITHMEGAPHONELEFT_END,
+ BPAT_WITHMEGAPHONERIGHT_START,
+ BPAT_WITHMEGAPHONERIGHT_LOOP,
+ BPAT_WITHMEGAPHONERIGHT_END,
+
+ BPAT_WITHBEARDLEFT_START,
+ BPAT_WITHBEARDLEFT_END,
+ BPAT_WITHBEARDRIGHT_START,
+ BPAT_WITHBEARDRIGHT_END,
+ BPAT_WITHBEARDLEFT_STATIC,
+ BPAT_WITHBEARDRIGHT_STATIC,
+
+ BPAT_MACBETH1,
+ BPAT_MACBETH2,
+ BPAT_MACBETH3,
+ BPAT_MACBETH4,
+ BPAT_MACBETH5,
+ BPAT_MACBETH6,
+ BPAT_MACBETH7,
+ BPAT_MACBETH8,
+ BPAT_MACBETH9,
+
+ BPAT_WITHSECRETARY
+ };
+
+public:
+ static void initStatics();
+ RMTony();
+
+ /**
+ * Initialize Tony
+ */
+ void init();
+
+ /**
+ * Free all memory
+ */
+ void close();
+
+ /**
+ * Tony makes a frame, updating the movement, etc.
+ */
+ void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc);
+
+ /**
+ * Draw method, which controls chararacter display
+ */
+ virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim);
+
+ /**
+ * Show or hide
+ */
+ void show();
+ void hide(bool bShowShadow = false);
+
+ /**
+ * Move and make an action, if necessary
+ */
+ void moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm = 0);
+
+ /**
+ * Tony stops (on the right side with respect to any subject)
+ */
+ virtual void stop(CORO_PARAM);
+ void stopNoAction(CORO_PARAM);
+
+ /**
+ * Set a pattern
+ */
+ void setPattern(int npatt, bool bPlayP0 = false);
+
+ /**
+ * Reads the current pattern
+ */
+ int getCurPattern();
+
+ /**
+ * Waits until the end of a pattern
+ */
+ void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE);
+
+ /**
+ * Check if currently in an action
+ */
+ bool inAction();
+
+ /**
+ * Check if there needs to be an update for scrolling movement
+ */
+ bool mustUpdateScrolling();
+
+ /**
+ * Returns Tony's position
+ */
+ RMPoint position();
+
+ /**
+ * Set the scrolling position
+ */
+ void setScrollPosition(const RMPoint &pt);
+
+ /**
+ * Set the take animation
+ */
+ void take(int nWhere, int nPart);
+ void put(int nWhere, int nPart);
+
+ /**
+ * Start or End Talk
+ */
+ bool startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat,
+ int &headLoopPat, int &bodyLoopPat);
+ void startTalk(CORO_PARAM, CharacterTalkType nTalkType);
+ bool endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic);
+ void endTalk(CORO_PARAM);
+
+ /**
+ * Start or End Static
+ */
+ void startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat,
+ int &bodyStartPat, int &bodyLoopPat);
+ void startStatic(CORO_PARAM, CharacterTalkType nTalkType);
+ void endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat);
+ void endStatic(CORO_PARAM, CharacterTalkType nTalkType);
+
+ /**
+ * Tony disguises himself!
+ */
+ void setShepherdess(bool bIsPast);
+
+ int getShepherdess();
+
+ /**
+ * Perform an action
+ */
+ void executeAction(int nAction, int nActionItem, int nParm);
+
+ void playSfx(int nSfx);
+};
+
+} // End of namespace Tony
+
+#endif
diff --git a/engines/tony/utils.cpp b/engines/tony/utils.cpp
new file mode 100644
index 0000000000..3cc09a1454
--- /dev/null
+++ b/engines/tony/utils.cpp
@@ -0,0 +1,448 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "tony/utils.h"
+#include "tony/tony.h"
+#include "tony/mpal/lzo.h"
+
+namespace Tony {
+
+/**
+ * Extracts a string from a data stream
+ * @param df data stream
+ */
+Common::String readString(Common::ReadStream &df) {
+ Common::String var;
+ uint8 len = df.readByte();
+
+ for (int i = 0; i < len; i++) {
+ char c;
+ c = df.readByte();
+ var += c;
+ }
+
+ return var;
+}
+
+/****************************************************************************\
+* RMPoint methods
+\****************************************************************************/
+
+/**
+ * Constructor
+ */
+RMPoint::RMPoint() {
+ _x = _y = 0;
+}
+
+/**
+ * Copy constructor
+ */
+RMPoint::RMPoint(const RMPoint &p) {
+ _x = p._x;
+ _y = p._y;
+}
+
+/**
+ * Constructor with integer parameters
+ */
+RMPoint::RMPoint(int x1, int y1) {
+ _x = x1;
+ _y = y1;
+}
+
+/**
+ * Copy operator
+ */
+RMPoint &RMPoint::operator=(RMPoint p) {
+ _x = p._x;
+ _y = p._y;
+
+ return *this;
+}
+
+/**
+ * Set a point
+ */
+void RMPoint::set(int x1, int y1) {
+ _x = x1;
+ _y = y1;
+}
+
+/**
+ * Offsets the point by another point
+ */
+void RMPoint::offset(const RMPoint &p) {
+ _x += p._x;
+ _y += p._y;
+}
+
+/**
+ * Offsets the point by a specified offset
+ */
+void RMPoint::offset(int xOff, int yOff) {
+ _x += xOff;
+ _y += yOff;
+}
+
+/**
+ * Sums together two points
+ */
+RMPoint operator+(RMPoint p1, RMPoint p2) {
+ RMPoint p(p1);
+
+ return (p += p2);
+}
+
+/**
+ * Subtracts two points
+ */
+RMPoint operator-(RMPoint p1, RMPoint p2) {
+ RMPoint p(p1);
+
+ return (p -= p2);
+}
+
+/**
+ * Sum (offset) of a point
+ */
+RMPoint &RMPoint::operator+=(RMPoint p) {
+ offset(p);
+ return *this;
+}
+
+/**
+ * Subtract (offset) of a point
+ */
+RMPoint &RMPoint::operator-=(RMPoint p) {
+ offset(-p);
+ return *this;
+}
+
+/**
+ * Inverts a point
+ */
+RMPoint RMPoint::operator-() {
+ RMPoint p;
+
+ p._x = -_x;
+ p._y = -_y;
+
+ return p;
+}
+
+/**
+ * Equality operator
+ */
+bool RMPoint::operator==(RMPoint p) {
+ return ((_x == p._x) && (_y == p._y));
+}
+
+/**
+ * Not equal operator
+ */
+bool RMPoint::operator!=(RMPoint p) {
+ return ((_x != p._x) || (_y != p._y));
+}
+
+/**
+ * Reads a point from a stream
+ */
+void RMPoint::readFromStream(Common::ReadStream &ds) {
+ _x = ds.readSint32LE();
+ _y = ds.readSint32LE();
+}
+
+/****************************************************************************\
+* RMPointReference methods
+\****************************************************************************/
+
+RMPointReference::RMPointReference(int &x, int &y): _x(x), _y(y) {
+}
+
+RMPointReference &RMPointReference::operator=(const RMPoint &p) {
+ _x = p._x; _y = p._y;
+ return *this;
+}
+
+RMPointReference &RMPointReference::operator-=(const RMPoint &p) {
+ _x -= p._x; _y -= p._y;
+ return *this;
+}
+
+RMPointReference::operator RMPoint() const {
+ return RMPoint(_x, _y);
+}
+
+/****************************************************************************\
+* RMRect methods
+\****************************************************************************/
+
+RMRect::RMRect(): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ setEmpty();
+}
+
+void RMRect::setEmpty() {
+ _x1 = _y1 = _x2 = _y2 = 0;
+}
+
+RMRect::RMRect(const RMPoint &p1, const RMPoint &p2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ setRect(p1, p2);
+}
+
+RMRect::RMRect(int X1, int Y1, int X2, int Y2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ setRect(X1, Y1, X2, Y2);
+}
+
+RMRect::RMRect(const RMRect &rc): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) {
+ copyRect(rc);
+}
+
+void RMRect::setRect(const RMPoint &p1, const RMPoint &p2) {
+ _x1 = p1._x;
+ _y1 = p1._y;
+ _x2 = p2._x;
+ _y2 = p2._y;
+}
+
+void RMRect::setRect(int X1, int Y1, int X2, int Y2) {
+ _x1 = X1;
+ _y1 = Y1;
+ _x2 = X2;
+ _y2 = Y2;
+}
+
+void RMRect::setRect(const RMRect &rc) {
+ copyRect(rc);
+}
+
+void RMRect::copyRect(const RMRect &rc) {
+ _x1 = rc._x1;
+ _y1 = rc._y1;
+ _x2 = rc._x2;
+ _y2 = rc._y2;
+}
+
+RMPointReference &RMRect::topLeft() {
+ return _topLeft;
+}
+
+RMPointReference &RMRect::bottomRight() {
+ return _bottomRight;
+}
+
+RMPoint RMRect::center() {
+ return RMPoint((_x2 - _x1) / 2, (_y2 - _y1) / 2);
+}
+
+int RMRect::width() const {
+ return _x2 - _x1;
+}
+
+int RMRect::height() const {
+ return _y2 - _y1;
+}
+
+int RMRect::size() const {
+ return width() * height();
+}
+
+RMRect::operator Common::Rect() const {
+ return Common::Rect(_x1, _y1, _x2, _y2);
+}
+
+bool RMRect::isEmpty() const {
+ return (_x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0);
+}
+
+const RMRect &RMRect::operator=(const RMRect &rc) {
+ copyRect(rc);
+ return *this;
+}
+
+void RMRect::offset(int xOff, int yOff) {
+ _x1 += xOff;
+ _y1 += yOff;
+ _x2 += xOff;
+ _y2 += yOff;
+}
+
+void RMRect::offset(const RMPoint &p) {
+ _x1 += p._x;
+ _y1 += p._y;
+ _x2 += p._x;
+ _y2 += p._y;
+}
+
+const RMRect &RMRect::operator+=(RMPoint p) {
+ offset(p);
+ return *this;
+}
+
+const RMRect &RMRect::operator-=(RMPoint p) {
+ offset(-p);
+ return *this;
+}
+
+RMRect operator+(const RMRect &rc, RMPoint p) {
+ RMRect r(rc);
+ return (r += p);
+}
+
+RMRect operator-(const RMRect &rc, RMPoint p) {
+ RMRect r(rc);
+
+ return (r -= p);
+}
+
+RMRect operator+(RMPoint p, const RMRect &rc) {
+ RMRect r(rc);
+
+ return (r += p);
+}
+
+RMRect operator-(RMPoint p, const RMRect &rc) {
+ RMRect r(rc);
+
+ return (r += p);
+}
+
+bool RMRect::operator==(const RMRect &rc) {
+ return ((_x1 == rc._x1) && (_y1 == rc._y1) && (_x2 == rc._x2) && (_y2 == rc._y2));
+}
+
+bool RMRect::operator!=(const RMRect &rc) {
+ return ((_x1 != rc._x1) || (_y1 != rc._y1) || (_x2 != rc._x2) || (_y2 != rc._y2));
+}
+
+void RMRect::normalizeRect() {
+ setRect(MIN(_x1, _x2), MIN(_y1, _y2), MAX(_x1, _x2), MAX(_y1, _y2));
+}
+
+void RMRect::readFromStream(Common::ReadStream &ds) {
+ _x1 = ds.readSint32LE();
+ _y1 = ds.readSint32LE();
+ _x2 = ds.readSint32LE();
+ _y2 = ds.readSint32LE();
+}
+
+/**
+ * Check if RMPoint is in RMRect
+ */
+bool RMRect::ptInRect(const RMPoint &pt) {
+ return (pt._x >= _x1 && pt._x <= _x2 && pt._y >= _y1 && pt._y <= _y2);
+}
+
+/****************************************************************************\
+* Resource Update
+\****************************************************************************/
+
+RMResUpdate::RMResUpdate() {
+ _infos = NULL;
+ _numUpd = 0;
+}
+
+RMResUpdate::~RMResUpdate() {
+ if (_infos) {
+ delete[] _infos;
+ _infos = NULL;
+ }
+
+ if (_hFile.isOpen())
+ _hFile.close();
+}
+
+void RMResUpdate::init(const Common::String &fileName) {
+ // Open the resource update file
+ if (!_hFile.open(fileName))
+ // It doesn't exist, so exit immediately
+ return;
+
+ uint8 version = _hFile.readByte();
+
+ _numUpd = _hFile.readUint32LE();
+
+ _infos = new ResUpdInfo[_numUpd];
+
+ // Load the index of the resources in the file
+ for (uint32 i = 0; i < _numUpd; ++i) {
+ ResUpdInfo &info = _infos[i];
+
+ info._dwRes = _hFile.readUint32LE();
+ info._offset = _hFile.readUint32LE();
+ info._size = _hFile.readUint32LE();
+ info._cmpSize = _hFile.readUint32LE();
+ }
+}
+
+MpalHandle RMResUpdate::queryResource(uint32 dwRes) {
+ // If there isn't an update file, return NULL
+ if (!_hFile.isOpen())
+ return NULL;
+
+ uint32 i;
+ for (i = 0; i < _numUpd; ++i)
+ if (_infos[i]._dwRes == dwRes)
+ // Found the index
+ break;
+
+ if (i == _numUpd)
+ // Couldn't find a matching resource, so return NULL
+ return NULL;
+
+ const ResUpdInfo &info = _infos[i];
+ byte *cmpBuf = new byte[info._cmpSize];
+ uint32 dwRead;
+
+ // Move to the correct offset and read in the compressed data
+ _hFile.seek(info._offset);
+ dwRead = _hFile.read(cmpBuf, info._cmpSize);
+
+ if (info._cmpSize > dwRead) {
+ // Error occurred reading data, so return NULL
+ delete[] cmpBuf;
+ return NULL;
+ }
+
+ // Allocate space for the output resource
+ MpalHandle destBuf = globalAllocate(0, info._size);
+ byte *lpDestBuf = (byte *)globalLock(destBuf);
+ uint32 dwSize;
+
+ // Decompress the data
+ lzo1x_decompress(cmpBuf, info._cmpSize, lpDestBuf, &dwSize);
+
+ // Delete buffer for compressed data
+ delete [] cmpBuf;
+
+ // Return the resource
+ globalUnlock(destBuf);
+ return destBuf;
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/utils.h b/engines/tony/utils.h
new file mode 100644
index 0000000000..9f13e5f19b
--- /dev/null
+++ b/engines/tony/utils.h
@@ -0,0 +1,176 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_UTILS_H
+#define TONY_UTILS_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+using namespace ::Tony::MPAL;
+
+Common::String readString(Common::ReadStream &ds);
+
+/**
+ * Point class
+ */
+class RMPoint {
+public:
+ int _x, _y;
+
+public:
+ // Constructor
+ RMPoint();
+ RMPoint(const RMPoint &p);
+ RMPoint(int x1, int y1);
+
+ // Copy
+ RMPoint &operator=(RMPoint p);
+
+ // Set
+ void set(int x1, int y1);
+
+ // Offset
+ void offset(int xOff, int yOff);
+ void offset(const RMPoint &p);
+ friend RMPoint operator+(RMPoint p1, RMPoint p2);
+ friend RMPoint operator-(RMPoint p1, RMPoint p2);
+ RMPoint &operator+=(RMPoint p);
+ RMPoint &operator-=(RMPoint p);
+ RMPoint operator-();
+
+ // Comparison
+ bool operator==(RMPoint p);
+ bool operator!=(RMPoint p);
+
+ // Casting a POINT
+ operator Common::Point() const;
+
+ // Extraction from data streams
+ void readFromStream(Common::ReadStream &ds);
+};
+
+class RMPointReference {
+public:
+ int &_x;
+ int &_y;
+
+ RMPointReference(int &x, int &y);
+ RMPointReference &operator=(const RMPoint &p);
+ RMPointReference &operator-=(const RMPoint &p);
+ operator RMPoint() const;
+};
+
+class RMRect {
+public:
+ int _x1, _y1;
+ int _x2, _y2;
+ RMPointReference _topLeft;
+ RMPointReference _bottomRight;
+
+public:
+ RMRect();
+ RMRect(int x1, int y1, int x2, int y2);
+ RMRect(const RMPoint &p1, const RMPoint &p2);
+ RMRect(const RMRect &rc);
+
+ // Attributes
+ RMPointReference &topLeft();
+ RMPointReference &bottomRight();
+ RMPoint center();
+ int width() const;
+ int height() const;
+ bool isEmpty() const;
+ int size() const;
+ operator Common::Rect() const;
+
+ // Set
+ void setRect(int x1, int y1, int x2, int y2);
+ void setRect(const RMPoint &p1, const RMPoint &p2);
+ void setEmpty();
+
+ // Copiers
+ void setRect(const RMRect &rc);
+ void copyRect(const RMRect &rc);
+ const RMRect &operator=(const RMRect &rc);
+
+ // Offset
+ void offset(int xOff, int yOff);
+ void offset(const RMPoint &p);
+ friend RMRect operator+(const RMRect &rc, RMPoint p);
+ friend RMRect operator-(const RMRect &rc, RMPoint p);
+ friend RMRect operator+(RMPoint p, const RMRect &rc);
+ friend RMRect operator-(RMPoint p, const RMRect &rc);
+ const RMRect &operator+=(RMPoint p);
+ const RMRect &operator-=(RMPoint p);
+
+ // Comparison
+ bool operator==(const RMRect &rc);
+ bool operator!=(const RMRect &rc);
+
+ // Normalize
+ void normalizeRect();
+
+ // Point in rect
+ bool ptInRect(const RMPoint &pt);
+
+ // Extract from data stream
+ void readFromStream(Common::ReadStream &ds);
+};
+
+/**
+ * Resource update manager
+ */
+class RMResUpdate {
+ struct ResUpdInfo {
+ uint32 _dwRes;
+ uint32 _offset;
+ uint32 _size;
+ uint32 _cmpSize;
+ };
+
+ uint32 _numUpd;
+ ResUpdInfo *_infos;
+ Common::File _hFile;
+
+public:
+ RMResUpdate();
+ ~RMResUpdate();
+
+ void init(const Common::String &fileName);
+ MpalHandle queryResource(uint32 dwRes);
+};
+
+} // End of namespace Tony
+
+#endif /* TONY_H */
diff --git a/engines/tony/window.cpp b/engines/tony/window.cpp
new file mode 100644
index 0000000000..013be84b4b
--- /dev/null
+++ b/engines/tony/window.cpp
@@ -0,0 +1,336 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "common/scummsys.h"
+#include "graphics/surface.h"
+#include "util.h"
+#include "tony/window.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+
+/****************************************************************************\
+* RMWindow Methods
+\****************************************************************************/
+
+RMWindow::RMWindow() {
+ _showDirtyRects = false;
+}
+
+RMWindow::~RMWindow() {
+ close();
+ RMText::unload();
+ RMGfxTargetBuffer::freeBWPrecalcTable();
+}
+
+/**
+ * Initializes the graphics window
+ */
+void RMWindow::init() {
+ Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ initGraphics(RM_SX, RM_SY, true, &pixelFormat);
+
+ _bGrabScreenshot = false;
+ _bGrabThumbnail = false;
+ _bGrabMovie = false;
+ _wiping = false;
+}
+
+void RMWindow::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
+ if (GLOBALS._bCfgAnni30) {
+ if (!RMGfxTargetBuffer::_precalcTable) {
+ RMGfxTargetBuffer::createBWPrecalcTable();
+ g_vm->getEngine()->getPointer().updateCursor();
+ }
+ Graphics::Surface *screen = g_system->lockScreen();
+ const uint16 *src = (const uint16 *)buf;
+ for (int i = 0; i < h; i++) {
+ uint16 *dst = (uint16 *)screen->getBasePtr(x, y + i);
+ for (int j = 0; j < w; j++) {
+ dst[j] = RMGfxTargetBuffer::_precalcTable[src[j] & 0x7FFF];
+ }
+ src += (pitch / 2);
+ }
+ g_system->unlockScreen();
+ } else {
+ if (RMGfxTargetBuffer::_precalcTable) {
+ RMGfxTargetBuffer::freeBWPrecalcTable();
+ g_vm->getEngine()->getPointer().updateCursor();
+ }
+ g_system->copyRectToScreen(buf, pitch, x, y, w, h);
+ }
+ }
+
+/**
+ * Close the window
+ */
+void RMWindow::close() {
+}
+
+void RMWindow::grabThumbnail(uint16 *thumbmem) {
+ _bGrabThumbnail = true;
+ _wThumbBuf = thumbmem;
+}
+
+/**
+ * Repaint the screen
+ */
+void RMWindow::repaint() {
+ g_system->updateScreen();
+}
+
+/**
+ * Wipes an area of the screen
+ */
+void RMWindow::wipeEffect(Common::Rect &rcBoundEllipse) {
+ if ((rcBoundEllipse.left == 0) && (rcBoundEllipse.top == 0) &&
+ (rcBoundEllipse.right == RM_SX) && (rcBoundEllipse.bottom == RM_SY)) {
+ // Full screen clear wanted, so use shortcut method
+ g_system->fillScreen(0);
+ } else {
+ // Clear the designated area a line at a time
+ uint16 line[RM_SX];
+ Common::fill(line, line + RM_SX, 0);
+
+ // Loop through each line
+ for (int yp = rcBoundEllipse.top; yp < rcBoundEllipse.bottom; ++yp) {
+ copyRectToScreen((const byte *)&line[0], RM_SX * 2, rcBoundEllipse.left, yp, rcBoundEllipse.width(), 1);
+ }
+ }
+}
+
+void RMWindow::getNewFrame(RMGfxTargetBuffer &bigBuf, Common::Rect *rcBoundEllipse) {
+ // Get a pointer to the bytes of the source buffer
+ byte *lpBuf = bigBuf;
+
+ if (rcBoundEllipse != NULL) {
+ // Circular wipe effect
+ getNewFrameWipe(lpBuf, *rcBoundEllipse);
+ _wiping = true;
+ } else if (_wiping) {
+ // Just finished a wiping effect, so copy the full screen
+ copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
+ _wiping = false;
+
+ } else {
+ // Standard screen copy - iterate through the dirty rects
+ Common::List<Common::Rect> dirtyRects = bigBuf.getDirtyRects();
+ Common::List<Common::Rect>::iterator i;
+
+ // If showing dirty rects, copy the entire screen background and set up a surface pointer
+ Graphics::Surface *s = NULL;
+ if (_showDirtyRects) {
+ copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
+ s = g_system->lockScreen();
+ }
+
+ for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
+ Common::Rect &r = *i;
+ const byte *lpSrc = lpBuf + (RM_SX * 2) * r.top + (r.left * 2);
+ copyRectToScreen(lpSrc, RM_SX * 2, r.left, r.top, r.width(), r.height());
+ }
+
+ if (_showDirtyRects) {
+ for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
+ // Frame the copied area with a rectangle
+ s->frameRect(*i, 0xffffff);
+ }
+
+ g_system->unlockScreen();
+ }
+ }
+
+ if (_bGrabThumbnail) {
+ // Need to generate a thumbnail
+ RMSnapshot s;
+
+ s.grabScreenshot(lpBuf, 4, _wThumbBuf);
+ _bGrabThumbnail = false;
+ }
+
+ // Clear the dirty rect list
+ bigBuf.clearDirtyRects();
+}
+
+/**
+ * Copies a section of the game frame in a circle bounded by the specified rectangle
+ */
+void RMWindow::getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse) {
+ // Clear the screen
+ g_system->fillScreen(0);
+
+ if (!rcBoundEllipse.isValidRect())
+ return;
+
+ Common::Point center(rcBoundEllipse.left + rcBoundEllipse.width() / 2,
+ rcBoundEllipse.top + rcBoundEllipse.height() / 2);
+
+ // The rectangle technically defines the area inside the ellipse, with the corners touching
+ // the ellipse boundary. Since we're currently simulating the ellipse using a plain circle,
+ // we need to calculate a necessary width using the hypotenuse of X/2 & Y/2
+ int x2y2 = (rcBoundEllipse.width() / 2) * (rcBoundEllipse.width() / 2) +
+ (rcBoundEllipse.height() / 2) * (rcBoundEllipse.height() / 2);
+ int radius = 0;
+ while ((radius * radius) < x2y2)
+ ++radius;
+
+ // Proceed copying a circular area of the frame with the calculated radius onto the screen
+ int error = -radius;
+ int x = radius;
+ int y = 0;
+
+ while (x >= y) {
+ plotSplices(lpBuf, center, x, y);
+
+ error += y;
+ ++y;
+ error += y;
+
+ if (error >= 0) {
+ error -= x;
+ --x;
+ error -= x;
+ }
+ }
+}
+
+/**
+ * Handles drawing the line splices for the circle of viewable area
+ */
+void RMWindow::plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y) {
+ plotLines(lpBuf, center, x, y);
+ if (x != y)
+ plotLines(lpBuf, center, y, x);
+}
+
+/**
+ * Handles drawing the line splices for the circle of viewable area
+ */
+void RMWindow::plotLines(const byte *lpBuf, const Common::Point &center, int x, int y) {
+ // Skips lines that have no width (i.e. at the top of the circle)
+ if ((x == 0) || (y > center.y))
+ return;
+
+ const byte *pSrc;
+ int xs = MAX(center.x - x, 0);
+ int width = MIN(RM_SX - xs, x * 2);
+
+ if ((center.y - y) >= 0) {
+ // Draw line in top half of circle
+ pSrc = lpBuf + ((center.y - y) * RM_SX * 2) + xs * 2;
+ copyRectToScreen(pSrc, RM_SX * 2, xs, center.y - y, width, 1);
+ }
+
+ if ((center.y + y) < RM_SY) {
+ // Draw line in bottom half of circle
+ pSrc = lpBuf + ((center.y + y) * RM_SX * 2) + xs * 2;
+ copyRectToScreen(pSrc, RM_SX * 2, xs, center.y + y, width, 1);
+ }
+}
+
+void RMWindow::showDirtyRects(bool v) {
+ _showDirtyRects = v;
+}
+
+/****************************************************************************\
+* RMSnapshot Methods
+\****************************************************************************/
+
+void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
+ uint16 *src = (uint16 *)lpBuf;
+
+ int dimx = RM_SX / dezoom;
+ int dimy = RM_SY / dezoom;
+
+ uint16 *cursrc;
+
+ if (lpDestBuf == NULL)
+ src += (RM_SY - 1) * RM_BBX;
+
+ if (dezoom == 1 && 0) {
+ byte *curOut = _rgb;
+
+ for (int y = 0; y < dimy; y++) {
+ for (int x = 0; x < dimx; x++) {
+ cursrc = &src[RM_SKIPX + x];
+
+ *curOut++ = ((*cursrc) & 0x1F) << 3;
+ *curOut++ = (((*cursrc) >> 5) & 0x1F) << 3;
+ *curOut++ = (((*cursrc) >> 10) & 0x1F) << 3;
+
+ if (lpDestBuf)
+ *lpDestBuf++ = *cursrc;
+ }
+
+ if (lpDestBuf == NULL)
+ src -= RM_BBX;
+ else
+ src += RM_BBX;
+ }
+ } else {
+ uint32 k = 0;
+ for (int y = 0; y < dimy; y++) {
+ for (int x = 0; x < dimx; x++) {
+ cursrc = &src[RM_SKIPX + x * dezoom];
+ int sommar, sommab, sommag, curv;
+ sommar = sommab = sommag = 0;
+
+ for (int v = 0; v < dezoom; v++) {
+ for (int u = 0; u < dezoom; u++) {
+ if (lpDestBuf == NULL)
+ curv = -v;
+ else
+ curv = v;
+
+ sommab += cursrc[curv * RM_BBX + u] & 0x1F;
+ sommag += (cursrc[curv * RM_BBX + u] >> 5) & 0x1F;
+ sommar += (cursrc[curv * RM_BBX + u] >> 10) & 0x1F;
+ }
+ }
+ _rgb[k + 0] = (byte)(sommab * 8 / (dezoom * dezoom));
+ _rgb[k + 1] = (byte)(sommag * 8 / (dezoom * dezoom));
+ _rgb[k + 2] = (byte)(sommar * 8 / (dezoom * dezoom));
+
+ if (lpDestBuf != NULL)
+ lpDestBuf[k / 3] = ((int)_rgb[k + 0] >> 3) | (((int)_rgb[k + 1] >> 3) << 5) |
+ (((int)_rgb[k + 2] >> 3) << 10);
+
+ k += 3;
+ }
+
+ if (lpDestBuf == NULL)
+ src -= RM_BBX * dezoom;
+ else
+ src += RM_BBX * dezoom;
+ }
+ }
+}
+
+} // End of namespace Tony
diff --git a/engines/tony/window.h b/engines/tony/window.h
new file mode 100644
index 0000000000..2e8769707f
--- /dev/null
+++ b/engines/tony/window.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#ifndef TONY_WINDOW_H
+#define TONY_WINDOW_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "tony/game.h"
+
+namespace Tony {
+
+class RMSnapshot {
+private:
+ // Buffer used to convert to RGB
+ byte _rgb[RM_SX *RM_SY * 3];
+public:
+ /**
+ * Take a screenshot
+ */
+ void grabScreenshot(byte *lpBuf, int dezoom = 1, uint16 *lpDestBuf = NULL);
+};
+
+
+class RMWindow {
+private:
+ void plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y);
+ void plotLines(const byte *lpBuf, const Common::Point &center, int x, int y);
+
+protected:
+ bool _wiping;
+ bool _showDirtyRects;
+
+ bool _bGrabScreenshot;
+ bool _bGrabThumbnail;
+ bool _bGrabMovie;
+ uint16 *_wThumbBuf;
+
+ void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
+ void wipeEffect(Common::Rect &rcBoundEllipse);
+ void getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse);
+
+public:
+ RMWindow();
+ ~RMWindow();
+
+ /**
+ * Initialization
+ */
+ void init();
+ void close();
+
+ /**
+ * Drawing
+ */
+ void repaint();
+
+ /**
+ * Reads the next frame
+ */
+ void getNewFrame(RMGfxTargetBuffer &lpBuf, Common::Rect *rcBoundEllipse);
+
+ /**
+ * Request a thumbnail be grabbed during the next frame
+ */
+ void grabThumbnail(uint16 *buf);
+
+ void showDirtyRects(bool v);
+};
+
+} // End of namespace Tony
+
+#endif /* TONY_WINDOW_H */