aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillem Jan Palenstijn2011-11-20 22:46:58 +0100
committerWillem Jan Palenstijn2011-11-20 22:49:18 +0100
commit2613e7f5879c8c3ca7663dfcea8d5b91c499c107 (patch)
treecb9a57e5ad2fb81d988c99b7704795477719807d
parent528c1173d7b9d15d9b7e36f25a876c8952357f26 (diff)
parentb592d73e6226c5c5375d4b40123b15e37b064b9e (diff)
downloadscummvm-rg350-2613e7f5879c8c3ca7663dfcea8d5b91c499c107.tar.gz
scummvm-rg350-2613e7f5879c8c3ca7663dfcea8d5b91c499c107.tar.bz2
scummvm-rg350-2613e7f5879c8c3ca7663dfcea8d5b91c499c107.zip
TOLTECS: Merge toltecs engine
This is an engine for the game "3 Skulls of the Toltecs". It is a renamed, tweaked and manual merge of the pull request at https://github.com/scummvm/scummvm/pull/119
-rw-r--r--base/plugins.cpp3
-rwxr-xr-xconfigure1
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/toltecs/TODO.txt54
-rw-r--r--engines/toltecs/animation.cpp164
-rw-r--r--engines/toltecs/animation.h69
-rw-r--r--engines/toltecs/detection.cpp287
-rw-r--r--engines/toltecs/menu.cpp638
-rw-r--r--engines/toltecs/menu.h167
-rw-r--r--engines/toltecs/microtiles.cpp220
-rw-r--r--engines/toltecs/microtiles.h61
-rw-r--r--engines/toltecs/module.mk28
-rw-r--r--engines/toltecs/movie.cpp290
-rw-r--r--engines/toltecs/movie.h59
-rw-r--r--engines/toltecs/music.cpp104
-rw-r--r--engines/toltecs/music.h53
-rw-r--r--engines/toltecs/palette.cpp244
-rw-r--r--engines/toltecs/palette.h85
-rw-r--r--engines/toltecs/render.cpp311
-rw-r--r--engines/toltecs/render.h99
-rw-r--r--engines/toltecs/resource.cpp128
-rw-r--r--engines/toltecs/resource.h85
-rw-r--r--engines/toltecs/saveload.cpp204
-rw-r--r--engines/toltecs/screen.cpp808
-rw-r--r--engines/toltecs/screen.h251
-rw-r--r--engines/toltecs/script.cpp1117
-rw-r--r--engines/toltecs/script.h184
-rw-r--r--engines/toltecs/segmap.cpp408
-rw-r--r--engines/toltecs/segmap.h116
-rw-r--r--engines/toltecs/sound.cpp188
-rw-r--r--engines/toltecs/sound.h74
-rw-r--r--engines/toltecs/sprite.cpp509
-rw-r--r--engines/toltecs/toltecs.cpp654
-rw-r--r--engines/toltecs/toltecs.h213
34 files changed, 7881 insertions, 0 deletions
diff --git a/base/plugins.cpp b/base/plugins.cpp
index e587b962d6..df12d0d3ba 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -172,6 +172,9 @@ public:
#if PLUGIN_ENABLED_STATIC(TINSEL)
LINK_PLUGIN(TINSEL)
#endif
+ #if PLUGIN_ENABLED_STATIC(TOLTECS)
+ LINK_PLUGIN(TOLTECS)
+ #endif
#if PLUGIN_ENABLED_STATIC(TOON)
LINK_PLUGIN(TOON)
#endif
diff --git a/configure b/configure
index 96667e643f..9675e4841a 100755
--- a/configure
+++ b/configure
@@ -117,6 +117,7 @@ add_engine sword25 "Broken Sword 2.5" no
add_engine teenagent "Teen Agent" yes
add_engine testbed "TestBed: the Testing framework" no
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 tsage "TsAGE" yes
diff --git a/engines/engines.mk b/engines/engines.mk
index b327ba68a8..5280bf92d7 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -188,6 +188,11 @@ DEFINES += -DENABLE_TINSEL=$(ENABLE_TINSEL)
MODULES += engines/tinsel
endif
+ifdef ENABLE_TOLTECS
+DEFINES += -DENABLE_TOLTECS=$(ENABLE_TOLTECS)
+MODULES += engines/toltecs
+endif
+
ifdef ENABLE_TOON
DEFINES += -DENABLE_TOON=$(ENABLE_TOON)
MODULES += engines/toon
diff --git a/engines/toltecs/TODO.txt b/engines/toltecs/TODO.txt
new file mode 100644
index 0000000000..31215f7fff
--- /dev/null
+++ b/engines/toltecs/TODO.txt
@@ -0,0 +1,54 @@
+NOTES
+-------
+- The game is completable with revision 20 with some minor glitches (see BUGS).
+
+
+TODO
+------
+- Finish music support (volume, looping perhaps?)
+- Check if background resources are used before purging them when changing scenes - check
+ the comments inside sfLoadScene.
+- Sometimes, some stray lines are drawn below fonts - probably because the text doesn't
+ fit inside the screen and the surface is cut off incorrectly.
+- Fix some occasional artifacts on screen (e.g. when text is shown over an animation)
+ sometimes.
+- Pathfinding bugs in Fort Apache and the Indian village - e.g. when moving to the door
+ on the right, the hero walks around a big path. Same when moving to the tent on the top
+ in the indian village.
+- When getting the piano player off the well, he walks up facing to the left (his side
+ animation is shown).
+- Load/start sound and music of the saved scene when loading.
+- Some sounds are cut off prematurely (e.g. when an animation finishes before the sound
+ sample, when changing scenes and a dialog is immediately started afterwards).
+- When saving a game, save the whole screen when an animation is playing
+ e.g. when playing animations (the script controlled ones, not the movies) it should save
+ the whole screen when the game is saved when such an animation is running since it uses
+ delta-frames so currently, once restored, the screen is wrong. This is only observed in
+ a few places.
+
+
+BUGS
+------
+None known (see TODO).
+
+DONE
+------
+- Crashes sometimes on scene changes
+ (I guess because some talktext is still running although the slot used by it has changed)
+ Crashes sometimes (e.g. when inside the barn and going up the ladder)
+ These crashes are caused by non-stopping background sounds, see TODO above.
+- The game music is in XMIDI. I'm assuming that the rest of the formats were because of badly
+ dumped resources - finalize() Calls were missing and the resulting music could contain anything.
+- Implement dirty rectangles (low priority)
+- Optimize segment mask redrawing (only redraw what's neccessary)
+- Add movie playback functionality (movie format is known, needs implementing)
+- Add sound support (after rewrite of the resource system)
+- Still some clipping issues (walking upstairs in the saloon, when on top of the tower in the hideout)
+- Add game menu
+ - Rewrite from scratch with only minor stuff from the disasm since the original menu is one huge
+ messy function
+- Rewrite the resource system to something similar as used by M4
+ - each resource type gets its own class, the resource cache manages the resource instances
+ - generic resource class which has a method that creates a MemoryReadStream
+ - rewrite parts of the engine to use the new resource system
+- Extend savegame format (savegame version, description, more data)
diff --git a/engines/toltecs/animation.cpp b/engines/toltecs/animation.cpp
new file mode 100644
index 0000000000..eef9cef9ed
--- /dev/null
+++ b/engines/toltecs/animation.cpp
@@ -0,0 +1,164 @@
+/* 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 "toltecs/toltecs.h"
+#include "toltecs/animation.h"
+#include "toltecs/palette.h"
+#include "toltecs/screen.h"
+
+namespace Toltecs {
+
+AnimationPlayer::AnimationPlayer(ToltecsEngine *vm) : _vm(vm) {
+ _animBuffer = new byte[262144];
+}
+
+AnimationPlayer::~AnimationPlayer() {
+ delete[] _animBuffer;
+}
+
+void AnimationPlayer::start(uint resIndex) {
+ debug(1, "AnimationPlayer::start(%d)", resIndex);
+
+ _resIndex = resIndex;
+
+ _vm->_arc->openResource(_resIndex);
+ _height = _vm->_arc->readUint16LE();
+ _width = _vm->_arc->readUint16LE();
+ _frameCount = _vm->_arc->readUint16LE();
+ _vm->_arc->read(_vm->_palette->getAnimPalette(), 768);
+ _curFrameSize = _vm->_arc->readUint32LE();
+ _nextFrameOffset = _curFrameSize + 782;
+ _vm->_arc->read(_animBuffer, _curFrameSize);
+ _nextFrameSize = _vm->_arc->readUint32LE();
+ _vm->_arc->closeResource();
+
+ debug(1, "AnimationPlayer::start() width = %d; height = %d; frameCount = %d", _width, _height, _frameCount);
+
+ _vm->_sceneWidth = _width;
+ _vm->_sceneHeight = _height;
+
+ unpackFrame();
+
+ _keepFrameCounter = 0;
+ _frameNumber = 0;
+ // TODO mov screenFlag01, 0FFFFh
+ // TODO mov animDrawFrameFlag, 0FFFFh
+
+ _firstNextFrameOffset = _nextFrameOffset;
+ _firstCurFrameSize = _curFrameSize;
+ _firstNextFrameSize = _nextFrameSize;
+
+}
+
+void AnimationPlayer::nextFrame() {
+ debug(1, "AnimationPlayer::nextFrame()");
+
+ if (_frameNumber == _frameCount) {
+ _nextFrameOffset = _firstNextFrameOffset;
+ _curFrameSize = _firstCurFrameSize;
+ _nextFrameSize = _firstNextFrameSize;
+ _frameNumber = 1;
+ } else {
+ _frameNumber++;
+ }
+
+ debug(1, "AnimationPlayer::nextFrame() frameNumber = %d", _frameNumber);
+
+ if (_keepFrameCounter > 0) {
+ _keepFrameCounter--;
+ return;
+ }
+
+ _vm->_arc->openResource(_resIndex);
+ _vm->_arc->seek(_nextFrameOffset, SEEK_CUR);
+ _curFrameSize = _nextFrameSize;
+
+ if (_curFrameSize == 0)
+ _curFrameSize = 1;
+
+ _vm->_arc->read(_animBuffer, _curFrameSize);
+ _nextFrameSize = _vm->_arc->readUint32LE();
+ _nextFrameOffset += _curFrameSize + 4;
+
+ if (_curFrameSize > 1) {
+ unpackFrame();
+ // TODO mov animDrawFrameFlag, 0FFFFh
+ } else {
+ _keepFrameCounter = _animBuffer[0] - 1;
+ // TODO mov animDrawFrameFlag, 0
+ }
+
+ _vm->_arc->closeResource();
+
+
+}
+
+int16 AnimationPlayer::getStatus() {
+ debug(1, "AnimationPlayer::getStatus()");
+ int16 status = -1;
+ if (_frameNumber == _frameCount)
+ status = 0;
+ else if (_frameNumber == _frameCount - 1)
+ status = 1;
+ debug(1, "AnimationPlayer::getStatus() status = %d", status);
+ return status;
+}
+
+void AnimationPlayer::unpackFrame() {
+ _vm->_screen->unpackRle(_animBuffer, _vm->_screen->_frontScreen, _width, _height);
+ _vm->_screen->unpackRle(_animBuffer, _vm->_screen->_backScreen, _width, _height);
+ _vm->_screen->_fullRefresh = true;
+}
+
+void AnimationPlayer::saveState(Common::WriteStream *out) {
+ out->writeUint16LE(_resIndex);
+ // NOTE: The original engine doesn't save width/height, but we do
+ out->writeUint16LE(_width);
+ out->writeUint16LE(_height);
+ out->writeUint16LE(_frameCount);
+ out->writeUint16LE(_frameNumber);
+ out->writeUint32LE(_keepFrameCounter);
+ out->writeUint32LE(_curFrameSize);
+ out->writeUint32LE(_nextFrameSize);
+ out->writeUint32LE(_nextFrameOffset);
+ out->writeUint32LE(_firstCurFrameSize);
+ out->writeUint32LE(_firstNextFrameSize);
+ out->writeUint32LE(_firstNextFrameOffset);
+}
+
+void AnimationPlayer::loadState(Common::ReadStream *in) {
+ _resIndex = in->readUint16LE();
+ _width = in->readUint16LE();
+ _height = in->readUint16LE();
+ _frameCount = in->readUint16LE();
+ _frameNumber = in->readUint16LE();
+ _keepFrameCounter = in->readUint32LE();
+ _curFrameSize = in->readUint32LE();
+ _nextFrameSize = in->readUint32LE();
+ _nextFrameOffset = in->readUint32LE();
+ _firstCurFrameSize = in->readUint32LE();
+ _firstNextFrameSize = in->readUint32LE();
+ _firstNextFrameOffset = in->readUint32LE();
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/animation.h b/engines/toltecs/animation.h
new file mode 100644
index 0000000000..22576d7535
--- /dev/null
+++ b/engines/toltecs/animation.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef TOLTECS_ANIMATION_H
+#define TOLTECS_ANIMATION_H
+
+#include "toltecs/toltecs.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+class AnimationPlayer {
+public:
+ AnimationPlayer(ToltecsEngine *vm);
+ ~AnimationPlayer();
+
+ void start(uint resIndex);
+ void nextFrame();
+ int16 getStatus();
+ uint16 getFrameNumber() const { return _frameNumber; }
+
+ void saveState(Common::WriteStream *out);
+ void loadState(Common::ReadStream *in);
+
+//protected:
+public:
+ ToltecsEngine *_vm;
+
+ // 262144
+ byte *_animBuffer;
+
+ uint16 _resIndex;
+
+ uint16 _width, _height;
+ uint16 _frameNumber, _frameCount;
+ uint32 _keepFrameCounter;
+
+ uint32 _curFrameSize;
+ uint32 _nextFrameSize, _nextFrameOffset;
+
+ uint32 _firstNextFrameOffset, _firstCurFrameSize, _firstNextFrameSize;
+
+ void unpackFrame();
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_ANIMATION_H */
diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp
new file mode 100644
index 0000000000..3717d33950
--- /dev/null
+++ b/engines/toltecs/detection.cpp
@@ -0,0 +1,287 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/system.h"
+
+#include "toltecs/toltecs.h"
+
+
+namespace Toltecs {
+
+struct ToltecsGameDescription {
+ ADGameDescription desc;
+};
+
+uint32 ToltecsEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+Common::Language ToltecsEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+}
+
+static const PlainGameDescriptor toltecsGames[] = {
+ {"toltecs", "3 Skulls of the Toltecs"},
+ {0, 0}
+};
+
+
+namespace Toltecs {
+
+static const ToltecsGameDescription gameDescriptions[] = {
+
+ {
+ // 3 Skulls of the Toltecs English version
+ {
+ "toltecs",
+ 0,
+ AD_ENTRY1s("WESTERN", "05472037e9cfde146e953c434e74f0f4", 337643527),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // 3 Skulls of the Toltecs Russian version
+ {
+ "toltecs",
+ 0,
+ AD_ENTRY1s("WESTERN", "ba1742d3193b68ceb9434e2ab7a09a9b", 391462783),
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // 3 Skulls of the Toltecs German version
+ {
+ "toltecs",
+ 0,
+ AD_ENTRY1s("WESTERN", "1a3292bad8e0bb5701800c73531dd75e", 345176617),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // 3 Skulls of the Toltecs German Demo version
+ {
+ "toltecs",
+ 0,
+ AD_ENTRY1s("WESTERN", "1c85e82712d24f1d5c1ea2a66ddd75c2", 47730038),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // 3 Skulls of the Toltecs French version
+ {
+ "toltecs",
+ 0,
+ AD_ENTRY1s("WESTERN", "4fb845635cbdac732453fe23be350df9", 327269545),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ {
+ // 3 Skulls of the Toltecs Spanish version
+ {
+ "toltecs",
+ 0,
+ AD_ENTRY1s("WESTERN", "479f468beccc1b0ce5873ec523d1380e", 308391018),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NONE)
+ },
+ },
+
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace Toltecs
+
+class ToltecsMetaEngine : public AdvancedMetaEngine {
+public:
+ ToltecsMetaEngine() : AdvancedMetaEngine(Toltecs::gameDescriptions, sizeof(Toltecs::ToltecsGameDescription), toltecsGames) {
+ _singleid = "toltecs";
+ }
+
+ virtual const char *getName() const {
+ return "Toltecs Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Toltecs Engine Revistronic (C) 1996";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool ToltecsMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+// (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail);
+}
+
+bool Toltecs::ToltecsEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool ToltecsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Toltecs::ToltecsGameDescription *gd = (const Toltecs::ToltecsGameDescription *)desc;
+ if (gd) {
+ *engine = new Toltecs::ToltecsEngine(syst, gd);
+ }
+ return gd != 0;
+}
+
+SaveStateList ToltecsMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Toltecs::ToltecsEngine::SaveHeader header;
+ Common::String pattern = target;
+ pattern += ".???";
+
+ Common::StringArray filenames;
+ filenames = saveFileMan->listSavefiles(pattern.c_str());
+ Common::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) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
+ if (in) {
+ if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) {
+ saveList.push_back(SaveStateDescriptor(slotNum, header.description));
+ }
+ delete in;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+int ToltecsMetaEngine::getMaximumSaveSlot() const {
+ return 999;
+}
+
+void ToltecsMetaEngine::removeSaveState(const char *target, int slot) const {
+ // Slot 0 can't be deleted, it's for restarting the game(s)
+ if (slot == 0)
+ return;
+
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot);
+
+ saveFileMan->removeSavefile(filename.c_str());
+
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += ".???";
+ filenames = saveFileMan->listSavefiles(pattern.c_str());
+ Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ 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);
+
+ // Rename every slot greater than the deleted slot,
+ // Also do not rename quicksaves.
+ if (slotNum > slot && slotNum < 990) {
+ // FIXME: Our savefile renaming done here is inconsitent with what we do in
+ // GUI_v2::deleteMenu. While here we rename every slot with a greater equal
+ // number of the deleted slot to deleted slot, deleted slot + 1 etc.,
+ // we only rename the following slots in GUI_v2::deleteMenu until a slot
+ // is missing.
+ saveFileMan->renameSavefile(file->c_str(), filename.c_str());
+
+ filename = Toltecs::ToltecsEngine::getSavegameFilename(target, ++slot);
+ }
+ }
+
+}
+
+SaveStateDescriptor ToltecsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot);
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
+
+ if (in) {
+ Toltecs::ToltecsEngine::SaveHeader header;
+ Toltecs::ToltecsEngine::kReadSaveHeaderError error;
+
+ error = Toltecs::ToltecsEngine::readSaveHeader(in, true, header);
+ delete in;
+
+ if (error == Toltecs::ToltecsEngine::kRSHENoError) {
+ SaveStateDescriptor desc(slot, header.description);
+
+ desc.setDeletableFlag(false);
+ desc.setWriteProtectedFlag(false);
+ desc.setThumbnail(header.thumbnail);
+
+ return desc;
+ }
+ }
+
+ return SaveStateDescriptor();
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(TOLTECS)
+ REGISTER_PLUGIN_DYNAMIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine);
+#endif
diff --git a/engines/toltecs/menu.cpp b/engines/toltecs/menu.cpp
new file mode 100644
index 0000000000..25c240f67e
--- /dev/null
+++ b/engines/toltecs/menu.cpp
@@ -0,0 +1,638 @@
+/* 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/savefile.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/menu.h"
+#include "toltecs/palette.h"
+#include "toltecs/render.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+MenuSystem::MenuSystem(ToltecsEngine *vm) : _vm(vm) {
+}
+
+MenuSystem::~MenuSystem() {
+}
+
+int MenuSystem::run() {
+
+ //debug("MenuSystem::run()");
+
+ _background = new Graphics::Surface();
+ _background->create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+
+ // Save original background
+ Graphics::Surface backgroundOrig;
+ backgroundOrig.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+ memcpy(backgroundOrig.getBasePtr(0,0), _vm->_screen->_frontScreen, 640 * 400);
+
+ _currMenuID = kMenuIdNone;
+ _newMenuID = kMenuIdMain;
+ _currItemID = kItemIdNone;
+ _editingDescription = false;
+ _cfgText = true;
+ _cfgVoices = true;
+ _cfgMasterVolume = 10;
+ _cfgVoicesVolume = 10;
+ _cfgMusicVolume = 10;
+ _cfgSoundFXVolume = 10;
+ _cfgBackgroundVolume = 10;
+ _running = true;
+ _top = 30 - _vm->_guiHeight / 2;
+ _needRedraw = false;
+
+ // TODO: buildColorTransTable2
+ _vm->_palette->buildColorTransTable(0, 16, 7);
+
+ _vm->_screen->_renderQueue->clear();
+ // Draw the menu background and frame
+ _vm->_screen->blastSprite(0x140 + _vm->_cameraX, 0x175 + _vm->_cameraY, 0, 1, 0x4000);
+ shadeRect(60, 39, 520, 246, 30, 94);
+
+ memcpy(_background->pixels, _vm->_screen->_frontScreen, 640 * 400);
+
+ while (_running) {
+ update();
+ _vm->_system->updateScreen();
+ }
+
+ // Restore original background
+ memcpy(_vm->_screen->_frontScreen, backgroundOrig.getBasePtr(0,0), 640 * 400);
+ _vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, 400);
+ _vm->_system->updateScreen();
+
+ // Cleanup
+ backgroundOrig.free();
+ _background->free();
+ delete _background;
+
+ return 0;
+}
+
+void MenuSystem::update() {
+
+ if (_currMenuID != _newMenuID) {
+ _currMenuID = _newMenuID;
+ //debug("_currMenuID = %d", _currMenuID);
+ initMenu(_currMenuID);
+ }
+
+ handleEvents();
+
+ if (_needRedraw) {
+ //_vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen + 39 * 640 + 60, 640, 60, 39, 520, 247);
+ _vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, 400);
+ //debug("redraw");
+ _needRedraw = false;
+ }
+
+ _vm->_system->delayMillis(5);
+
+}
+
+void MenuSystem::handleEvents() {
+
+ Common::Event event;
+ Common::EventManager *eventMan = _vm->_system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ handleKeyDown(event.kbd);
+ break;
+ case Common::EVENT_QUIT:
+ _running = false;
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ handleMouseMove(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ handleMouseClick(event.mouse.x, event.mouse.y);
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+void MenuSystem::addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor) {
+ Item item;
+ item.id = id;
+ item.defaultColor = defaultColor;
+ item.activeColor = activeColor;
+ item.x = x;
+ item.y = y;
+ item.w = w;
+ item.fontNum = fontNum;
+ setItemCaption(&item, caption);
+ _items.push_back(item);
+}
+
+void MenuSystem::drawItem(ItemID itemID, bool active) {
+ Item *item = getItem(itemID);
+ if (item) {
+ byte color = active ? item->activeColor : item->defaultColor;
+ drawString(item->rect.left, item->y, 0, item->fontNum, color, item->caption.c_str());
+ }
+}
+
+void MenuSystem::handleMouseMove(int x, int y) {
+ if (!_editingDescription) {
+ ItemID newItemID = findItemAt(x, y);
+ if (_currItemID != newItemID) {
+ leaveItem(_currItemID);
+ _currItemID = newItemID;
+ enterItem(newItemID);
+ }
+ }
+}
+
+void MenuSystem::handleMouseClick(int x, int y) {
+ if (!_editingDescription) {
+ ItemID id = findItemAt(x, y);
+ clickItem(id);
+ }
+}
+
+void MenuSystem::handleKeyDown(const Common::KeyState& kbd) {
+ if (_editingDescription) {
+ if (kbd.keycode >= Common::KEYCODE_SPACE && kbd.keycode <= Common::KEYCODE_z) {
+ _editingDescriptionItem->caption += kbd.ascii;
+ restoreRect(_editingDescriptionItem->rect.left, _editingDescriptionItem->rect.top,
+ _editingDescriptionItem->rect.width() + 1, _editingDescriptionItem->rect.height() - 2);
+ setItemCaption(_editingDescriptionItem, _editingDescriptionItem->caption.c_str());
+ drawItem(_editingDescriptionID, true);
+ } else if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
+ _editingDescriptionItem->caption.deleteLastChar();
+ restoreRect(_editingDescriptionItem->rect.left, _editingDescriptionItem->rect.top,
+ _editingDescriptionItem->rect.width() + 1, _editingDescriptionItem->rect.height() - 2);
+ setItemCaption(_editingDescriptionItem, _editingDescriptionItem->caption.c_str());
+ drawItem(_editingDescriptionID, true);
+ } else if (kbd.keycode == Common::KEYCODE_RETURN) {
+ SavegameItem *savegameItem = getSavegameItemByID(_editingDescriptionID);
+ _editingDescription = false;
+ _vm->requestSavegame(savegameItem->_slotNum, _editingDescriptionItem->caption);
+ _running = false;
+ } else if (kbd.keycode == Common::KEYCODE_ESCAPE) {
+ _editingDescription = false;
+ }
+ }
+}
+
+ItemID MenuSystem::findItemAt(int x, int y) {
+ for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
+ if ((*iter).rect.contains(x, y))
+ return (*iter).id;
+ }
+ return kItemIdNone;
+}
+
+MenuSystem::Item *MenuSystem::getItem(ItemID id) {
+ for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
+ if ((*iter).id == id)
+ return &(*iter);
+ }
+ return NULL;
+}
+
+void MenuSystem::setItemCaption(Item *item, const char *caption) {
+ Font font(_vm->_res->load(_vm->_screen->getFontResIndex(item->fontNum))->data);
+ int width = font.getTextWidth((const byte*)caption);
+ int height = font.getHeight();
+ item->rect = Common::Rect(item->x, item->y - height, item->x + width, item->y);
+ if (item->w) {
+ item->rect.translate(item->w - width / 2, 0);
+ }
+ item->caption = caption;
+}
+
+void MenuSystem::initMenu(MenuID menuID) {
+
+ int newSlotNum;
+
+ _items.clear();
+
+ memcpy(_vm->_screen->_frontScreen, _background->pixels, 640 * 400);
+
+ switch (menuID) {
+ case kMenuIdMain:
+ drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou));
+ addClickTextItem(kItemIdLoad, 0, 115, 320, 0, _vm->getSysString(kStrLoad), 229, 255);
+ addClickTextItem(kItemIdSave, 0, 135, 320, 0, _vm->getSysString(kStrSave), 229, 255);
+ addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(kStrTextOn), 229, 255);
+ addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(kStrVoicesOn), 229, 255);
+ addClickTextItem(kItemIdVolumesMenu, 0, 215, 320, 0, _vm->getSysString(kStrVolume), 229, 255);
+ addClickTextItem(kItemIdPlay, 0, 245, 320, 0, _vm->getSysString(kStrPlay), 229, 255);
+ addClickTextItem(kItemIdQuit, 0, 275, 320, 0, _vm->getSysString(kStrQuit), 229, 255);
+ break;
+ case kMenuIdLoad:
+ drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrLoadGame));
+ addClickTextItem(kItemIdSavegameUp, 0, 155, 545, 1, "^", 255, 253);
+ addClickTextItem(kItemIdSavegameDown, 0, 195, 545, 1, "\\", 255, 253);
+ addClickTextItem(kItemIdCancel, 0, 275, 320, 0, _vm->getSysString(kStrCancel), 255, 253);
+ addClickTextItem(kItemIdSavegame1, 0, 115 + 20 * 0, 300, 0, "SAVEGAME 1", 231, 234);
+ addClickTextItem(kItemIdSavegame2, 0, 115 + 20 * 1, 300, 0, "SAVEGAME 2", 231, 234);
+ addClickTextItem(kItemIdSavegame3, 0, 115 + 20 * 2, 300, 0, "SAVEGAME 3", 231, 234);
+ addClickTextItem(kItemIdSavegame4, 0, 115 + 20 * 3, 300, 0, "SAVEGAME 4", 231, 234);
+ addClickTextItem(kItemIdSavegame5, 0, 115 + 20 * 4, 300, 0, "SAVEGAME 5", 231, 234);
+ addClickTextItem(kItemIdSavegame6, 0, 115 + 20 * 5, 300, 0, "SAVEGAME 6", 231, 234);
+ addClickTextItem(kItemIdSavegame7, 0, 115 + 20 * 6, 300, 0, "SAVEGAME 7", 231, 234);
+ loadSavegamesList();
+ setSavegameCaptions();
+ break;
+ case kMenuIdSave:
+ drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrSaveGame));
+ addClickTextItem(kItemIdSavegameUp, 0, 155, 545, 1, "^", 255, 253);
+ addClickTextItem(kItemIdSavegameDown, 0, 195, 545, 1, "\\", 255, 253);
+ addClickTextItem(kItemIdCancel, 0, 275, 320, 0, _vm->getSysString(kStrCancel), 255, 253);
+ addClickTextItem(kItemIdSavegame1, 0, 115 + 20 * 0, 300, 0, "SAVEGAME 1", 231, 234);
+ addClickTextItem(kItemIdSavegame2, 0, 115 + 20 * 1, 300, 0, "SAVEGAME 2", 231, 234);
+ addClickTextItem(kItemIdSavegame3, 0, 115 + 20 * 2, 300, 0, "SAVEGAME 3", 231, 234);
+ addClickTextItem(kItemIdSavegame4, 0, 115 + 20 * 3, 300, 0, "SAVEGAME 4", 231, 234);
+ addClickTextItem(kItemIdSavegame5, 0, 115 + 20 * 4, 300, 0, "SAVEGAME 5", 231, 234);
+ addClickTextItem(kItemIdSavegame6, 0, 115 + 20 * 5, 300, 0, "SAVEGAME 6", 231, 234);
+ addClickTextItem(kItemIdSavegame7, 0, 115 + 20 * 6, 300, 0, "SAVEGAME 7", 231, 234);
+ newSlotNum = loadSavegamesList() + 1;
+ _savegames.push_back(SavegameItem(newSlotNum, Common::String::format("GAME %03d", _savegames.size() + 1)));
+ setSavegameCaptions();
+ break;
+ case kMenuIdVolumes:
+ drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrAdjustVolume));
+ drawString(0, 130, 200, 0, 246, _vm->getSysString(kStrMaster));
+ drawString(0, 155, 200, 0, 244, _vm->getSysString(kStrVoices));
+ drawString(0, 180, 200, 0, 244, _vm->getSysString(kStrMusic));
+ drawString(0, 205, 200, 0, 244, _vm->getSysString(kStrSoundFx));
+ drawString(0, 230, 200, 0, 244, _vm->getSysString(kStrBackground));
+ addClickTextItem(kItemIdDone, 0, 275, 200, 0, _vm->getSysString(kStrDone), 229, 253);
+ addClickTextItem(kItemIdCancel, 0, 275, 440, 0, _vm->getSysString(kStrCancel), 229, 253);
+ addClickTextItem(kItemIdMasterDown, 0, 130 + 25 * 0, 348, 1, "[", 229, 253);
+ addClickTextItem(kItemIdVoicesDown, 0, 130 + 25 * 1, 348, 1, "[", 229, 253);
+ addClickTextItem(kItemIdMusicDown, 0, 130 + 25 * 2, 348, 1, "[", 229, 253);
+ addClickTextItem(kItemIdSoundFXDown, 0, 130 + 25 * 3, 348, 1, "[", 229, 253);
+ addClickTextItem(kItemIdBackgroundDown, 0, 130 + 25 * 4, 348, 1, "[", 229, 253);
+ addClickTextItem(kItemIdMasterUp, 0, 130 + 25 * 0, 372, 1, "]", 229, 253);
+ addClickTextItem(kItemIdVoicesUp, 0, 130 + 25 * 1, 372, 1, "]", 229, 253);
+ addClickTextItem(kItemIdMusicUp, 0, 130 + 25 * 2, 372, 1, "]", 229, 253);
+ addClickTextItem(kItemIdSoundFXUp, 0, 130 + 25 * 3, 372, 1, "]", 229, 253);
+ addClickTextItem(kItemIdBackgroundUp, 0, 130 + 25 * 4, 372, 1, "]", 229, 253);
+ drawVolumeBar(kItemIdMaster);
+ drawVolumeBar(kItemIdVoices);
+ drawVolumeBar(kItemIdMusic);
+ drawVolumeBar(kItemIdSoundFX);
+ drawVolumeBar(kItemIdBackground);
+ break;
+ default:
+ break;
+ }
+
+ for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
+ drawItem((*iter).id, false);
+ }
+
+}
+
+void MenuSystem::enterItem(ItemID id) {
+ drawItem(id, true);
+}
+
+void MenuSystem::leaveItem(ItemID id) {
+ drawItem(id, false);
+}
+
+void MenuSystem::clickItem(ItemID id) {
+ //Item *item = getItem(id);
+ switch (id) {
+ // Main menu
+ case kItemIdSave:
+ _newMenuID = kMenuIdSave;
+ break;
+ case kItemIdLoad:
+ _newMenuID = kMenuIdLoad;
+ break;
+ case kItemIdToggleText:
+ setCfgText(!_cfgText, true);
+ if (!_cfgVoices && !_cfgText)
+ setCfgVoices(true, false);
+ break;
+ case kItemIdToggleVoices:
+ setCfgVoices(!_cfgVoices, true);
+ if (!_cfgVoices && !_cfgText)
+ setCfgText(true, false);
+ break;
+ case kItemIdVolumesMenu:
+ //debug("kItemIdVolumesMenu");
+ _newMenuID = kMenuIdVolumes;
+ break;
+ case kItemIdPlay:
+ //debug("kItemIdPlay");
+ _running = false;
+ break;
+ case kItemIdQuit:
+ _running = false;
+ _vm->quitGame();
+ break;
+ // Volumes menu
+ case kItemIdMasterUp:
+ changeVolumeBar(kItemIdMaster, +1);
+ break;
+ case kItemIdVoicesUp:
+ changeVolumeBar(kItemIdVoices, +1);
+ break;
+ case kItemIdMusicUp:
+ changeVolumeBar(kItemIdMusic, +1);
+ break;
+ case kItemIdSoundFXUp:
+ changeVolumeBar(kItemIdSoundFX, +1);
+ break;
+ case kItemIdBackgroundUp:
+ changeVolumeBar(kItemIdBackground, +1);
+ break;
+ case kItemIdMasterDown:
+ changeVolumeBar(kItemIdMaster, -1);
+ break;
+ case kItemIdVoicesDown:
+ changeVolumeBar(kItemIdVoices, -1);
+ break;
+ case kItemIdMusicDown:
+ changeVolumeBar(kItemIdMusic, -1);
+ break;
+ case kItemIdSoundFXDown:
+ changeVolumeBar(kItemIdSoundFX, -1);
+ break;
+ case kItemIdBackgroundDown:
+ changeVolumeBar(kItemIdBackground, -1);
+ break;
+ case kItemIdCancel:
+ _newMenuID = kMenuIdMain;
+ break;
+ // Save/Load menu
+ case kItemIdSavegame1:
+ case kItemIdSavegame2:
+ case kItemIdSavegame3:
+ case kItemIdSavegame4:
+ case kItemIdSavegame5:
+ case kItemIdSavegame6:
+ case kItemIdSavegame7:
+ clickSavegameItem(id);
+ break;
+ case kItemIdDone:
+ _newMenuID = kMenuIdMain;
+ break;
+ case kItemIdSavegameUp:
+ scrollSavegames(-6);
+ break;
+ case kItemIdSavegameDown:
+ scrollSavegames(+6);
+ break;
+ default:
+ break;
+ }
+}
+
+void MenuSystem::restoreRect(int x, int y, int w, int h) {
+ byte *src = (byte*)_background->getBasePtr(x, y);
+ byte *dst = _vm->_screen->_frontScreen + x + y * 640;
+ while (h--) {
+ memcpy(dst, src, w);
+ src += 640;
+ dst += 640;
+ }
+}
+
+void MenuSystem::shadeRect(int x, int y, int w, int h, byte color1, byte color2) {
+ byte *src = (byte*)_background->getBasePtr(x, y);
+ for (int xc = 0; xc < w; xc++) {
+ src[xc] = color2;
+ src[xc + h * 640] = color1;
+ }
+ src += 640;
+ w -= 1;
+ h -= 1;
+ while (h--) {
+ src[0] = color2;
+ src[w] = color1;
+ src += 640;
+ }
+}
+
+void MenuSystem::drawString(int16 x, int16 y, int w, uint fontNum, byte color, const char *text) {
+ fontNum = _vm->_screen->getFontResIndex(fontNum);
+ Font font(_vm->_res->load(fontNum)->data);
+ if (w) {
+ x = x + w - font.getTextWidth((const byte*)text) / 2;
+ }
+ _vm->_screen->drawString(x, y - font.getHeight(), color, fontNum, (const byte*)text, -1, NULL, true);
+ _needRedraw = true;
+}
+
+int MenuSystem::loadSavegamesList() {
+
+ int maxSlotNum = -1;
+
+ _savegameListTopIndex = 0;
+ _savegames.clear();
+
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Toltecs::ToltecsEngine::SaveHeader header;
+ Common::String pattern = _vm->getTargetName();
+ pattern += ".???";
+
+ Common::StringArray filenames;
+ filenames = saveFileMan->listSavefiles(pattern.c_str());
+ Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ 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 > maxSlotNum)
+ maxSlotNum = slotNum;
+
+ if (slotNum >= 0 && slotNum <= 999) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
+ if (in) {
+ if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) {
+ _savegames.push_back(SavegameItem(slotNum, header.description));
+ //debug("%s -> %s", file->c_str(), header.description.c_str());
+ }
+ delete in;
+ }
+ }
+ }
+
+ return maxSlotNum;
+}
+
+MenuSystem::SavegameItem *MenuSystem::getSavegameItemByID(ItemID id) {
+ switch (id) {
+ case kItemIdSavegame1:
+ case kItemIdSavegame2:
+ case kItemIdSavegame3:
+ case kItemIdSavegame4:
+ case kItemIdSavegame5:
+ case kItemIdSavegame6:
+ case kItemIdSavegame7:
+ return &_savegames[_savegameListTopIndex + id - kItemIdSavegame1];
+ default:
+ return NULL;
+ }
+}
+
+void MenuSystem::setSavegameCaptions() {
+ uint index = _savegameListTopIndex;
+ setItemCaption(getItem(kItemIdSavegame1), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+ setItemCaption(getItem(kItemIdSavegame2), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+ setItemCaption(getItem(kItemIdSavegame3), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+ setItemCaption(getItem(kItemIdSavegame4), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+ setItemCaption(getItem(kItemIdSavegame5), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+ setItemCaption(getItem(kItemIdSavegame6), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+ setItemCaption(getItem(kItemIdSavegame7), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
+}
+
+void MenuSystem::scrollSavegames(int delta) {
+ int newPos = CLIP<int>(_savegameListTopIndex + delta, 0, _savegames.size() - 1);
+ _savegameListTopIndex = newPos;
+ restoreRect(80, 92, 440, 140);
+ setSavegameCaptions();
+ drawItem(kItemIdSavegame1, false);
+ drawItem(kItemIdSavegame2, false);
+ drawItem(kItemIdSavegame3, false);
+ drawItem(kItemIdSavegame4, false);
+ drawItem(kItemIdSavegame5, false);
+ drawItem(kItemIdSavegame6, false);
+ drawItem(kItemIdSavegame7, false);
+}
+
+void MenuSystem::clickSavegameItem(ItemID id) {
+ if (_currMenuID == kMenuIdLoad) {
+ SavegameItem *savegameItem = getSavegameItemByID(id);
+ //debug("slotNum = [%d]; description = [%s]", savegameItem->_slotNum, savegameItem->_description.c_str());
+ //_vm->loadgame(savegameItem->_filename.c_str());
+ _vm->requestLoadgame(savegameItem->_slotNum);
+ _running = false;
+ } else {
+ _editingDescription = true;
+ _editingDescriptionItem = getItem(id);
+ _editingDescriptionID = id;
+ _editingDescriptionItem->activeColor = 249;
+ _editingDescriptionItem->defaultColor = 249;
+ drawItem(_editingDescriptionID, true);
+ }
+}
+
+void MenuSystem::setCfgText(bool value, bool active) {
+ if (_cfgText != value) {
+ Item *item = getItem(kItemIdToggleText);
+ _cfgText = value;
+ restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
+ setItemCaption(item, _vm->getSysString(_cfgText ? kStrTextOn : kStrTextOff));
+ drawItem(kItemIdToggleText, true);
+ }
+}
+
+void MenuSystem::setCfgVoices(bool value, bool active) {
+ if (_cfgVoices != value) {
+ Item *item = getItem(kItemIdToggleVoices);
+ _cfgVoices = value;
+ restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
+ setItemCaption(item, _vm->getSysString(_cfgVoices ? kStrVoicesOn : kStrVoicesOff));
+ drawItem(kItemIdToggleVoices, true);
+ }
+}
+
+void MenuSystem::drawVolumeBar(ItemID itemID) {
+ int w = 440, y, volume;
+ char text[21];
+
+ switch (itemID) {
+ case kItemIdMaster:
+ y = 130 + 25 * 0;
+ volume = _cfgMasterVolume;
+ break;
+ case kItemIdVoices:
+ y = 130 + 25 * 1;
+ volume = _cfgVoicesVolume;
+ break;
+ case kItemIdMusic:
+ y = 130 + 25 * 2;
+ volume = _cfgMusicVolume;
+ break;
+ case kItemIdSoundFX:
+ y = 130 + 25 * 3;
+ volume = _cfgSoundFXVolume;
+ break;
+ case kItemIdBackground:
+ y = 130 + 25 * 4;
+ volume = _cfgBackgroundVolume;
+ break;
+ default:
+ return;
+ }
+
+ Font font(_vm->_res->load(_vm->_screen->getFontResIndex(1))->data);
+ restoreRect(390, y - font.getHeight(), 100, 25);
+
+ for (int i = 0; i < volume; i++)
+ text[i] = '|';
+ text[volume] = 0;
+
+ drawString(0, y, w, 0, 246, text);
+
+}
+
+void MenuSystem::changeVolumeBar(ItemID itemID, int delta) {
+
+ int *volume, newVolume;
+
+ switch (itemID) {
+ case kItemIdMaster:
+ volume = &_cfgMasterVolume;
+ break;
+ case kItemIdVoices:
+ volume = &_cfgVoicesVolume;
+ break;
+ case kItemIdMusic:
+ volume = &_cfgMusicVolume;
+ break;
+ case kItemIdSoundFX:
+ volume = &_cfgSoundFXVolume;
+ break;
+ case kItemIdBackground:
+ volume = &_cfgBackgroundVolume;
+ break;
+ default:
+ return;
+ }
+
+ newVolume = CLIP(*volume + delta, 0, 20);
+
+ if (newVolume != *volume) {
+ *volume = newVolume;
+ drawVolumeBar(itemID);
+ }
+
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/menu.h b/engines/toltecs/menu.h
new file mode 100644
index 0000000000..3e2c2da8d9
--- /dev/null
+++ b/engines/toltecs/menu.h
@@ -0,0 +1,167 @@
+/* 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 TOLTECS_MENU_H
+#define TOLTECS_MENU_H
+
+#include "common/array.h"
+#include "common/str-array.h"
+
+namespace Toltecs {
+
+enum MenuID {
+ kMenuIdNone,
+ kMenuIdMain,
+ kMenuIdSave,
+ kMenuIdLoad,
+ kMenuIdVolumes
+};
+
+enum ItemID {
+ kItemIdNone,
+ // Main menu
+ kItemIdSave,
+ kItemIdLoad,
+ kItemIdToggleText,
+ kItemIdToggleVoices,
+ kItemIdVolumesMenu,
+ kItemIdPlay,
+ kItemIdQuit,
+ // Volumes menu
+ kItemIdMasterUp,
+ kItemIdVoicesUp,
+ kItemIdMusicUp,
+ kItemIdSoundFXUp,
+ kItemIdBackgroundUp,
+ kItemIdMasterDown,
+ kItemIdVoicesDown,
+ kItemIdMusicDown,
+ kItemIdSoundFXDown,
+ kItemIdBackgroundDown,
+ kItemIdMaster,
+ kItemIdVoices,
+ kItemIdMusic,
+ kItemIdSoundFX,
+ kItemIdBackground,
+ kItemIdDone,
+ kItemIdCancel,
+ // Save/load menu
+ kItemIdSavegameUp,
+ kItemIdSavegameDown,
+ kItemIdSavegame1,
+ kItemIdSavegame2,
+ kItemIdSavegame3,
+ kItemIdSavegame4,
+ kItemIdSavegame5,
+ kItemIdSavegame6,
+ kItemIdSavegame7,
+ // TODO
+ kMenuIdDummy
+};
+
+class MenuSystem {
+
+public:
+ MenuSystem(ToltecsEngine *vm);
+ ~MenuSystem();
+
+ int run();
+ void update();
+ void handleEvents();
+
+protected:
+
+ struct Item {
+ Common::Rect rect;
+ ItemID id;
+ Common::String caption;
+ byte defaultColor, activeColor;
+ int x, y, w;
+ uint fontNum;
+ };
+
+ struct SavegameItem {
+ int _slotNum;
+ Common::String _description;
+ SavegameItem()
+ : _slotNum(-1), _description("") {}
+ SavegameItem(int slotNum, Common::String description)
+ : _slotNum(slotNum), _description(description) {}
+ };
+
+ ToltecsEngine *_vm;
+ Graphics::Surface *_background;
+
+ bool _running;
+ MenuID _currMenuID, _newMenuID;
+ ItemID _currItemID;
+ int _top;
+ int _savegameListTopIndex;
+ bool _editingDescription;
+ ItemID _editingDescriptionID;
+ Item *_editingDescriptionItem;
+ bool _needRedraw;
+
+ Common::Array<Item> _items;
+ Common::Array<SavegameItem> _savegames;
+
+ bool _cfgText, _cfgVoices;
+ int _cfgMasterVolume, _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume, _cfgBackgroundVolume;
+
+ void addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor);
+
+ void drawItem(ItemID itemID, bool active);
+ void handleMouseMove(int x, int y);
+ void handleMouseClick(int x, int y);
+ void handleKeyDown(const Common::KeyState& kbd);
+
+ ItemID findItemAt(int x, int y);
+ Item *getItem(ItemID id);
+ void setItemCaption(Item *item, const char *caption);
+
+ void initMenu(MenuID menuID);
+
+ void enterItem(ItemID id);
+ void leaveItem(ItemID id);
+ void clickItem(ItemID id);
+
+ void restoreRect(int x, int y, int w, int h);
+ void shadeRect(int x, int y, int w, int h, byte color1, byte color2);
+ void drawString(int16 x, int16 y, int w, uint fontNum, byte color, const char *text);
+
+ SavegameItem *getSavegameItemByID(ItemID id);
+
+ int loadSavegamesList();
+ void setSavegameCaptions();
+ void scrollSavegames(int delta);
+ void clickSavegameItem(ItemID id);
+ void setCfgText(bool value, bool active);
+ void setCfgVoices(bool value, bool active);
+ void drawVolumeBar(ItemID itemID);
+ void changeVolumeBar(ItemID itemID, int delta);
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_MENU_H */
diff --git a/engines/toltecs/microtiles.cpp b/engines/toltecs/microtiles.cpp
new file mode 100644
index 0000000000..38e4f6578e
--- /dev/null
+++ b/engines/toltecs/microtiles.cpp
@@ -0,0 +1,220 @@
+/* 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 "toltecs/microtiles.h"
+
+namespace Toltecs {
+
+MicroTileArray::MicroTileArray(int16 width, int16 height) {
+ _tilesW = (width / TileSize) + ((width % TileSize) > 0 ? 1 : 0);
+ _tilesH = (height / TileSize) + ((height % TileSize) > 0 ? 1 : 0);
+ _tiles = new BoundingBox[_tilesW * _tilesH];
+ clear();
+}
+
+MicroTileArray::~MicroTileArray() {
+ delete[] _tiles;
+}
+
+void MicroTileArray::addRect(Common::Rect r) {
+
+ int ux0, uy0, ux1, uy1;
+ int tx0, ty0, tx1, ty1;
+ int ix0, iy0, ix1, iy1;
+
+ r.clip(Common::Rect(0, 0, 639, 399));
+
+ ux0 = r.left / TileSize;
+ uy0 = r.top / TileSize;
+ ux1 = r.right / TileSize;
+ uy1 = r.bottom / TileSize;
+
+ tx0 = r.left % TileSize;
+ ty0 = r.top % TileSize;
+ tx1 = r.right % TileSize;
+ ty1 = r.bottom % TileSize;
+
+ for (int yc = uy0; yc <= uy1; yc++) {
+ for (int xc = ux0; xc <= ux1; xc++) {
+ ix0 = (xc == ux0) ? tx0 : 0;
+ ix1 = (xc == ux1) ? tx1 : TileSize - 1;
+ iy0 = (yc == uy0) ? ty0 : 0;
+ iy1 = (yc == uy1) ? ty1 : TileSize - 1;
+ updateBoundingBox(_tiles[xc + yc * _tilesW], ix0, iy0, ix1, iy1);
+ }
+ }
+
+}
+
+void MicroTileArray::clear() {
+ memset(_tiles, 0, _tilesW * _tilesH * sizeof(BoundingBox));
+}
+
+byte MicroTileArray::TileX0(const BoundingBox &boundingBox) {
+ return (boundingBox >> 24) & 0xFF;
+}
+
+byte MicroTileArray::TileY0(const BoundingBox &boundingBox) {
+ return (boundingBox >> 16) & 0xFF;
+}
+
+byte MicroTileArray::TileX1(const BoundingBox &boundingBox) {
+ return (boundingBox >> 8) & 0xFF;
+}
+
+byte MicroTileArray::TileY1(const BoundingBox &boundingBox) {
+ return boundingBox & 0xFF;
+}
+
+bool MicroTileArray::isBoundingBoxEmpty(const BoundingBox &boundingBox) {
+ return boundingBox == EmptyBoundingBox;
+}
+
+bool MicroTileArray::isBoundingBoxFull(const BoundingBox &boundingBox) {
+ return boundingBox == FullBoundingBox;
+}
+
+void MicroTileArray::setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) {
+ boundingBox = (x0 << 24) | (y0 << 16) | (x1 << 8) | y1;
+}
+
+void MicroTileArray::updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) {
+ if (!isBoundingBoxEmpty(boundingBox)) {
+ x0 = MIN(TileX0(boundingBox), x0);
+ y0 = MIN(TileY0(boundingBox), y0);
+ x1 = MAX(TileX1(boundingBox), x1);
+ y1 = MAX(TileY1(boundingBox), y1);
+ }
+ setBoundingBox(boundingBox, x0, y0, x1, y1);
+}
+
+Common::Rect * MicroTileArray::getRectangles(int *num_rects, int min_x, int min_y, int max_x, int max_y) {
+
+ Common::Rect *rects = new Common::Rect[_tilesW * _tilesH];
+
+ int n_rects = 0;
+ int x, y;
+ int x0, y0, x1, y1;
+ int i = 0;
+
+ for (y = 0; y < _tilesH; ++y) {
+ for (x = 0; x < _tilesW; ++x) {
+
+ int start;
+ int finish = 0;
+ BoundingBox boundingBox;
+
+ boundingBox = _tiles[i];
+
+ if (isBoundingBoxEmpty(boundingBox))
+ goto next;
+
+ x0 = (x * TileSize) + TileX0(boundingBox);
+ y0 = (y * TileSize) + TileY0(boundingBox);
+ y1 = (y * TileSize) + TileY1(boundingBox);
+
+ x0 = CLIP (x0, min_x, max_x);
+ y0 = CLIP (y0, min_y, max_y);
+ y1 = CLIP (y1, min_y, max_y);
+
+ // FIXME: Why is the following code in an #if block?
+#if 1
+ start = i;
+
+ if (TileX1(boundingBox) != TileSize - 1 || x == _tilesW - 1) {
+ /* the tile does not continue */
+ goto done;
+ }
+
+ while (!finish) {
+ ++x;
+ ++i;
+
+ if (x == _tilesW || i >= _tilesW * _tilesH ||
+ TileY0(_tiles[i]) != TileY0(boundingBox) ||
+ TileY1(_tiles[i]) != TileY1(boundingBox) ||
+ TileX0(_tiles[i]) != 0)
+ {
+ --x;
+ --i;
+ finish = 1;
+ }
+ }
+
+ done:
+#endif
+ x1 = (x * TileSize) + TileX1(_tiles[i]);
+
+ x1 = CLIP (x1, min_x, max_x);
+
+ // FIXME: Why is the following code in an #if block?
+
+ #if 1
+
+ rects[n_rects].left = x0;
+ rects[n_rects].top = y0;
+ rects[n_rects].right = x1 + 1;
+ rects[n_rects].bottom = y1 + 1;
+ n_rects++;
+
+ #else
+
+ // FIXME: Why is this code disabled?
+
+ if (glom [start] != -1 && /* try to glom */
+ rects [glom [start]].left == x0 &&
+ rects [glom [start]].right == x1 &&
+ rects [glom [start]].bottom == y0 - 1)
+ {
+ rects [glom [start]].bottom = y1;
+ if (y != tilesH - 1) {
+ glom [start + tilesW] = glom [start];
+ }
+ } else {
+ rects[n_rects].left = x0;
+ rects[n_rects].top = y0;
+ rects[n_rects].right = x1;
+ rects[n_rects].bottom = y1;
+ if (y != tilesH - 1) {
+ glom [start + tilesW] = n_rects;
+ }
+ n_rects ++;
+ }
+
+ #endif
+
+ next:
+ ++i;
+
+ }
+ }
+
+ *num_rects = n_rects;
+
+ //delete glom;
+
+ return rects;
+
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/microtiles.h b/engines/toltecs/microtiles.h
new file mode 100644
index 0000000000..53933621a9
--- /dev/null
+++ b/engines/toltecs/microtiles.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef TOLTECS_MICROTILES_H
+#define TOLTECS_MICROTILES_H
+
+#include "common/scummsys.h"
+#include "common/util.h"
+#include "common/rect.h"
+
+namespace Toltecs {
+
+typedef uint32 BoundingBox;
+
+const BoundingBox FullBoundingBox = 0x00001F1F;
+const BoundingBox EmptyBoundingBox = 0x00000000;
+const int TileSize = 32;
+
+class MicroTileArray {
+public:
+ MicroTileArray(int16 width, int16 height);
+ ~MicroTileArray();
+ void addRect(Common::Rect r);
+ void clear();
+ Common::Rect *getRectangles(int *num_rects, int min_x, int min_y, int max_x, int max_y);
+protected:
+ BoundingBox *_tiles;
+ int16 _tilesW, _tilesH;
+ byte TileX0(const BoundingBox &boundingBox);
+ byte TileY0(const BoundingBox &boundingBox);
+ byte TileX1(const BoundingBox &boundingBox);
+ byte TileY1(const BoundingBox &boundingBox);
+ bool isBoundingBoxEmpty(const BoundingBox &boundingBox);
+ bool isBoundingBoxFull(const BoundingBox &boundingBox);
+ void setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1);
+ void updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1);
+};
+
+} // namespace Toltecs
+
+#endif // TOLTECS_MICROTILES_H
diff --git a/engines/toltecs/module.mk b/engines/toltecs/module.mk
new file mode 100644
index 0000000000..aa4a6f376b
--- /dev/null
+++ b/engines/toltecs/module.mk
@@ -0,0 +1,28 @@
+MODULE := engines/toltecs
+
+MODULE_OBJS = \
+ animation.o \
+ detection.o \
+ menu.o \
+ microtiles.o \
+ movie.o \
+ music.o \
+ palette.o \
+ toltecs.o \
+ render.o \
+ resource.o \
+ saveload.o \
+ screen.o \
+ script.o \
+ segmap.o \
+ sound.o \
+ sprite.o
+
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_TOLTECS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp
new file mode 100644
index 0000000000..054683b63e
--- /dev/null
+++ b/engines/toltecs/movie.cpp
@@ -0,0 +1,290 @@
+/* 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 "audio/mixer.h"
+#include "audio/decoders/raw.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/movie.h"
+#include "toltecs/palette.h"
+#include "toltecs/resource.h"
+#include "toltecs/screen.h"
+#include "toltecs/script.h"
+
+namespace Toltecs {
+
+enum ChunkTypes {
+ kChunkFirstImage = 0,
+ kChunkSubsequentImages = 1,
+ kChunkPalette = 2,
+ kChunkUnused = 3,
+ kChunkAudio = 4,
+ kChunkShowSubtitle = 5,
+ kChunkShakeScreen = 6,
+ kChunkSetupSubtitles = 7,
+ kChunkStopSubtitles = 8
+};
+
+MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm) {
+}
+
+MoviePlayer::~MoviePlayer() {
+}
+
+void MoviePlayer::playMovie(uint resIndex) {
+
+ const uint32 subtitleSlot = kMaxScriptSlots - 1;
+ int16 savedSceneWidth = _vm->_sceneWidth;
+ int16 savedSceneHeight = _vm->_sceneHeight;
+ int16 savedCameraHeight = _vm->_cameraHeight;
+ int16 savedCameraX = _vm->_cameraX;
+ int16 savedCameraY = _vm->_cameraY;
+ int16 savedGuiHeight = _vm->_guiHeight;
+ byte moviePalette[768];
+
+ _vm->_isSaveAllowed = false;
+
+ memset(moviePalette, 0, sizeof(moviePalette));
+
+ _vm->_screen->finishTalkTextItems();
+ _vm->_screen->clearSprites();
+
+ _vm->_arc->openResource(resIndex);
+
+ _frameCount = _vm->_arc->readUint32LE();
+ _chunkCount = _vm->_arc->readUint32LE();
+
+ // TODO: Figure out rest of the header
+ _vm->_arc->readUint32LE();
+ _vm->_arc->readUint32LE();
+ _framesPerSoundChunk = _vm->_arc->readUint32LE();
+ _vm->_arc->readUint32LE();
+
+ _vm->_sceneWidth = 640;
+ _vm->_sceneHeight = 400;
+ _vm->_cameraHeight = 400;
+ _vm->_cameraX = 0;
+ _vm->_cameraY = 0;
+ _vm->_guiHeight = 0;
+
+ _audioStream = Audio::makeQueuingAudioStream(22050, false);
+
+ _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream);
+
+ _soundChunkFramesLeft = 0;
+ _lastPrefetchOfs = 0;
+
+ fetchAudioChunks();
+
+ uint32 lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle);
+
+ while (_chunkCount--) {
+
+ byte chunkType = _vm->_arc->readByte();
+ uint32 chunkSize = _vm->_arc->readUint32LE();
+ byte *chunkBuffer = NULL;
+ uint32 movieOffset;
+
+ debug(0, "chunkType = %d; chunkSize = %d", chunkType, chunkSize);
+
+ // Skip audio chunks - we've already queued them in
+ // fetchAudioChunks() above
+ if (chunkType == kChunkAudio) {
+ _vm->_arc->skip(chunkSize);
+ } else {
+ chunkBuffer = new byte[chunkSize];
+ _vm->_arc->read(chunkBuffer, chunkSize);
+ }
+
+ movieOffset = _vm->_arc->pos();
+
+ switch (chunkType) {
+ case kChunkFirstImage:
+ case kChunkSubsequentImages:
+ unpackRle(chunkBuffer, _vm->_screen->_backScreen);
+ // TODO: Rework this
+ _vm->_screen->updateShakeScreen();
+ _vm->_screen->_fullRefresh = true;
+ _vm->updateInput();
+ _vm->drawScreen();
+
+ _soundChunkFramesLeft--;
+ if (_soundChunkFramesLeft <= _framesPerSoundChunk) {
+ fetchAudioChunks();
+ }
+
+ while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < lastTime + 111) {
+ g_system->delayMillis(10);
+ }
+
+ lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle);
+
+ break;
+ case kChunkPalette:
+ unpackPalette(chunkBuffer, moviePalette, 256, 3);
+ _vm->_palette->setFullPalette(moviePalette);
+ break;
+ case kChunkUnused:
+ error("Chunk considered to be unused has been encountered");
+ case kChunkAudio:
+ // Already processed
+ break;
+ case kChunkShowSubtitle:
+ // TODO: Check if the text is a subtitle (last character == 0xFE).
+ // If so, don't show it if text display is disabled.
+ memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize);
+ _vm->_screen->updateTalkText(subtitleSlot, 0);
+ break;
+ case kChunkShakeScreen: // start/stop shakescreen effect
+ if (chunkBuffer[0] == 0xFF)
+ _vm->_screen->stopShakeScreen();
+ else
+ _vm->_screen->startShakeScreen(chunkBuffer[0]);
+ break;
+ case kChunkSetupSubtitles: // setup subtitle parameters
+ _vm->_screen->_talkTextY = READ_LE_UINT16(chunkBuffer + 0);
+ _vm->_screen->_talkTextX = READ_LE_UINT16(chunkBuffer + 2);
+ _vm->_screen->_talkTextFontColor = ((chunkBuffer[4] << 4) & 0xF0) | ((chunkBuffer[4] >> 4) & 0x0F);
+ debug(0, "_talkTextX = %d; _talkTextY = %d; _talkTextFontColor = %d",
+ _vm->_screen->_talkTextX, _vm->_screen->_talkTextY, _vm->_screen->_talkTextFontColor);
+ break;
+ case kChunkStopSubtitles:
+ _vm->_script->getSlotData(subtitleSlot)[0] = 0xFF;
+ _vm->_screen->finishTalkTextItems();
+ break;
+ default:
+ error("MoviePlayer::playMovie(%04X) Unknown chunk type %d at %08X", resIndex, chunkType, _vm->_arc->pos() - 5 - chunkSize);
+ }
+
+ delete[] chunkBuffer;
+
+ _vm->_arc->seek(movieOffset, SEEK_SET);
+
+ if (!handleInput())
+ break;
+
+ }
+
+ _audioStream->finish();
+ _vm->_mixer->stopHandle(_audioStreamHandle);
+
+ _vm->_arc->closeResource();
+
+ debug(0, "playMovie() done");
+
+ _vm->_sceneWidth = savedSceneWidth;
+ _vm->_sceneHeight = savedSceneHeight;
+ _vm->_cameraHeight = savedCameraHeight;
+ _vm->_cameraX = savedCameraX;
+ _vm->_cameraY = savedCameraY;
+ _vm->_guiHeight = savedGuiHeight;
+
+ _vm->_isSaveAllowed = true;
+}
+
+void MoviePlayer::fetchAudioChunks() {
+
+ uint32 startOfs = _vm->_arc->pos();
+ uint32 chunkCount = _chunkCount;
+ uint prefetchChunkCount = 0;
+
+ if (_lastPrefetchOfs != 0)
+ _vm->_arc->seek(_lastPrefetchOfs, SEEK_SET);
+
+ while (chunkCount-- && prefetchChunkCount < _framesPerSoundChunk / 2) {
+ byte chunkType = _vm->_arc->readByte();
+ uint32 chunkSize = _vm->_arc->readUint32LE();
+ if (chunkType == 4) {
+ byte *chunkBuffer = (byte*)malloc(chunkSize);
+ _vm->_arc->read(chunkBuffer, chunkSize);
+ _audioStream->queueBuffer(chunkBuffer, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+ chunkBuffer = NULL;
+ prefetchChunkCount++;
+ _soundChunkFramesLeft += _framesPerSoundChunk;
+ } else {
+ _vm->_arc->seek(chunkSize, SEEK_CUR);
+ }
+ }
+
+ _lastPrefetchOfs = _vm->_arc->pos();
+
+ _vm->_arc->seek(startOfs, SEEK_SET);
+
+}
+
+void MoviePlayer::unpackPalette(byte *source, byte *dest, int elemCount, int elemSize) {
+ int ofs = 0, size = elemCount * elemSize;
+ while (ofs < size) {
+ byte len;
+ len = *source++;
+ if (len == 0) {
+ len = *source++;
+ } else {
+ byte value = *source++;
+ memset(dest, value, len);
+ }
+ ofs += len;
+ dest += len;
+ }
+}
+
+void MoviePlayer::unpackRle(byte *source, byte *dest) {
+ int size = 256000;
+ while (size > 0) {
+ byte a = *source++;
+ byte b = *source++;
+ if (a == 0) {
+ dest += b;
+ size -= b;
+ } else {
+ memset(dest, b, a);
+ dest += a;
+ size -= a;
+ }
+ }
+}
+
+bool MoviePlayer::handleInput() {
+ Common::Event event;
+ Common::EventManager *eventMan = g_system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+ return false;
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_RBUTTONDOWN:
+ return false;
+ case Common::EVENT_QUIT:
+ _vm->quitGame();
+ return false;
+ break;
+ default:
+ break;
+ }
+ }
+ return !_vm->shouldQuit();
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/movie.h b/engines/toltecs/movie.h
new file mode 100644
index 0000000000..aecfac240f
--- /dev/null
+++ b/engines/toltecs/movie.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.
+ *
+ *
+ */
+
+#ifndef TOLTECS_MOVIE_H
+#define TOLTECS_MOVIE_H
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h" // for Audio::SoundHandle
+
+namespace Toltecs {
+
+class MoviePlayer {
+
+public:
+ MoviePlayer(ToltecsEngine *vm);
+ ~MoviePlayer();
+
+ void playMovie(uint resIndex);
+
+protected:
+ ToltecsEngine *_vm;
+ Audio::QueuingAudioStream *_audioStream;
+ Audio::SoundHandle _audioStreamHandle;
+
+ uint32 _chunkCount, _frameCount, _lastPrefetchOfs;
+ uint32 _soundChunkFramesLeft, _framesPerSoundChunk;
+
+ void unpackPalette(byte *source, byte *dest, int elemCount, int elemSize);
+ void unpackRle(byte *source, byte *dest);
+
+ void fetchAudioChunks();
+
+ bool handleInput();
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_MOVIE_H */
diff --git a/engines/toltecs/music.cpp b/engines/toltecs/music.cpp
new file mode 100644
index 0000000000..ef44421de9
--- /dev/null
+++ b/engines/toltecs/music.cpp
@@ -0,0 +1,104 @@
+/* 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.
+ *
+ */
+
+// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume).
+
+#include "toltecs/music.h"
+
+#include "audio/midiparser.h"
+
+namespace Toltecs {
+
+MusicPlayer::MusicPlayer(bool isGM) : _isGM(isGM), _buffer(NULL) {
+ MidiPlayer::createDriver();
+
+ int ret = _driver->open();
+ if (ret == 0) {
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
+ _driver->setTimerCallback(this, &timerCallback);
+ }
+}
+
+void MusicPlayer::send(uint32 b) {
+ if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ }
+
+ Audio::MidiPlayer::send(b);
+}
+
+void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) {
+ Common::StackLock lock(_mutex);
+
+ stopAndClear();
+
+ _buffer = new byte[size];
+ memcpy(_buffer, data, size);
+
+ MidiParser *parser;
+
+ if (!memcmp(data, "FORM", 4))
+ parser = MidiParser::createParser_XMIDI(NULL);
+ else
+ parser = MidiParser::createParser_SMF();
+
+ if (parser->loadMusic(_buffer, size)) {
+ parser->setTrack(0);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(_driver->getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+ parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
+
+ _parser = parser;
+
+ setVolume(127);
+
+ _isLooping = loop;
+ _isPlaying = true;
+ } else {
+ delete parser;
+ }
+}
+
+void MusicPlayer::pause() {
+ setVolume(-1);
+ _isPlaying = false;
+}
+
+void MusicPlayer::resume() {
+ setVolume(127);
+ _isPlaying = true;
+}
+
+void MusicPlayer::stopAndClear() {
+ Common::StackLock lock(_mutex);
+ stop();
+
+ delete[] _buffer;
+ _buffer = NULL;
+}
+
+} // End of namespace Made
diff --git a/engines/toltecs/music.h b/engines/toltecs/music.h
new file mode 100644
index 0000000000..c7189e89d6
--- /dev/null
+++ b/engines/toltecs/music.h
@@ -0,0 +1,53 @@
+/* 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.
+ *
+ */
+
+// Music class
+
+#ifndef MADE_MUSIC_H
+#define MADE_MUSIC_H
+
+#include "audio/midiplayer.h"
+
+namespace Toltecs {
+
+class MusicPlayer : public Audio::MidiPlayer {
+public:
+ MusicPlayer(bool isGM = true);
+
+ void playMIDI(const byte *data, uint32 size, bool loop = false);
+ void pause();
+ void resume();
+ void stopAndClear();
+
+ // MidiDriver_BASE interface implementation
+ virtual void send(uint32 b);
+
+protected:
+ bool _isGM;
+
+private:
+ byte *_buffer;
+};
+
+} // End of namespace Made
+
+#endif
diff --git a/engines/toltecs/palette.cpp b/engines/toltecs/palette.cpp
new file mode 100644
index 0000000000..8857abc698
--- /dev/null
+++ b/engines/toltecs/palette.cpp
@@ -0,0 +1,244 @@
+/* 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 "graphics/palette.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/palette.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+Palette::Palette(ToltecsEngine *vm) : _vm(vm) {
+
+ clearFragments();
+
+ memset(_colorTransTable, 0, sizeof(_colorTransTable));
+
+}
+
+Palette::~Palette() {
+}
+
+void Palette::setFullPalette(byte *palette) {
+ byte colors[768];
+ for (int i = 0; i < 256; i++) {
+ colors[i * 3 + 0] = palette[i * 3 + 0] << 2;
+ colors[i * 3 + 1] = palette[i * 3 + 1] << 2;
+ colors[i * 3 + 2] = palette[i * 3 + 2] << 2;
+ }
+ _vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256);
+ _vm->_system->updateScreen();
+}
+
+void Palette::getFullPalette(byte *palette) {
+ byte colors[768];
+ _vm->_system->getPaletteManager()->grabPalette(colors, 0, 256);
+ for (int i = 0; i < 256; i++) {
+ palette[i * 3 + 0] = colors[i * 3 + 0] >> 2;
+ palette[i * 3 + 1] = colors[i * 3 + 1] >> 2;
+ palette[i * 3 + 2] = colors[i * 3 + 2] >> 2;
+ }
+}
+
+void Palette::setDeltaPalette(byte *palette, byte mask, char deltaValue, int16 count, int16 startIndex) {
+ byte colors[768];
+
+ byte *palPtr = palette + startIndex * 3;
+ int16 index = startIndex, colorCount = count;
+ byte rgb;
+
+ count++;
+
+ _vm->_system->getPaletteManager()->grabPalette(colors, 0, 256);
+
+ deltaValue *= -1;
+
+ while (count--) {
+ rgb = *palPtr++;
+ if (mask & 1) colors[index * 3 + 0] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
+ rgb = *palPtr++;
+ if (mask & 2) colors[index * 3 + 1] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
+ rgb = *palPtr++;
+ if (mask & 4) colors[index * 3 + 2] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
+ index++;
+ }
+
+ debug(0, "startIndex = %d; colorCount = %d", startIndex, colorCount);
+
+ _vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256);
+}
+
+void Palette::loadAddPalette(uint resIndex, byte startIndex) {
+ Resource *paletteResource = _vm->_res->load(resIndex);
+ memcpy(&_mainPalette[startIndex * 3], paletteResource->data, paletteResource->size);
+}
+
+void Palette::loadAddPaletteFrom(byte *source, byte startIndex, byte count) {
+ memcpy(&_mainPalette[startIndex * 3], source, count * 3);
+}
+
+void Palette::addFragment(uint resIndex, int16 id) {
+ debug(0, "Palette::addFragment(%d, %d)", resIndex, id);
+
+ Resource *fragmentResource = _vm->_res->load(resIndex);
+ byte count = fragmentResource->size / 3;
+
+ memcpy(&_mainPalette[_fragmentIndex * 3], fragmentResource->data, count * 3);
+
+ PaletteFragment fragment;
+ fragment.id = id;
+ fragment.index = _fragmentIndex;
+ fragment.count = count;
+ _fragments.push_back(fragment);
+
+ debug(0, "Palette::addFragment() index = %02X; count = %02X", fragment.index, fragment.count);
+
+ _fragmentIndex += count;
+
+}
+
+uint16 Palette::findFragment(int16 id) {
+ debug(0, "Palette::findFragment(%d)", id);
+
+ uint16 result = 0;
+ for (PaletteFragmentArray::iterator iter = _fragments.begin(); iter != _fragments.end(); iter++) {
+ PaletteFragment fragment = *iter;
+ if (fragment.id == id) {
+ result = (fragment.count << 8) | fragment.index;
+ break;
+ }
+ }
+
+ debug(0, "Palette::findFragment() result = %04X", result);
+
+ return result;
+}
+
+void Palette::clearFragments() {
+ debug(0, "Palette::clearFragments()");
+ _fragmentIndex = 128;
+ _fragments.clear();
+}
+
+void Palette::buildColorTransTable(byte limit, char deltaValue, byte mask) {
+
+ // TODO
+
+ byte r = 0, g = 0, b = 0;
+
+ mask &= 7;
+
+ for (int i = 0; i < 256; i++) {
+
+ if (deltaValue < 0) {
+ // TODO
+ warning("Palette::buildColorTransTable(%d, %d, %02X) not yet implemented!", limit, deltaValue, mask);
+ } else {
+ r = _mainPalette[i * 3 + 0];
+ g = _mainPalette[i * 3 + 1];
+ b = _mainPalette[i * 3 + 2];
+ if (MAX(r, MAX(b, g)) >= limit) {
+ if ((mask & 1) && r >= deltaValue)
+ r -= deltaValue;
+ if ((mask & 2) && g >= deltaValue)
+ g -= deltaValue;
+ if ((mask & 4) && b >= deltaValue)
+ b -= deltaValue;
+ }
+ }
+
+ int bestIndex = 0;
+ uint16 bestMatch = 0xFFFF;
+
+ for (int j = 0; j < 256; j++) {
+ byte distance = ABS(_mainPalette[j * 3 + 0] - r) + ABS(_mainPalette[j * 3 + 1] - g) + ABS(_mainPalette[j * 3 + 2] - b);
+ byte maxColor = MAX(_mainPalette[j * 3 + 0], MAX(_mainPalette[j * 3 + 1], _mainPalette[j * 3 + 2]));
+ uint16 match = (distance << 8) | maxColor;
+ if (match < bestMatch) {
+ bestMatch = match;
+ bestIndex = j;
+ }
+ }
+
+ _colorTransTable[i] = bestIndex;
+
+ }
+
+}
+
+void Palette::buildColorTransTable2(byte limit, char deltaValue, byte mask) {
+
+ // TODO
+
+}
+
+void Palette::saveState(Common::WriteStream *out) {
+
+ // Save currently active palette
+ byte palette[768];
+ getFullPalette(palette);
+ out->write(palette, 768);
+
+ out->write(_mainPalette, 768);
+ out->write(_animPalette, 768);
+ out->write(_colorTransTable, 256);
+
+ uint16 fragmentCount = _fragments.size();
+ out->writeUint16LE(fragmentCount);
+ for (PaletteFragmentArray::iterator iter = _fragments.begin(); iter != _fragments.end(); iter++) {
+ PaletteFragment fragment = *iter;
+ out->writeUint16LE(fragment.id);
+ out->writeByte(fragment.index);
+ out->writeByte(fragment.count);
+ }
+ out->writeByte(_fragmentIndex);
+
+}
+
+void Palette::loadState(Common::ReadStream *in) {
+
+ // Save currently active palette
+ byte palette[768];
+ in->read(palette, 768);
+ setFullPalette(palette);
+
+ in->read(_mainPalette, 768);
+ in->read(_animPalette, 768);
+ in->read(_colorTransTable, 256);
+
+ uint16 fragmentCount = in->readUint16LE();
+ _fragments.clear();
+ for (uint16 i = 0; i < fragmentCount; i++) {
+ PaletteFragment fragment;
+ fragment.id = in->readUint16LE();
+ fragment.index = in->readByte();
+ fragment.count = in->readByte();
+ _fragments.push_back(fragment);
+ }
+ _fragmentIndex = in->readByte();
+
+}
+
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/palette.h b/engines/toltecs/palette.h
new file mode 100644
index 0000000000..140d59b431
--- /dev/null
+++ b/engines/toltecs/palette.h
@@ -0,0 +1,85 @@
+/* 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 TOLTECS_PALETTE_H
+#define TOLTECS_PALETTE_H
+
+#include "common/array.h"
+#include "common/system.h"
+
+#include "toltecs/toltecs.h"
+
+namespace Toltecs {
+
+//#define ROT(index) (((index << 4) & 0xF0) | ((index >> 4) & 0x0F))
+//#define ROT(index) (index)
+
+class Palette {
+public:
+ Palette(ToltecsEngine *vm);
+ ~Palette();
+
+ void setFullPalette(byte *palette);
+ void getFullPalette(byte *palette);
+ void setDeltaPalette(byte *palette, byte mask, char deltaValue, int16 count, int16 startIndex);
+
+ void loadAddPalette(uint resIndex, byte startIndex);
+ void loadAddPaletteFrom(byte *source, byte startIndex, byte count);
+
+ void addFragment(uint resIndex, int16 id);
+ uint16 findFragment(int16 id);
+ void clearFragments();
+
+ void buildColorTransTable(byte limit, char deltaValue, byte mask);
+ void buildColorTransTable2(byte limit, char deltaValue, byte mask);
+ byte getColorTransPixel(byte pixel) const { return _colorTransTable[pixel]; }
+
+ byte *getMainPalette() { return _mainPalette; }
+ byte *getAnimPalette() { return _animPalette; }
+
+ void saveState(Common::WriteStream *out);
+ void loadState(Common::ReadStream *in);
+
+protected:
+
+ struct PaletteFragment {
+ int16 id;
+ byte index, count;
+ };
+
+ typedef Common::Array<PaletteFragment> PaletteFragmentArray;
+
+ ToltecsEngine *_vm;
+
+ byte _mainPalette[768];
+ byte _animPalette[768];
+ byte _colorTransTable[256];
+
+ PaletteFragmentArray _fragments;
+ byte _fragmentIndex;
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_PALETTE_H */
diff --git a/engines/toltecs/render.cpp b/engines/toltecs/render.cpp
new file mode 100644
index 0000000000..de5e77fb50
--- /dev/null
+++ b/engines/toltecs/render.cpp
@@ -0,0 +1,311 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "common/system.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/render.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+Common::Rect makeRect(int16 x, int16 y, int16 width, int16 height) {
+ Common::Rect rect;
+ rect.left = x;
+ rect.top = y;
+ rect.setWidth(width);
+ rect.setHeight(height);
+ return rect;
+}
+
+RenderQueue::RenderQueue(ToltecsEngine *vm) : _vm(vm) {
+ _currQueue = new RenderQueueArray();
+ _prevQueue = new RenderQueueArray();
+ _updateUta = new MicroTileArray(640, 400);
+}
+
+RenderQueue::~RenderQueue() {
+ delete _currQueue;
+ delete _prevQueue;
+ delete _updateUta;
+}
+
+void RenderQueue::addSprite(SpriteDrawItem &sprite) {
+
+ RenderQueueItem item;
+ item.type = kSprite;
+ item.flags = kRefresh;
+ item.rect = makeRect(sprite.x - _vm->_cameraX, sprite.y - _vm->_cameraY, sprite.width, sprite.height);
+ item.priority = sprite.priority;
+
+ item.sprite = sprite;
+ item.sprite.x -= _vm->_cameraX;
+ item.sprite.y -= _vm->_cameraY;
+
+ // Add sprite sorted by priority
+ RenderQueueArray::iterator iter = _currQueue->begin();
+ while (iter != _currQueue->end() && (*iter).priority <= item.priority) {
+ iter++;
+ }
+ _currQueue->insert(iter, item);
+
+}
+
+void RenderQueue::addText(int16 x, int16 y, byte color, uint fontResIndex, byte *text, int len) {
+
+ Font font(_vm->_res->load(fontResIndex)->data);
+
+ RenderQueueItem item;
+ item.type = kText;
+ item.flags = kRefresh;
+ item.rect = makeRect(x, y, font.getTextWidth(text), font.getHeight());
+ item.priority = 1000;
+
+ item.text.color = color;
+ item.text.fontResIndex = fontResIndex;
+ item.text.text = text;
+ item.text.len = len;
+
+ _currQueue->push_back(item);
+
+}
+
+void RenderQueue::addMask(SegmapMaskRect &mask) {
+
+ RenderQueueItem item;
+ item.type = kMask;
+ item.flags = kRefresh;
+ item.rect = makeRect(mask.x - _vm->_cameraX, mask.y - _vm->_cameraY, mask.width, mask.height);
+ item.priority = mask.priority;
+
+ item.mask = mask;
+
+ // Only add the mask if a sprite intersects its rect
+ if (rectIntersectsItem(item.rect)) {
+ RenderQueueArray::iterator iter = _currQueue->begin();
+ while (iter != _currQueue->end() && (*iter).priority <= item.priority) {
+ iter++;
+ }
+ _currQueue->insert(iter, item);
+ }
+
+}
+
+void RenderQueue::update() {
+
+ bool doFullRefresh = _vm->_screen->_fullRefresh;
+
+ _updateUta->clear();
+
+ if (!doFullRefresh) {
+
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ RenderQueueItem *item = &(*iter);
+ RenderQueueItem *prevItem = findItemInQueue(_prevQueue, *item);
+ if (prevItem) {
+ if (hasItemChanged(*prevItem, *item)) {
+ item->flags = kRefresh;
+ addDirtyRect(prevItem->rect);
+ } else {
+ item->flags = kUnchanged;
+ }
+ } else {
+ item->flags = kRefresh;
+ }
+ }
+
+ for (RenderQueueArray::iterator iter = _prevQueue->begin(); iter != _prevQueue->end(); iter++) {
+ RenderQueueItem *prevItem = &(*iter);
+ RenderQueueItem *item = findItemInQueue(_currQueue, *prevItem);
+ if (!item) {
+ prevItem->flags = kRemoved;
+ addDirtyRect(prevItem->rect);
+ }
+ }
+
+ restoreDirtyBackground();
+
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ RenderQueueItem *item = &(*iter);
+ if (item->flags != kUnchanged)
+ invalidateItemsByRect(item->rect, item);
+ }
+
+ } else {
+ byte *destp = _vm->_screen->_frontScreen;
+ byte *srcp = _vm->_screen->_backScreen + _vm->_cameraX + _vm->_cameraY * _vm->_sceneWidth;
+ int16 w = MIN<int16>(640, _vm->_sceneWidth);
+ int16 h = MIN<int16>(400, _vm->_cameraHeight);
+ while (h--) {
+ memcpy(destp, srcp, w);
+ destp += 640;
+ srcp += _vm->_sceneWidth;
+ }
+ _vm->_screen->_fullRefresh = false;
+ }
+
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ const RenderQueueItem *item = &(*iter);
+
+ if (item->flags == kRefresh || doFullRefresh) {
+
+ switch (item->type) {
+ case kSprite:
+ _vm->_screen->drawSprite(item->sprite);
+ break;
+ case kText:
+ _vm->_screen->drawString(item->rect.left, item->rect.top, item->text.color, item->text.fontResIndex,
+ item->text.text, item->text.len, NULL, true);
+ break;
+ case kMask:
+ _vm->_screen->drawSurface(item->rect.left, item->rect.top, item->mask.surface);
+ break;
+ default:
+ break;
+ }
+
+ if (!doFullRefresh)
+ addDirtyRect(item->rect);
+
+ }
+
+ }
+
+ if (doFullRefresh) {
+ clear();
+ _vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, _vm->_cameraHeight);
+ } else {
+ updateDirtyRects();
+ }
+
+ SWAP(_currQueue, _prevQueue);
+ _currQueue->clear();
+
+}
+
+void RenderQueue::clear() {
+ _prevQueue->clear();
+ _currQueue->clear();
+}
+
+bool RenderQueue::rectIntersectsItem(const Common::Rect &rect) {
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ const RenderQueueItem *item = &(*iter);
+ if (rect.intersects(item->rect))
+ return true;
+ }
+ return false;
+}
+
+RenderQueueItem *RenderQueue::findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item) {
+ /* This checks if the given item also exists in the previously drawn frame.
+ The state of the item (position, color etc) is handled elsewhere.
+ */
+ for (RenderQueueArray::iterator iter = queue->begin(); iter != queue->end(); iter++) {
+ RenderQueueItem *prevItem = &(*iter);
+ if (prevItem->type == item.type) {
+ switch (item.type) {
+ case kSprite:
+ if (prevItem->sprite.resIndex == item.sprite.resIndex &&
+ prevItem->sprite.frameNum == item.sprite.frameNum)
+ return prevItem;
+ break;
+ case kText:
+ if (prevItem->text.text == item.text.text &&
+ prevItem->text.len == item.text.len)
+ return prevItem;
+ break;
+ case kMask:
+ if (prevItem->mask.surface == item.mask.surface)
+ return prevItem;
+ break;
+ }
+ }
+ }
+ return NULL; // Not found
+}
+
+bool RenderQueue::hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2) {
+
+ if (item1.type != item2.type)
+ return true;
+
+ if (item1.rect.left != item2.rect.left ||
+ item1.rect.top != item2.rect.top ||
+ item1.rect.right != item2.rect.right ||
+ item1.rect.bottom != item2.rect.bottom)
+ return true;
+
+ if (item1.type == kText && item1.text.color != item2.text.color)
+ return true;
+
+ return false;
+}
+
+void RenderQueue::invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item) {
+ for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
+ RenderQueueItem *subItem = &(*iter);
+ if (item != subItem &&
+ subItem->flags == kUnchanged &&
+ rect.intersects(subItem->rect)) {
+
+ subItem->flags = kRefresh;
+ invalidateItemsByRect(subItem->rect, subItem);
+ }
+ }
+}
+
+void RenderQueue::addDirtyRect(const Common::Rect &rect) {
+ _updateUta->addRect(rect);
+}
+
+void RenderQueue::restoreDirtyBackground() {
+ int n_rects = 0;
+ Common::Rect *rects = _updateUta->getRectangles(&n_rects, 0, 0, 639, _vm->_cameraHeight - 1);
+ for (int i = 0; i < n_rects; i++) {
+ byte *destp = _vm->_screen->_frontScreen + rects[i].left + rects[i].top * 640;
+ byte *srcp = _vm->_screen->_backScreen + (_vm->_cameraX + rects[i].left) + (_vm->_cameraY + rects[i].top) * _vm->_sceneWidth;
+ int16 w = rects[i].width();
+ int16 h = rects[i].height();
+ while (h--) {
+ memcpy(destp, srcp, w);
+ destp += 640;
+ srcp += _vm->_sceneWidth;
+ }
+ invalidateItemsByRect(rects[i], NULL);
+ }
+ delete[] rects;
+}
+
+void RenderQueue::updateDirtyRects() {
+ int n_rects = 0;
+ Common::Rect *rects = _updateUta->getRectangles(&n_rects, 0, 0, 639, _vm->_cameraHeight - 1);
+ for (int i = 0; i < n_rects; i++) {
+ _vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen + rects[i].left + rects[i].top * 640,
+ 640, rects[i].left, rects[i].top, rects[i].width(), rects[i].height());
+ }
+ delete[] rects;
+}
+
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/render.h b/engines/toltecs/render.h
new file mode 100644
index 0000000000..bb9ec29959
--- /dev/null
+++ b/engines/toltecs/render.h
@@ -0,0 +1,99 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef TOLTECS_RENDER_H
+#define TOLTECS_RENDER_H
+
+#include "graphics/surface.h"
+
+#include "toltecs/segmap.h"
+#include "toltecs/screen.h"
+#include "toltecs/microtiles.h"
+
+namespace Toltecs {
+
+enum RenderType {
+ kSprite,
+ kText,
+ kMask
+};
+
+enum RenderFlags {
+ kNone = 1 << 0,
+ kRefresh = 1 << 1,
+ kRemoved = 1 << 2,
+ kMoved = 1 << 3,
+ kUnchanged = 1 << 4
+};
+
+struct RenderTextItem {
+ byte color;
+ uint fontResIndex;
+ byte *text;
+ int len;
+};
+
+struct RenderQueueItem {
+ RenderType type;
+ uint flags;
+ Common::Rect rect;
+ int16 priority;
+ union {
+ SpriteDrawItem sprite;
+ RenderTextItem text;
+ SegmapMaskRect mask;
+ };
+};
+
+class RenderQueue {
+public:
+ RenderQueue(ToltecsEngine *vm);
+ ~RenderQueue();
+
+ void addSprite(SpriteDrawItem &sprite);
+ void addText(int16 x, int16 y, byte color, uint fontResIndex, byte *text, int len);
+ void addMask(SegmapMaskRect &mask);
+ void update();
+ void clear();
+
+protected:
+ typedef Common::List<RenderQueueItem> RenderQueueArray;
+
+ ToltecsEngine *_vm;
+ RenderQueueArray *_currQueue, *_prevQueue;
+ MicroTileArray *_updateUta;
+
+ bool rectIntersectsItem(const Common::Rect &rect);
+ RenderQueueItem *findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item);
+ bool hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2);
+ void invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item);
+
+ void addDirtyRect(const Common::Rect &rect);
+ void restoreDirtyBackground();
+ void updateDirtyRects();
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_RENDER_H */
diff --git a/engines/toltecs/resource.cpp b/engines/toltecs/resource.cpp
new file mode 100644
index 0000000000..b95e0444b1
--- /dev/null
+++ b/engines/toltecs/resource.cpp
@@ -0,0 +1,128 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ *
+ */
+
+#include "common/file.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+
+/* ArchiveReader */
+
+ArchiveReader::ArchiveReader() {
+}
+
+ArchiveReader::~ArchiveReader() {
+ delete[] _offsets;
+}
+
+void ArchiveReader::openArchive(const char *filename) {
+ open(filename);
+ uint32 firstOffs = readUint32LE();
+ uint count = firstOffs / 4;
+ _offsets = new uint32[count];
+ _offsets[0] = firstOffs;
+ for (uint i = 1; i < count; i++)
+ _offsets[i] = readUint32LE();
+}
+
+uint32 ArchiveReader::openResource(uint resIndex) {
+ uint32 resourceSize = getResourceSize(resIndex);
+ seek(_offsets[resIndex]);
+ return resourceSize;
+}
+
+void ArchiveReader::closeResource() {
+}
+
+uint32 ArchiveReader::getResourceSize(uint resIndex) {
+ return _offsets[resIndex + 1] - _offsets[resIndex];
+}
+
+void ArchiveReader::dump(uint resIndex, const char *prefix) {
+ int32 resourceSize = getResourceSize(resIndex);
+ byte *data = new byte[resourceSize];
+
+ Common::String fn;
+
+ if (prefix)
+ fn = Common::String::format("%s_%04X.0", prefix, resIndex);
+ else
+ fn = Common::String::format("%04X.0", resIndex);
+
+ openResource(resIndex);
+ read(data, resourceSize);
+ closeResource();
+
+ Common::DumpFile o;
+ o.open(fn);
+ o.write(data, resourceSize);
+ o.finalize();
+ o.close();
+
+ delete[] data;
+}
+
+/* ResourceCache */
+
+ResourceCache::ResourceCache(ToltecsEngine *vm) : _vm(vm) {
+}
+
+ResourceCache::~ResourceCache() {
+ purgeCache();
+}
+
+void ResourceCache::purgeCache() {
+ for (ResourceMap::iterator iter = _cache.begin(); iter != _cache.end(); ++iter) {
+ delete[] iter->_value->data;
+ delete iter->_value;
+ iter->_value = 0;
+ }
+
+ _cache.clear();
+}
+
+Resource *ResourceCache::load(uint resIndex) {
+ ResourceMap::iterator item = _cache.find(resIndex);
+ if (item != _cache.end()) {
+ debug(1, "ResourceCache::load(%d) From cache", resIndex);
+ return (*item)._value;
+ } else {
+ debug(1, "ResourceCache::load(%d) From disk", resIndex);
+
+ Resource *resItem = new Resource();
+ resItem->size = _vm->_arc->openResource(resIndex);
+ resItem->data = new byte[resItem->size];
+ _vm->_arc->read(resItem->data, resItem->size);
+ _vm->_arc->closeResource();
+
+ _cache[resIndex] = resItem;
+
+ return resItem;
+
+ }
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/resource.h b/engines/toltecs/resource.h
new file mode 100644
index 0000000000..7f4694f0f6
--- /dev/null
+++ b/engines/toltecs/resource.h
@@ -0,0 +1,85 @@
+/* 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 TOLTECS_RESOURCE_H
+#define TOLTECS_RESOURCE_H
+
+#include "common/file.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+#include "engines/engine.h"
+
+namespace Toltecs {
+
+const uint kMaxCacheItems = 1024;
+const uint kMaxCacheSize = 8 * 1024 * 1024; // 8 MB
+
+
+class ArchiveReader : public Common::File {
+public:
+ ArchiveReader();
+ ~ArchiveReader();
+
+ void openArchive(const char *filename);
+
+ // Returns the size of the opened resource
+ uint32 openResource(uint resIndex);
+ // Closes the resource
+ void closeResource();
+ // Returns the size of the resource
+ uint32 getResourceSize(uint resIndex);
+
+ void dump(uint resIndex, const char *prefix = NULL);
+
+protected:
+ uint32 *_offsets;
+
+};
+
+struct Resource {
+ uint32 size;
+ byte *data;
+};
+
+class ResourceCache {
+public:
+ ResourceCache(ToltecsEngine *vm);
+ ~ResourceCache();
+
+ Resource *load(uint resIndex);
+ void purgeCache();
+
+protected:
+ typedef Common::HashMap<uint, Resource*> ResourceMap;
+
+ ToltecsEngine *_vm;
+
+ ResourceMap _cache;
+
+};
+
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_H */
diff --git a/engines/toltecs/saveload.cpp b/engines/toltecs/saveload.cpp
new file mode 100644
index 0000000000..c421be4cd2
--- /dev/null
+++ b/engines/toltecs/saveload.cpp
@@ -0,0 +1,204 @@
+/* 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/savefile.h"
+
+#include "graphics/thumbnail.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/animation.h"
+#include "toltecs/palette.h"
+#include "toltecs/script.h"
+#include "toltecs/screen.h"
+
+namespace Toltecs {
+
+/* TODO:
+ - Save with F7; Load with F9
+ - Saving during an animation (AnimationPlayer) is not working correctly yet
+ - Maybe switch to SCUMM/Tinsel serialization approach?
+*/
+
+#define TOLTECS_SAVEGAME_VERSION 0 // 0 is dev version until in official SVN
+
+ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) {
+
+ header.version = in->readUint32LE();
+ if (header.version != TOLTECS_SAVEGAME_VERSION)
+ return kRSHEInvalidVersion;
+
+ byte descriptionLen = in->readByte();
+ header.description = "";
+ while (descriptionLen--)
+ header.description += (char)in->readByte();
+
+ if (loadThumbnail) {
+ header.thumbnail = Graphics::loadThumbnail(*in);
+ } else {
+ Graphics::skipThumbnail(*in);
+ }
+
+ // Not used yet, reserved for future usage
+ header.gameID = in->readByte();
+ header.flags = in->readUint32LE();
+
+ return ((in->eos() || in->err()) ? kRSHEIoError : kRSHENoError);
+}
+
+void ToltecsEngine::savegame(const char *filename, const char *description) {
+
+ Common::OutSaveFile *out;
+ if (!(out = g_system->getSavefileManager()->openForSaving(filename))) {
+ warning("Can't create file '%s', game not saved", filename);
+ return;
+ }
+
+ out->writeUint32LE(TOLTECS_SAVEGAME_VERSION);
+
+ byte descriptionLen = strlen(description);
+ out->writeByte(descriptionLen);
+ out->write(description, descriptionLen);
+
+ Graphics::saveThumbnail(*out);
+
+ // Not used yet, reserved for future usage
+ out->writeByte(0);
+ out->writeUint32LE(0);
+
+ out->writeUint16LE(_cameraX);
+ out->writeUint16LE(_cameraY);
+ out->writeUint16LE(_cameraHeight);
+
+ out->writeUint16LE(_guiHeight);
+
+ out->writeUint16LE(_sceneWidth);
+ out->writeUint16LE(_sceneHeight);
+ out->writeUint32LE(_sceneResIndex);
+
+ out->writeUint16LE(_walkSpeedX);
+ out->writeUint16LE(_walkSpeedY);
+
+ out->writeUint32LE(_counter01);
+ out->writeUint32LE(_counter02);
+ out->writeByte(_movieSceneFlag ? 1 : 0);
+ out->writeByte(_flag01);
+
+ out->writeUint16LE(_mouseX);
+ out->writeUint16LE(_mouseY);
+ out->writeUint16LE(_mouseDisabled);
+
+ _palette->saveState(out);
+ _script->saveState(out);
+ _anim->saveState(out);
+ _screen->saveState(out);
+
+ out->finalize();
+ delete out;
+
+}
+
+void ToltecsEngine::loadgame(const char *filename) {
+
+ Common::InSaveFile *in;
+ if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
+ warning("Can't open file '%s', game not loaded", filename);
+ return;
+ }
+
+ SaveHeader header;
+
+ kReadSaveHeaderError errorCode = readSaveHeader(in, false, header);
+
+ if (errorCode != kRSHENoError) {
+ warning("Error loading savegame '%s'", filename);
+ delete in;
+ return;
+ }
+
+ _cameraX = in->readUint16LE();
+ _cameraY = in->readUint16LE();
+ _cameraHeight = in->readUint16LE();
+
+ _guiHeight = in->readUint16LE();
+
+ _sceneWidth = in->readUint16LE();
+ _sceneHeight = in->readUint16LE();
+ _sceneResIndex = in->readUint32LE();
+
+ _walkSpeedX = in->readUint16LE();
+ _walkSpeedY = in->readUint16LE();
+
+ _counter01 = in->readUint32LE();
+ _counter02 = in->readUint32LE();
+ _movieSceneFlag = in->readByte() != 0;
+ _flag01 = in->readByte();
+
+ _mouseX = in->readUint16LE();
+ _mouseY = in->readUint16LE();
+ _mouseDisabled = in->readUint16LE();
+
+ _system->warpMouse(_mouseX, _mouseY);
+ _system->showMouse(_mouseDisabled == 0);
+
+ _palette->loadState(in);
+ _script->loadState(in);
+ _anim->loadState(in);
+ _screen->loadState(in);
+
+ delete in;
+
+ loadScene(_sceneResIndex);
+
+ _newCameraX = _cameraX;
+ _newCameraY = _cameraY;
+
+}
+
+Common::Error ToltecsEngine::loadGameState(int slot) {
+ const char *fileName = getSavegameFilename(slot);
+ loadgame(fileName);
+ return Common::kNoError;
+}
+
+Common::Error ToltecsEngine::saveGameState(int slot, const Common::String &description) {
+ const char *fileName = getSavegameFilename(slot);
+ savegame(fileName, description.c_str());
+ return Common::kNoError;
+}
+
+const char *ToltecsEngine::getSavegameFilename(int num) {
+ static Common::String filename;
+ filename = getSavegameFilename(_targetName, num);
+ return filename.c_str();
+}
+
+Common::String ToltecsEngine::getSavegameFilename(const Common::String &target, int num) {
+ assert(num >= 0 && num <= 999);
+
+ char extension[5];
+ sprintf(extension, "%03d", num);
+
+ return target + "." + extension;
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/screen.cpp b/engines/toltecs/screen.cpp
new file mode 100644
index 0000000000..4eda8f643f
--- /dev/null
+++ b/engines/toltecs/screen.cpp
@@ -0,0 +1,808 @@
+/* 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 "graphics/cursorman.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/palette.h"
+#include "toltecs/render.h"
+#include "toltecs/resource.h"
+#include "toltecs/screen.h"
+#include "toltecs/script.h"
+
+namespace Toltecs {
+
+Screen::Screen(ToltecsEngine *vm) : _vm(vm) {
+
+ _frontScreen = new byte[268800];
+ _backScreen = new byte[870400];
+
+ memset(_fontResIndexArray, 0, sizeof(_fontResIndexArray));
+ _fontColor1 = 0;
+ _fontColor2 = 0;
+
+ // Screen shaking
+ _shakeActive = false;
+ _shakeCounterInit = 0;
+ _shakeCounter = 0;
+ _shakePos = 0;
+
+ // Verb line
+ _verbLineNum = 0;
+ memset(_verbLineItems, 0, sizeof(_verbLineItems));
+ _verbLineX = 160;
+ _verbLineY = 2;
+ _verbLineWidth = 20;
+ _verbLineCount = 0;
+
+ // Talk text
+ _talkTextItemNum = 0;
+ memset(_talkTextItems, 0, sizeof(_talkTextItems));
+ _talkTextX = 0;
+ _talkTextY = 0;
+ _talkTextFontColor = 0;
+ _talkTextMaxWidth = 520;
+
+ _renderQueue = new RenderQueue(_vm);
+ _fullRefresh = false;
+ _guiRefresh = false;
+
+}
+
+Screen::~Screen() {
+
+ delete[] _frontScreen;
+ delete[] _backScreen;
+
+ delete _renderQueue;
+
+}
+
+void Screen::unpackRle(byte *source, byte *dest, uint16 width, uint16 height) {
+ int32 size = width * height;
+ while (size > 0) {
+ byte a = *source++;
+ byte b = *source++;
+ if (a == 0) {
+ dest += b;
+ size -= b;
+ } else {
+ b = ((b << 4) & 0xF0) | ((b >> 4) & 0x0F);
+ memset(dest, b, a);
+ dest += a;
+ size -= a;
+ }
+ }
+}
+
+void Screen::loadMouseCursor(uint resIndex) {
+ byte mouseCursor[16 * 16], *mouseCursorP = mouseCursor;
+ byte *cursorData = _vm->_res->load(resIndex)->data;
+ for (int i = 0; i < 32; i++) {
+ byte pixel;
+ byte mask1 = *cursorData++;
+ byte mask2 = *cursorData++;
+ for (int j = 0; j < 8; j++) {
+ pixel = 0xE5;
+ if ((mask2 & 0x80) == 0)
+ pixel = 0xE0;
+ mask2 <<= 1;
+ if ((mask1 & 0x80) == 0)
+ pixel = 0;
+ mask1 <<= 1;
+ *mouseCursorP++ = pixel;
+ }
+ }
+ // FIXME: Where's the cursor hotspot? Using 8, 8 seems good enough for now.
+ CursorMan.replaceCursor((const byte*)mouseCursor, 16, 16, 8, 8, 0);
+}
+
+void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) {
+
+ byte *imageData = _vm->_res->load(resIndex)->data;
+ int16 headerSize = READ_LE_UINT16(imageData);
+ int16 width = imageData[2];
+ int16 height = imageData[3];
+ int16 workWidth = width, workHeight = height;
+ imageData += headerSize;
+
+ byte *dest = _frontScreen + x + (y + _vm->_cameraHeight) * 640;
+
+ //debug(0, "Screen::drawGuiImage() x = %d; y = %d; w = %d; h = %d; resIndex = %d", x, y, width, height, resIndex);
+
+ while (workHeight > 0) {
+ int count = 1;
+ byte pixel = *imageData++;
+ if (pixel & 0x80) {
+ pixel &= 0x7F;
+ count = *imageData++;
+ count += 2;
+ }
+ pixel = pixel + 0xE0;
+ while (count-- && workHeight > 0) {
+ *dest++ = pixel;
+ workWidth--;
+ if (workWidth == 0) {
+ workHeight--;
+ dest += 640 - width;
+ workWidth = width;
+ }
+ }
+ }
+
+ _guiRefresh = true;
+
+}
+
+void Screen::startShakeScreen(int16 shakeCounter) {
+ _shakeActive = true;
+ _shakeCounterInit = shakeCounter;
+ _shakeCounter = shakeCounter;
+ _shakePos = 0;
+}
+
+void Screen::stopShakeScreen() {
+ _shakeActive = false;
+ _vm->_system->setShakePos(0);
+}
+
+void Screen::updateShakeScreen() {
+ if (_shakeActive) {
+ _shakeCounter--;
+ if (_shakeCounter == 0) {
+ _shakeCounter = _shakeCounterInit;
+ _shakePos ^= 8;
+ _vm->_system->setShakePos(_shakePos);
+ }
+ }
+}
+
+void Screen::addStaticSprite(byte *spriteItem) {
+
+ DrawRequest drawRequest;
+ memset(&drawRequest, 0, sizeof(drawRequest));
+
+ drawRequest.y = READ_LE_UINT16(spriteItem + 0);
+ drawRequest.x = READ_LE_UINT16(spriteItem + 2);
+ int16 fragmentId = READ_LE_UINT16(spriteItem + 4);
+ drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF;
+ drawRequest.resIndex = READ_LE_UINT16(spriteItem + 6);
+ drawRequest.flags = READ_LE_UINT16(spriteItem + 8);
+ drawRequest.scaling = 0;
+
+ debug(0, "Screen::addStaticSprite() x = %d; y = %d; baseColor = %d; resIndex = %d; flags = %04X", drawRequest.x, drawRequest.y, drawRequest.baseColor, drawRequest.resIndex, drawRequest.flags);
+
+ addDrawRequest(drawRequest);
+
+}
+
+void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode) {
+
+ //debug(0, "Screen::addAnimatedSprite(%d, %d, %d)", x, y, fragmentId);
+
+ DrawRequest drawRequest;
+ memset(&drawRequest, 0, sizeof(drawRequest));
+
+ drawRequest.x = x;
+ drawRequest.y = y;
+ drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF;
+
+ if (mode == 1) {
+ drawRequest.scaling = _vm->_segmap->getScalingAtPoint(drawRequest.x, drawRequest.y);
+ } else if (mode == 2) {
+ drawRequest.scaling = 0;
+ }
+
+ int16 count = spriteArray[0];
+
+ //debug(0, "count = %d", count);
+
+ for (int16 index = 1; index <= count; index++) {
+
+ byte *spriteItem = data + spriteArray[index];
+
+ uint16 loopNum = READ_LE_UINT16(spriteItem + 0) & 0x7FFF;
+ uint16 loopCount = READ_LE_UINT16(spriteItem + 2);
+ uint16 frameNum = READ_LE_UINT16(spriteItem + 4);
+ uint16 frameCount = READ_LE_UINT16(spriteItem + 6);
+ drawRequest.resIndex = READ_LE_UINT16(spriteItem + 8);
+ drawRequest.flags = READ_LE_UINT16(spriteItem + 10 + loopNum * 2);
+
+ debug(0, "Screen::addAnimatedSprite(%d of %d) loopNum = %d; loopCount = %d; frameNum = %d; frameCount = %d; resIndex = %d; flags = %04X, mode = %d",
+ index, count, loopNum, loopCount, frameNum, frameCount, drawRequest.resIndex, drawRequest.flags, mode);
+
+ addDrawRequest(drawRequest);
+
+ frameNum++;
+ if (frameNum == frameCount) {
+ frameNum = 0;
+ loopNum++;
+ if (loopNum == loopCount) {
+ if (loop) {
+ loopNum = 0;
+ } else {
+ loopNum--;
+ }
+ }
+ } else {
+ loopNum |= 0x8000;
+ }
+
+ WRITE_LE_UINT16(spriteItem + 0, loopNum);
+ WRITE_LE_UINT16(spriteItem + 4, frameNum);
+
+ }
+
+}
+
+void Screen::clearSprites() {
+
+}
+
+void Screen::blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uint16 flags) {
+
+ DrawRequest drawRequest;
+ SpriteDrawItem sprite;
+
+ drawRequest.x = x;
+ drawRequest.y = y;
+ drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF;
+ drawRequest.resIndex = resIndex;
+ drawRequest.flags = flags;
+ drawRequest.scaling = 0;
+
+ if (createSpriteDrawItem(drawRequest, sprite)) {
+ sprite.x -= _vm->_cameraX;
+ sprite.y -= _vm->_cameraY;
+ drawSprite(sprite);
+ }
+
+}
+
+void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) {
+
+ debug(0, "Screen::updateVerbLine() _verbLineNum = %d; _verbLineX = %d; _verbLineY = %d; _verbLineWidth = %d; _verbLineCount = %d",
+ _verbLineNum, _verbLineX, _verbLineY, _verbLineWidth, _verbLineCount);
+
+ Font font(_vm->_res->load(_fontResIndexArray[0])->data);
+
+ _verbLineItems[_verbLineNum].slotIndex = slotIndex;
+ _verbLineItems[_verbLineNum].slotOffset = slotOffset;
+
+ // First clear the line
+ int16 y = _verbLineY;
+ for (int16 i = 0; i < _verbLineCount; i++) {
+ byte *dest = _frontScreen + _verbLineX - _verbLineWidth / 2 + (y - 1 + _vm->_cameraHeight) * 640;
+ for (int16 j = 0; j < 20; j++) {
+ memset(dest, 0xE0, _verbLineWidth);
+ dest += 640;
+ }
+ y += 18;
+ }
+
+ GuiTextWrapState wrapState;
+ int16 len = 0;
+ wrapState.width = 0;
+ wrapState.destString = wrapState.textBuffer;
+ wrapState.len1 = 0;
+ wrapState.len2 = 0;
+
+ y = _verbLineY;
+
+ memset(wrapState.textBuffer, 0, sizeof(wrapState.textBuffer));
+
+ for (int16 i = 0; i <= _verbLineNum; i++) {
+ wrapState.sourceString = _vm->_script->getSlotData(_verbLineItems[i].slotIndex) + _verbLineItems[i].slotOffset;
+ len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState);
+ wrapState.len1 += len;
+ }
+
+ if (_verbLineCount != 1) {
+ int16 charWidth = 0;
+ if (*wrapState.sourceString < 0xF0) {
+ while (*wrapState.sourceString > 0x20 && *wrapState.sourceString < 0xF0 && len > 0) {
+ byte ch = *wrapState.sourceString--;
+ wrapState.len1--;
+ len--;
+ charWidth = font.getCharWidth(ch) + font.getSpacing() - 1;
+ wrapState.width -= charWidth;
+ }
+ wrapState.width += charWidth;
+ wrapState.sourceString++;
+ wrapState.len1 -= len;
+ wrapState.len2 = len + 1;
+
+ drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState);
+
+ wrapState.destString = wrapState.textBuffer;
+ wrapState.width = 0;
+ len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState);
+ wrapState.len1 += len;
+
+ y += 9;
+ }
+ y += 9;
+ }
+
+ wrapState.len1 -= len;
+ wrapState.len2 = len;
+
+ drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState);
+
+ _guiRefresh = true;
+
+}
+
+void Screen::updateTalkText(int16 slotIndex, int16 slotOffset) {
+
+ int16 x, y, maxWidth, width, length;
+ byte durationModifier = 1;
+ byte *textData = _vm->_script->getSlotData(slotIndex) + slotOffset;
+
+ TalkTextItem *item = &_talkTextItems[_talkTextItemNum];
+
+ item->fontNum = 0;
+ item->color = _talkTextFontColor;
+
+ x = CLIP<int16>(_talkTextX - _vm->_cameraX, 120, _talkTextMaxWidth);
+ y = CLIP<int16>(_talkTextY - _vm->_cameraY, 4, _vm->_cameraHeight - 16);
+
+ maxWidth = 624 - ABS(x - 320) * 2;
+
+ while (1) {
+ if (*textData == 0x0A) {
+ x = CLIP<int16>(READ_LE_UINT16(&textData[3]), 120, _talkTextMaxWidth);
+ y = CLIP<int16>(READ_LE_UINT16(&textData[1]), 4, _vm->_cameraHeight - 16);
+ maxWidth = 624 - ABS(x - 320) * 2;
+ textData += 4;
+ } else if (*textData == 0x14) {
+ item->color = ((textData[1] << 4) & 0xF0) | ((textData[1] >> 4) & 0x0F);
+ textData += 2;
+ } else if (*textData == 0x19) {
+ durationModifier = textData[1];
+ textData += 2;
+ } else if (*textData < 0x0A) {
+ item->fontNum = textData[0];
+ // FIXME: Some texts request a font which isn't registered so we change it to a font that is
+ if (_fontResIndexArray[item->fontNum] == 0)
+ item->fontNum = 0;
+ textData += 1;
+ } else
+ break;
+ }
+
+ item->slotIndex = slotIndex;
+ item->slotOffset = textData - _vm->_script->getSlotData(slotIndex);
+
+ width = 0;
+ length = 0;
+
+ item->duration = 0;
+ item->lineCount = 0;
+
+ Font font(_vm->_res->load(_fontResIndexArray[item->fontNum])->data);
+ int16 wordLength, wordWidth;
+
+ while (*textData < 0xF0) {
+ if (*textData == 0x1E) {
+ textData++;
+ addTalkTextRect(font, x, y, length, width, item);
+ width = 0;
+ length = 0;
+ } else {
+ wordLength = 0;
+ wordWidth = 0;
+ while (*textData >= 0x20 && *textData < 0xF0) {
+ byte ch = *textData++;
+ wordLength++;
+ if (ch == 0x20) {
+ wordWidth += font.getWidth();
+ break;
+ } else {
+ wordWidth += font.getCharWidth(ch) + font.getSpacing() - 1;
+ }
+ }
+ if (width + wordWidth > maxWidth + font.getWidth()) {
+ addTalkTextRect(font, x, y, length, width, item);
+ width = wordWidth;
+ length = wordLength;
+ } else {
+ width += wordWidth;
+ length += wordLength;
+ }
+ }
+ }
+
+ addTalkTextRect(font, x, y, length, width, item);
+
+ if (item->lineCount > 0) {
+ int16 ysub = (font.getHeight() - 1) * item->lineCount;
+ if (item->lines[0].y - 4 < ysub)
+ ysub = item->lines[0].y - 4;
+ for (int16 l = 0; l < item->lineCount; l++)
+ item->lines[l].y -= ysub;
+ }
+
+ int16 textDurationMultiplier = item->duration + 8;
+ if (_vm->_doSpeech && *textData == 0xFE) {
+ textDurationMultiplier += 100;
+ }
+ item->duration = 4 * textDurationMultiplier * durationModifier;
+
+}
+
+void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item) {
+
+ if (width > 0) {
+ TextRect *textRect = &item->lines[item->lineCount];
+ width = width + 1 - font.getSpacing();
+ textRect->width = width;
+ item->duration += length;
+ textRect->length = length;
+ textRect->y = y;
+ textRect->x = CLIP<int16>(x - width / 2, 0, 640);
+ item->lineCount++;
+ }
+
+ y += font.getHeight() - 1;
+
+}
+
+void Screen::addTalkTextItemsToRenderQueue() {
+
+ for (int16 i = 0; i <= _talkTextItemNum; i++) {
+ TalkTextItem *item = &_talkTextItems[i];
+ byte *text = _vm->_script->getSlotData(item->slotIndex) + item->slotOffset;
+
+ if (item->fontNum == -1 || item->duration == 0)
+ continue;
+
+ //item->duration -= _vm->_counter01;
+ item->duration--;
+ if (item->duration < 0)
+ item->duration = 0;
+
+ for (byte j = 0; j < item->lineCount; j++) {
+ _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, _fontResIndexArray[item->fontNum],
+ text, item->lines[j].length);
+ text += item->lines[j].length;
+ }
+
+ }
+
+}
+
+int16 Screen::getTalkTextDuration() {
+ return _talkTextItems[_talkTextItemNum].duration;
+}
+
+void Screen::finishTalkTextItems() {
+ for (int16 i = 0; i <= _talkTextItemNum; i++) {
+ _talkTextItems[i].duration = 0;
+ }
+}
+
+void Screen::keepTalkTextItemsAlive() {
+ for (int16 i = 0; i <= _talkTextItemNum; i++) {
+ TalkTextItem *item = &_talkTextItems[i];
+ if (item->fontNum == -1)
+ item->duration = 0;
+ else if (item->duration > 0)
+ item->duration = 2;
+ }
+}
+
+void Screen::registerFont(uint fontIndex, uint resIndex) {
+ _fontResIndexArray[fontIndex] = resIndex;
+}
+
+void Screen::drawGuiTextMulti(byte *textData) {
+
+ int16 x = 0, y = 0;
+
+ // Really strange stuff.
+ for (int i = 30; i >= 0; i--) {
+ if (textData[i] >= 0xF0)
+ break;
+ if (i == 0)
+ return;
+ }
+
+ GuiTextWrapState wrapState;
+ wrapState.sourceString = textData;
+
+ do {
+ if (*wrapState.sourceString == 0x0A) {
+ // Set text position
+ y = wrapState.sourceString[1];
+ x = READ_LE_UINT32(wrapState.sourceString + 2);
+ wrapState.sourceString += 4;
+ } else if (*wrapState.sourceString == 0x0B) {
+ // Inc text position
+ y += wrapState.sourceString[1];
+ x += wrapState.sourceString[2];
+ wrapState.sourceString += 3;
+ } else {
+ wrapState.destString = wrapState.textBuffer;
+ wrapState.width = 0;
+ wrapState.len1 = 0;
+ wrapState.len2 = wrapGuiText(_fontResIndexArray[1], 640, wrapState);
+ drawGuiText(x - wrapState.width / 2, y, _fontColor1, _fontColor2, _fontResIndexArray[1], wrapState);
+ }
+ } while (*wrapState.sourceString != 0xFF);
+
+ _guiRefresh = true;
+
+}
+
+int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wrapState) {
+
+ Font font(_vm->_res->load(fontResIndex)->data);
+ int16 len = 0;
+
+ while (*wrapState.sourceString >= 0x20 && *wrapState.sourceString < 0xF0) {
+ byte ch = *wrapState.sourceString;
+ byte charWidth;
+ if (ch <= 0x20)
+ charWidth = font.getWidth();
+ else
+ charWidth = font.getCharWidth(ch) + font.getSpacing() - 1;
+ if (wrapState.width + charWidth >= maxWidth)
+ break;
+ len++;
+ wrapState.width += charWidth;
+ *wrapState.destString++ = *wrapState.sourceString++;
+ }
+
+ return len;
+
+}
+
+void Screen::drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState) {
+
+ debug(0, "Screen::drawGuiText(%d, %d, %d, %d, %d) wrapState.len1 = %d; wrapState.len2 = %d", x, y, fontColor1, fontColor2, fontResIndex, wrapState.len1, wrapState.len2);
+
+ int16 ywobble = 1;
+
+ x = drawString(x + 1, y + _vm->_cameraHeight, fontColor1, fontResIndex, wrapState.textBuffer, wrapState.len1, &ywobble, false);
+ x = drawString(x, y + _vm->_cameraHeight, fontColor2, fontResIndex, wrapState.textBuffer + wrapState.len1, wrapState.len2, &ywobble, false);
+
+}
+
+int16 Screen::drawString(int16 x, int16 y, byte color, uint fontResIndex, const byte *text, int len, int16 *ywobble, bool outline) {
+
+ //debug(0, "Screen::drawString(%d, %d, %d, %d)", x, y, color, fontResIndex);
+
+ Font font(_vm->_res->load(fontResIndex)->data);
+
+ if (len == -1)
+ len = strlen((const char*)text);
+
+ int16 yadd = 0;
+ if (ywobble)
+ yadd = *ywobble;
+
+ while (len--) {
+ byte ch = *text++;
+ if (ch <= 0x20) {
+ x += font.getWidth();
+ } else {
+ drawChar(font, _frontScreen, x, y - yadd, ch, color, outline);
+ x += font.getCharWidth(ch) + font.getSpacing() - 1;
+ yadd = -yadd;
+ }
+ }
+
+ if (ywobble)
+ *ywobble = yadd;
+
+ return x;
+
+}
+
+void Screen::drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color, bool outline) {
+
+ int16 charWidth, charHeight;
+ byte *charData;
+
+ dest += x + y * 640;
+
+ charWidth = font.getCharWidth(ch);
+ //charHeight = font.getHeight() - 2;//Why was this here?!
+ charHeight = font.getHeight();
+ charData = font.getCharData(ch);
+
+ while (charHeight--) {
+ byte lineWidth = charWidth;
+ while (lineWidth > 0) {
+ byte count = charData[0] & 0x0F;
+ byte flags = charData[0] & 0xF0;
+ charData++;
+ if ((flags & 0x80) == 0) {
+ if (flags & 0x10) {
+ memset(dest, color, count);
+ } else if (outline) {
+ memset(dest, 0, count);
+ }
+ }
+ dest += count;
+ lineWidth -= count;
+ }
+ dest += 640 - charWidth;
+ }
+
+}
+
+void Screen::drawSurface(int16 x, int16 y, Graphics::Surface *surface) {
+
+ int16 skipX = 0;
+ int16 width = surface->w;
+ int16 height = surface->h;
+ byte *surfacePixels = (byte*)surface->getBasePtr(0, 0);
+ byte *frontScreen;
+
+ // Not on screen, skip
+ if (x + width < 0 || y + height < 0 || x >= 640 || y >= _vm->_cameraHeight)
+ return;
+
+ if (x < 0) {
+ skipX = -x;
+ x = 0;
+ width -= skipX;
+ }
+
+ if (y < 0) {
+ int16 skipY = -y;
+ surfacePixels += surface->w * skipY;
+ y = 0;
+ height -= skipY;
+ }
+
+ if (x + width >= 640) {
+ width -= x + width - 640;
+ }
+
+ if (y + height >= _vm->_cameraHeight) {
+ height -= y + height - _vm->_cameraHeight;
+ }
+
+ frontScreen = _vm->_screen->_frontScreen + x + (y * 640);
+
+ for (int16 h = 0; h < height; h++) {
+ surfacePixels += skipX;
+ for (int16 w = 0; w < width; w++) {
+ if (*surfacePixels != 0xFF)
+ *frontScreen = *surfacePixels;
+ frontScreen++;
+ surfacePixels++;
+ }
+ frontScreen += 640 - width;
+ surfacePixels += surface->w - width - skipX;
+ }
+
+}
+
+void Screen::saveState(Common::WriteStream *out) {
+
+ // Save verb line
+ out->writeUint16LE(_verbLineNum);
+ out->writeUint16LE(_verbLineX);
+ out->writeUint16LE(_verbLineY);
+ out->writeUint16LE(_verbLineWidth);
+ out->writeUint16LE(_verbLineCount);
+ for (int i = 0; i < 8; i++) {
+ out->writeUint16LE(_verbLineItems[i].slotIndex);
+ out->writeUint16LE(_verbLineItems[i].slotOffset);
+ }
+
+ // Save talk text items
+ out->writeUint16LE(_talkTextX);
+ out->writeUint16LE(_talkTextY);
+ out->writeUint16LE(_talkTextMaxWidth);
+ out->writeByte(_talkTextFontColor);
+ out->writeUint16LE(_talkTextItemNum);
+ for (int i = 0; i < 5; i++) {
+ out->writeUint16LE(_talkTextItems[i].duration);
+ out->writeUint16LE(_talkTextItems[i].slotIndex);
+ out->writeUint16LE(_talkTextItems[i].slotOffset);
+ out->writeUint16LE(_talkTextItems[i].fontNum);
+ out->writeByte(_talkTextItems[i].color);
+ out->writeByte(_talkTextItems[i].lineCount);
+ for (int j = 0; j < _talkTextItems[i].lineCount; j++) {
+ out->writeUint16LE(_talkTextItems[i].lines[j].x);
+ out->writeUint16LE(_talkTextItems[i].lines[j].y);
+ out->writeUint16LE(_talkTextItems[i].lines[j].width);
+ out->writeUint16LE(_talkTextItems[i].lines[j].length);
+ }
+ }
+
+ // Save GUI bitmap
+ {
+ byte *gui = _frontScreen + _vm->_cameraHeight * 640;
+ for (int i = 0; i < _vm->_guiHeight; i++) {
+ out->write(gui, 640);
+ gui += 640;
+ }
+ }
+
+ // Save fonts
+ for (int i = 0; i < 10; i++)
+ out->writeUint32LE(_fontResIndexArray[i]);
+ out->writeByte(_fontColor1);
+ out->writeByte(_fontColor2);
+
+}
+
+void Screen::loadState(Common::ReadStream *in) {
+
+ // Load verb line
+ _verbLineNum = in->readUint16LE();
+ _verbLineX = in->readUint16LE();
+ _verbLineY = in->readUint16LE();
+ _verbLineWidth = in->readUint16LE();
+ _verbLineCount = in->readUint16LE();
+ for (int i = 0; i < 8; i++) {
+ _verbLineItems[i].slotIndex = in->readUint16LE();
+ _verbLineItems[i].slotOffset = in->readUint16LE();
+ }
+
+ // Load talk text items
+ _talkTextX = in->readUint16LE();
+ _talkTextY = in->readUint16LE();
+ _talkTextMaxWidth = in->readUint16LE();
+ _talkTextFontColor = in->readByte();
+ _talkTextItemNum = in->readUint16LE();
+ for (int i = 0; i < 5; i++) {
+ _talkTextItems[i].duration = in->readUint16LE();
+ _talkTextItems[i].slotIndex = in->readUint16LE();
+ _talkTextItems[i].slotOffset = in->readUint16LE();
+ _talkTextItems[i].fontNum = in->readUint16LE();
+ _talkTextItems[i].color = in->readByte();
+ _talkTextItems[i].lineCount = in->readByte();
+ for (int j = 0; j < _talkTextItems[i].lineCount; j++) {
+ _talkTextItems[i].lines[j].x = in->readUint16LE();
+ _talkTextItems[i].lines[j].y = in->readUint16LE();
+ _talkTextItems[i].lines[j].width = in->readUint16LE();
+ _talkTextItems[i].lines[j].length = in->readUint16LE();
+ }
+ }
+
+ // Load GUI bitmap
+ {
+ byte *gui = _frontScreen + _vm->_cameraHeight * 640;
+ for (int i = 0; i < _vm->_guiHeight; i++) {
+ in->read(gui, 640);
+ gui += 640;
+ }
+ _guiRefresh = true;
+ }
+
+ // Load fonts
+ for (int i = 0; i < 10; i++)
+ _fontResIndexArray[i] = in->readUint32LE();
+ _fontColor1 = in->readByte();
+ _fontColor2 = in->readByte();
+
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/screen.h b/engines/toltecs/screen.h
new file mode 100644
index 0000000000..988f59c840
--- /dev/null
+++ b/engines/toltecs/screen.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.
+ *
+ *
+ */
+
+#ifndef TOLTECS_SCREEN_H
+#define TOLTECS_SCREEN_H
+
+#include "graphics/surface.h"
+#include "toltecs/toltecs.h"
+
+namespace Toltecs {
+
+struct DrawRequest {
+ int16 x, y;
+ int16 resIndex;
+ uint16 flags;
+ int16 baseColor;
+ int8 scaling;
+};
+
+struct SpriteDrawItem {
+ int16 x, y;
+ int16 width, height;
+ int16 origWidth, origHeight;
+ int16 resIndex, frameNum;
+ uint32 offset;
+ int16 xdelta, ydelta;
+ uint16 flags;
+ int16 skipX, yerror;
+ int16 priority;
+ int16 baseColor;
+};
+
+struct SpriteFrameEntry {
+ int16 y, x, h, w;
+ uint32 offset;
+ SpriteFrameEntry() {
+ }
+ SpriteFrameEntry(byte *data) {
+ y = READ_LE_UINT16(data + 0);
+ x = READ_LE_UINT16(data + 2);
+ h = READ_LE_UINT16(data + 4);
+ w = READ_LE_UINT16(data + 6);
+ offset = READ_LE_UINT32(data + 8);
+ }
+};
+
+class Font {
+public:
+ Font(byte *fontData) : _fontData(fontData) {
+ }
+ ~Font() {
+ }
+ int16 getSpacing() const {
+ return _fontData[1];
+ }
+ int16 getHeight() const {
+ return _fontData[2];
+ }
+ int16 getWidth() const {
+ return _fontData[3];
+ }
+ int16 getCharWidth(byte ch) const {
+ return _fontData[4 + (ch - 0x21)];
+ }
+ byte *getCharData(byte ch) const {
+ return _fontData + 0x298 + READ_LE_UINT16(&_fontData[0xE0 + (ch - 0x21) * 2]);
+ }
+ int16 getTextWidth(const byte *text) {
+ int16 width = 0;
+ while (*text && *text < 0xF0) {
+ byte ch = *text++;
+ if (ch <= 0x20) {
+ width += getWidth();
+ } else {
+ width += getCharWidth(ch) + getSpacing() - 1;
+ }
+ }
+ return width;
+ }
+
+protected:
+ byte *_fontData;
+};
+
+struct PixelPacket {
+ byte count;
+ byte pixel;
+};
+
+enum SpriteReaderStatus {
+ kSrsPixelsLeft,
+ kSrsEndOfLine,
+ kSrsEndOfSprite
+};
+
+class SpriteFilter {
+public:
+ SpriteFilter(const SpriteDrawItem &sprite) : _sprite(&sprite) {
+ }
+ virtual ~SpriteFilter() {}
+ virtual SpriteReaderStatus readPacket(PixelPacket &packet) = 0;
+protected:
+ const SpriteDrawItem *_sprite;
+};
+
+struct TextRect {
+ int16 x, y;
+ int16 width, length;
+};
+
+struct TalkTextItem {
+ int16 duration;
+ int16 slotIndex;
+ int16 slotOffset;
+ int16 fontNum;
+ byte color;
+ byte lineCount;
+ TextRect lines[15];
+};
+
+struct GuiTextWrapState {
+ int16 len1, len2;
+ byte *sourceString;
+ byte *destString;
+ int16 width;
+ byte textBuffer[100];
+};
+
+class RenderQueue;
+
+class Screen {
+public:
+ Screen(ToltecsEngine *vm);
+ ~Screen();
+
+ void unpackRle(byte *source, byte *dest, uint16 width, uint16 height);
+
+ void loadMouseCursor(uint resIndex);
+
+ void drawGuiImage(int16 x, int16 y, uint resIndex);
+
+ void startShakeScreen(int16 shakeCounter);
+ void stopShakeScreen();
+ void updateShakeScreen();
+
+ // Sprite list
+ void addStaticSprite(byte *spriteItem);
+ void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode);
+ void clearSprites();
+
+ // Sprite drawing
+ void drawSprite(const SpriteDrawItem &sprite);
+ void drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawItem &sprite);
+ void blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uint16 flags);
+
+ // Verb line
+ void updateVerbLine(int16 slotIndex, int16 slotOffset);
+
+ // Talk text
+ void updateTalkText(int16 slotIndex, int16 slotOffset);
+ void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item);
+ void addTalkTextItemsToRenderQueue();
+ int16 getTalkTextDuration();
+ void finishTalkTextItems();
+ void keepTalkTextItemsAlive();
+
+ // Font/text
+ void registerFont(uint fontIndex, uint resIndex);
+ void drawGuiTextMulti(byte *textData);
+ int16 wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wrapState);
+ void drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState);
+
+ int16 drawString(int16 x, int16 y, byte color, uint fontResIndex, const byte *text, int len = -1, int16 *ywobble = NULL, bool outline = false);
+ void drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color, bool outline);
+
+ void drawSurface(int16 x, int16 y, Graphics::Surface *surface);
+
+ void saveState(Common::WriteStream *out);
+ void loadState(Common::ReadStream *in);
+
+ uint getFontResIndex(int fontNum) const { return _fontResIndexArray[fontNum]; }
+
+//protected:
+public:
+
+ struct VerbLineItem {
+ int16 slotIndex;
+ int16 slotOffset;
+ };
+
+ struct Rect {
+ int16 x, y, width, height;
+ };
+
+ ToltecsEngine *_vm;
+
+ byte *_frontScreen, *_backScreen;
+
+ uint _fontResIndexArray[10];
+ byte _fontColor1, _fontColor2;
+
+ // Screen shaking
+ bool _shakeActive;
+ int16 _shakeCounterInit, _shakeCounter;
+ int _shakePos;
+
+ // Verb line
+ int16 _verbLineNum;
+ VerbLineItem _verbLineItems[8];
+ int16 _verbLineX, _verbLineY, _verbLineWidth;
+ int16 _verbLineCount;
+
+ // Talk text
+ int16 _talkTextX, _talkTextY;
+ int16 _talkTextMaxWidth;
+ byte _talkTextFontColor;
+ int16 _talkTextItemNum;
+ TalkTextItem _talkTextItems[5];
+
+ RenderQueue *_renderQueue;
+ bool _fullRefresh;
+ bool _guiRefresh;
+
+ bool createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem &sprite);
+ void addDrawRequest(const DrawRequest &drawRequest);
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_SCREEN_H */
diff --git a/engines/toltecs/script.cpp b/engines/toltecs/script.cpp
new file mode 100644
index 0000000000..708afb7008
--- /dev/null
+++ b/engines/toltecs/script.cpp
@@ -0,0 +1,1117 @@
+/* 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.
+ *
+ *
+ */
+
+// TODO: Clean up game variable handling and move it to ToltecsEngine
+
+#include "common/error.h"
+
+#include "graphics/cursorman.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/animation.h"
+#include "toltecs/menu.h"
+#include "toltecs/movie.h"
+#include "toltecs/music.h"
+#include "toltecs/palette.h"
+#include "toltecs/resource.h"
+#include "toltecs/script.h"
+#include "toltecs/segmap.h"
+#include "toltecs/sound.h"
+
+namespace Toltecs {
+
+ScriptInterpreter::ScriptInterpreter(ToltecsEngine *vm) : _vm(vm) {
+
+ _stack = new byte[kScriptStackSize];
+
+ memset(_slots, 0, sizeof(_slots));
+
+ _savedSp = 0;
+
+ _slots[kMaxScriptSlots - 1].size = 1024;
+ _slots[kMaxScriptSlots - 1].data = new byte[_slots[kMaxScriptSlots - 1].size];
+
+ setupScriptFunctions();
+
+}
+
+ScriptInterpreter::~ScriptInterpreter() {
+ delete[] _stack;
+ for (int i = 0; i < kMaxScriptSlots; i++)
+ delete[] _slots[i].data;
+ for (uint i = 0; i < _scriptFuncs.size(); ++i)
+ delete _scriptFuncs[i];
+}
+
+typedef Common::Functor0Mem<void, ScriptInterpreter> ScriptFunctionF;
+#define RegisterScriptFunction(x) \
+ _scriptFuncs.push_back(new ScriptFunctionF(this, &ScriptInterpreter::x)); \
+ _scriptFuncNames.push_back(#x);
+void ScriptInterpreter::setupScriptFunctions() {
+
+ // 0
+ RegisterScriptFunction(sfNop);
+ RegisterScriptFunction(sfNop);
+ RegisterScriptFunction(sfGetGameVar);
+ RegisterScriptFunction(sfSetGameVar);
+ RegisterScriptFunction(sfUpdateScreen);
+ // 5
+ RegisterScriptFunction(sfGetRandomNumber);
+ RegisterScriptFunction(sfDrawGuiTextMulti);
+ RegisterScriptFunction(sfUpdateVerbLine);
+ RegisterScriptFunction(sfSetFontColor);
+ RegisterScriptFunction(sfGetTalkTextDuration);
+ // 10
+ RegisterScriptFunction(sfTalk);
+ RegisterScriptFunction(sfFindPaletteFragment);
+ RegisterScriptFunction(sfClearPaletteFragments);
+ RegisterScriptFunction(sfAddPaletteFragment);
+ RegisterScriptFunction(sfSetDeltaAnimPalette);
+ // 15
+ RegisterScriptFunction(sfSetUnkPaletteEffect);
+ RegisterScriptFunction(sfBuildColorTransTable);
+ RegisterScriptFunction(sfSetDeltaMainPalette);
+ RegisterScriptFunction(sfLoadScript);
+ RegisterScriptFunction(sfRegisterFont);
+ // 20
+ RegisterScriptFunction(sfLoadAddPalette);
+ RegisterScriptFunction(sfLoadScene);
+ RegisterScriptFunction(sfSetGuiHeight);
+ RegisterScriptFunction(sfFindMouseInRectIndex1);
+ RegisterScriptFunction(sfFindMouseInRectIndex2);
+ // 25
+ RegisterScriptFunction(sfDrawGuiImage);
+ RegisterScriptFunction(sfAddAnimatedSpriteNoLoop);
+ RegisterScriptFunction(sfAddAnimatedSprite);
+ RegisterScriptFunction(sfAddStaticSprite);
+ RegisterScriptFunction(sfAddAnimatedSpriteScaled);
+ // 30
+ RegisterScriptFunction(sfFindPath);
+ RegisterScriptFunction(sfWalk);
+ RegisterScriptFunction(sfScrollCameraUp);
+ RegisterScriptFunction(sfScrollCameraDown);
+ RegisterScriptFunction(sfScrollCameraLeft);
+ // 35
+ RegisterScriptFunction(sfScrollCameraRight);
+ RegisterScriptFunction(sfScrollCameraUpEx);
+ RegisterScriptFunction(sfScrollCameraDownEx);
+ RegisterScriptFunction(sfScrollCameraLeftEx);
+ RegisterScriptFunction(sfScrollCameraRightEx);
+ // 40
+ RegisterScriptFunction(sfSetCamera);
+ RegisterScriptFunction(sfGetCameraChanged);
+ RegisterScriptFunction(sfGetRgbModifiertAtPoint);
+ RegisterScriptFunction(sfStartAnim);
+ RegisterScriptFunction(sfAnimNextFrame);
+ // 45
+ RegisterScriptFunction(sfNop);
+ RegisterScriptFunction(sfGetAnimFrameNumber);
+ RegisterScriptFunction(sfGetAnimStatus);
+ RegisterScriptFunction(sfStartShakeScreen);
+ RegisterScriptFunction(sfStopShakeScreen);
+ // 50
+ RegisterScriptFunction(sfStartSequence);
+ RegisterScriptFunction(sfEndSequence);
+ RegisterScriptFunction(sfSetSequenceVolume);
+ RegisterScriptFunction(sfPlayPositionalSound);
+ RegisterScriptFunction(sfPlaySound2);
+ // 55
+ RegisterScriptFunction(sfClearScreen);
+ RegisterScriptFunction(sfNop);
+ RegisterScriptFunction(sfHandleInput);
+ RegisterScriptFunction(sfRunOptionsScreen);
+ RegisterScriptFunction(sfPrecacheSprites);
+ // 60
+ RegisterScriptFunction(sfPrecacheSounds1);
+ RegisterScriptFunction(sfDeletePrecachedFiles);
+ RegisterScriptFunction(sfPrecacheSounds2);
+ RegisterScriptFunction(sfRestoreStackPtr);
+ RegisterScriptFunction(sfSaveStackPtr);
+ // 65
+ RegisterScriptFunction(sfPlayMovie);
+ RegisterScriptFunction(sfNop);
+
+}
+
+void ScriptInterpreter::loadScript(uint resIndex, uint slotIndex) {
+
+ delete[] _slots[slotIndex].data;
+
+ _slots[slotIndex].resIndex = resIndex;
+ Resource *scriptResource = _vm->_res->load(resIndex);
+ _slots[slotIndex].size = scriptResource->size;
+ _slots[slotIndex].data = new byte[_slots[slotIndex].size];
+ memcpy(_slots[slotIndex].data, scriptResource->data, _slots[slotIndex].size);
+
+}
+
+void ScriptInterpreter::setMainScript(uint slotIndex) {
+ _switchLocalDataNear = true;
+ _switchLocalDataFar = false;
+ _switchLocalDataToStack = false;
+ _cmpBitTest = false;
+ _regs.reg0 = 0;
+ _regs.reg1 = 0;
+ _regs.reg2 = 0;
+ _regs.reg3 = 0;
+ _regs.reg4 = slotIndex;
+ _regs.reg5 = 0;
+ _regs.reg6 = 0;
+ _regs.sp = 4096;
+ _regs.reg8 = 0;
+ _code = getSlotData(_regs.reg4);
+}
+
+void ScriptInterpreter::runScript() {
+
+ while (!_vm->shouldQuit()) {
+
+ if (_vm->_movieSceneFlag)
+ _vm->_mouseButton = 0;
+
+ if (_vm->_saveLoadRequested != 0) {
+ if (_vm->_saveLoadRequested == 1)
+ _vm->loadGameState(_vm->_saveLoadSlot);
+ else if (_vm->_saveLoadRequested == 2)
+ _vm->saveGameState(_vm->_saveLoadSlot, _vm->_saveLoadDescription);
+ _vm->_saveLoadRequested = 0;
+ }
+
+ if (_switchLocalDataNear) {
+ _switchLocalDataNear = false;
+ _localData = getSlotData(_regs.reg4);
+ }
+
+ if (_switchLocalDataFar) {
+ _switchLocalDataFar = false;
+ _localData = getSlotData(_regs.reg5);
+ _switchLocalDataNear = true;
+ }
+
+ if (_switchLocalDataToStack) {
+ _switchLocalDataToStack = false;
+ _localData = _stack + 2;
+ _switchLocalDataNear = true;
+ }
+
+ byte opcode = readByte();
+ execOpcode(opcode);
+
+ // Call updateScreen roughly every 10ms else the mouse cursor will be jerky
+ if (_vm->_system->getMillis() % 10 == 0)
+ _vm->_system->updateScreen();
+
+ }
+
+}
+
+byte ScriptInterpreter::readByte() {
+ return *_code++;
+}
+
+int16 ScriptInterpreter::readInt16() {
+ int16 value = READ_LE_UINT16(_code);
+ _code += 2;
+ return value;
+}
+
+void ScriptInterpreter::execOpcode(byte opcode) {
+
+ int16 ofs;
+
+ debug(1, "opcode = %d", opcode);
+
+ switch (opcode) {
+ case 0:
+ {
+ // ok
+ _subCode = _code;
+ byte length = readByte();
+ debug(1, "length = %d", length);
+ uint16 index = readInt16();
+ debug(1, "callScriptFunction %d", index);
+ execScriptFunction(index);
+ _code += length - 2;
+ break;
+ }
+ case 1:
+ // ok
+ _regs.reg0 = readInt16();
+ break;
+ case 2:
+ // ok
+ _regs.reg1 = readInt16();
+ break;
+ case 3:
+ // ok
+ _regs.reg3 = readInt16();
+ break;
+ case 4:
+ // ok
+ _regs.reg5 = _regs.reg0;
+ break;
+ case 5:
+ // ok
+ _regs.reg3 = _regs.reg0;
+ break;
+ case 6:
+ // ok
+ _regs.reg1 = _regs.reg0;
+ break;
+ case 7:
+ _regs.reg1 = localRead16(_regs.reg3);
+ break;
+ case 8:
+ localWrite16(_regs.reg3, _regs.reg0);
+ break;
+ case 9:
+ localWrite16(readInt16(), _regs.reg0);
+ break;
+ case 10:
+ localWrite8(readInt16(), _regs.reg0);
+ break;
+ case 11:
+ localWrite16(readInt16(), _regs.reg5);
+ break;
+ case 12:
+ localWrite16(readInt16(), _regs.reg4);
+ break;
+ case 13:
+ localWrite16(readInt16(), _regs.reg3);
+ break;
+ case 14:
+ _regs.reg3 = localRead16(readInt16());
+ break;
+ case 15:
+ _regs.reg2 = localRead16(_regs.reg1);
+ break;
+ case 16:
+ _regs.reg2 = localRead16(_regs.reg1 + readInt16());
+ break;
+ case 17:
+ _regs.reg2 = _regs.reg0;
+ break;
+ case 18:
+ _regs.reg0 += readInt16();
+ break;
+ case 19:
+ localWrite16(_regs.reg3, localRead16(_regs.reg3) + _regs.reg0);
+ break;
+ case 20:
+ _regs.reg0 += _regs.reg2;
+ break;
+ case 21:
+ _regs.reg3 += _regs.sp;
+ break;
+ case 22:
+ _regs.reg1 += _regs.sp;
+ break;
+ case 23:
+ localWrite16(_regs.reg3, localRead16(_regs.reg3) - _regs.reg0);
+ break;
+ case 24:
+ _regs.reg0 /= readInt16();
+ break;
+ case 25:
+ localWrite16(_regs.reg3, localRead16(_regs.reg3) / _regs.reg0);
+ break;
+ case 26:
+ // NOP
+ break;
+ case 27:
+ _regs.reg0 *= readInt16();
+ break;
+ case 28:
+ localWrite16(_regs.reg3, localRead16(_regs.reg3) * _regs.reg0);
+ break;
+ case 29:
+ _regs.reg0 *= _regs.reg2;
+ break;
+ case 30:
+ localWrite16(_regs.reg3, localRead16(_regs.reg3) + 1);
+ break;
+ case 31:
+ localWrite16(_regs.reg3, localRead16(_regs.reg3) - 1);
+ break;
+ case 32:
+ _switchLocalDataFar = true;
+ break;
+ case 33:
+ _switchLocalDataToStack = true;
+ break;
+ case 34:
+ pushInt16(_regs.reg0);
+ break;
+ case 35:
+ pushInt16(_regs.reg1);
+ break;
+ case 36:
+ _regs.reg1 = popInt16();
+ break;
+ case 37:
+ _regs.reg0 = popInt16();
+ break;
+ case 38:
+ _regs.reg2 = -_regs.reg2;
+ break;
+ case 39:
+ _regs.reg8 = readInt16();
+ _cmpBitTest = false;
+ break;
+ case 40:
+ _regs.reg8 = _regs.reg0;
+ _cmpBitTest = false;
+ break;
+ case 41:
+ _regs.reg8 = readInt16();
+ _cmpBitTest = true;
+ break;
+ case 42:
+ _regs.reg8 = _regs.reg0;
+ _cmpBitTest = true;
+ break;
+ case 43:
+ _code = getSlotData(_regs.reg4) + _regs.reg0;
+ break;
+ case 44:
+ _code = getSlotData(_regs.reg5) + _regs.reg0;
+ _regs.reg4 = _regs.reg5;
+ _switchLocalDataNear = true;
+ break;
+ case 45:
+ pushInt16(_code - getSlotData(_regs.reg4));
+ pushInt16(_regs.reg4);
+ _code = getSlotData(_regs.reg4) + _regs.reg0;
+ break;
+ case 46:
+ pushInt16(_code - getSlotData(_regs.reg4));
+ pushInt16(_regs.reg4);
+ _code = getSlotData(_regs.reg5) + _regs.reg0;
+ _regs.reg4 = _regs.reg5;
+ _switchLocalDataNear = true;
+ break;
+ case 47:
+ _regs.reg4 = popInt16();
+ ofs = popInt16();
+ _code = getSlotData(_regs.reg4) + ofs;
+ _switchLocalDataNear = true;
+ break;
+ case 48:
+ _regs.reg4 = popInt16();
+ ofs = popInt16();
+ _code = getSlotData(_regs.reg4) + ofs;
+ _regs.sp += _regs.reg0;
+ _switchLocalDataNear = true;
+ break;
+ case 49:
+ ofs = readByte();
+ _code += ofs;
+ break;
+ case 50:
+ if (_cmpBitTest) {
+ _regs.reg1 &= _regs.reg8;
+ if (_regs.reg1 == 0)
+ _code += 4;
+ } else {
+ if (_regs.reg1 == _regs.reg8)
+ _code += 4;
+ }
+ _code++;
+ break;
+ case 51:
+ if (_cmpBitTest) {
+ _regs.reg1 &= _regs.reg8;
+ if (_regs.reg1 != 0)
+ _code += 4;
+ } else {
+ if (_regs.reg1 != _regs.reg8)
+ _code += 4;
+ }
+ _code++;
+ break;
+ case 52:
+ if ((uint16)_regs.reg1 >= (uint16)_regs.reg8)
+ _code += 4;
+ _code++;
+ break;
+ case 53:
+ if ((uint16)_regs.reg1 <= (uint16)_regs.reg8)
+ _code += 4;
+ _code++;
+ break;
+ case 54:
+ if ((uint16)_regs.reg1 < (uint16)_regs.reg8)
+ _code += 4;
+ _code++;
+ break;
+ case 55:
+ if ((uint16)_regs.reg1 > (uint16)_regs.reg8)
+ _code += 4;
+ _code++;
+ break;
+ default:
+ error("Invalid opcode %d", opcode);
+ }
+
+}
+
+void ScriptInterpreter::execScriptFunction(uint16 index) {
+ debug(4, "execScriptFunction(%d)", index);
+ if (index >= _scriptFuncs.size())
+ error("ScriptInterpreter::execScriptFunction() Invalid script function index %d", index);
+ debug(4, "%s", _scriptFuncNames[index]);
+ (*_scriptFuncs[index])();
+}
+
+VarType ScriptInterpreter::getGameVarType(uint variable) {
+ switch (variable) {
+ case 0: return vtByte;
+ case 1: return vtWord;
+ case 2: return vtWord;
+ case 3: return vtByte;
+ case 4: return vtWord;
+ case 5: return vtWord;
+ case 6: return vtWord;
+ case 7: return vtWord;
+ case 8: return vtWord;
+ case 9: return vtWord;
+ case 10: return vtWord;
+ case 11: return vtWord;
+ case 12: return vtByte;
+ case 13: return vtWord;
+ case 14: return vtWord;
+ case 15: return vtWord;
+ case 16: return vtWord;
+ case 17: return vtWord;
+ case 18: return vtWord;
+ case 19: return vtWord;
+ case 20: return vtWord;
+ case 21: return vtWord;
+ default:
+ error("Invalid game variable");
+ }
+}
+
+const char *getVarName(uint variable) {
+ switch (variable) {
+ case 0: return "mouseDisabled";
+ case 1: return "mouseY";
+ case 2: return "mouseX";
+ case 3: return "mouseButton";
+ case 4: return "verbLineY";
+ case 5: return "verbLineX";
+ case 6: return "verbLineWidth";
+ case 7: return "verbLineCount";
+ case 8: return "verbLineNum";
+ case 9: return "talkTextItemNum";
+ case 10: return "talkTextY";
+ case 11: return "talkTextX";
+ case 12: return "talkTextFontColor";
+ case 13: return "cameraY";
+ case 14: return "cameraX";
+ case 15: return "walkSpeedY";
+ case 16: return "walkSpeedX";
+ case 17: return "flag01";
+ case 18: return "sceneResIndex";
+ case 19: return "guiHeight";
+ case 20: return "sceneHeight";
+ case 21: return "sceneWidth";
+ }
+ return "(invalid)";
+}
+
+int16 ScriptInterpreter::getGameVar(uint variable) {
+ debug(0, "ScriptInterpreter::getGameVar(%d{%s})", variable, getVarName(variable));
+
+ switch (variable) {
+ case 0: return _vm->_mouseDisabled;
+ case 1: return _vm->_mouseY;
+ case 2: return _vm->_mouseX;
+ case 3: return _vm->_mouseButton;
+ case 4: return _vm->_screen->_verbLineY;
+ case 5: return _vm->_screen->_verbLineX;
+ case 6: return _vm->_screen->_verbLineWidth;
+ case 7: return _vm->_screen->_verbLineCount;
+ case 8: return _vm->_screen->_verbLineNum;
+ case 9: return _vm->_screen->_talkTextItemNum;
+ case 10: return _vm->_screen->_talkTextY;
+ case 11: return _vm->_screen->_talkTextX;
+ case 12: return _vm->_screen->_talkTextFontColor;
+ case 13: return _vm->_cameraY;
+ case 14: return _vm->_cameraX;
+ case 15: return _vm->_walkSpeedY;
+ case 16: return _vm->_walkSpeedX;
+ case 17: return _vm->_flag01;
+ case 18: return _vm->_sceneResIndex;
+ case 19: return _vm->_guiHeight;
+ case 20: return _vm->_sceneHeight;
+ case 21: return _vm->_sceneWidth;
+ default:
+ warning("Getting unimplemented game variable %s (%d)", getVarName(variable), variable);
+ return 0;
+ }
+}
+
+void ScriptInterpreter::setGameVar(uint variable, int16 value) {
+ debug(0, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, getVarName(variable), value);
+
+ switch (variable) {
+ case 0:
+ _vm->_mouseDisabled = value;
+ CursorMan.showMouse(value == 0);
+ break;
+ case 3:
+ _vm->_mouseButton = value;
+ break;
+ case 4:
+ _vm->_screen->_verbLineY = value;
+ break;
+ case 5:
+ _vm->_screen->_verbLineX = value;
+ break;
+ case 6:
+ _vm->_screen->_verbLineWidth = value;
+ break;
+ case 7:
+ _vm->_screen->_verbLineCount = value;
+ break;
+ case 8:
+ _vm->_screen->_verbLineNum = value;
+ break;
+ case 9:
+ _vm->_screen->_talkTextItemNum = value;
+ break;
+ case 10:
+ _vm->_screen->_talkTextY = value;
+ break;
+ case 11:
+ _vm->_screen->_talkTextX = value;
+ break;
+ case 12:
+ _vm->_screen->_talkTextFontColor = value;
+ break;
+ case 13:
+ _vm->_cameraY = value;
+ break;
+ case 14:
+ _vm->_cameraX = value;
+ break;
+ case 15:
+ _vm->_walkSpeedY = value;
+ break;
+ case 16:
+ _vm->_walkSpeedX = value;
+ break;
+ case 17:
+ _vm->_flag01 = value != 0;
+ break;
+ case 18:
+ _vm->_sceneResIndex = value;
+ break;
+ case 19:
+ _vm->_guiHeight = value;
+ break;
+ case 20:
+ _vm->_sceneHeight = value;
+ break;
+ case 21:
+ _vm->_sceneWidth = value;
+ break;
+ case 1:
+ case 2:
+ default:
+ warning("Setting unimplemented game variable %s (%d) to %d", getVarName(variable), variable, value);
+ break;
+ }
+
+}
+
+byte ScriptInterpreter::arg8(int16 offset) {
+ return _subCode[offset];
+}
+
+int16 ScriptInterpreter::arg16(int16 offset) {
+ return READ_LE_UINT16(&_subCode[offset]);
+}
+
+void ScriptInterpreter::pushInt16(int16 value) {
+ WRITE_LE_UINT16(_stack + _regs.sp, value);
+ _regs.sp -= 2;
+}
+
+int16 ScriptInterpreter::popInt16() {
+ _regs.sp += 2;
+ return READ_LE_UINT16(_stack + _regs.sp);
+}
+
+void ScriptInterpreter::localWrite8(int16 offset, byte value) {
+ //debug(1, "localWrite8(%d, %d)", offset, value);
+ _localData[offset] = value;
+}
+
+byte ScriptInterpreter::localRead8(int16 offset) {
+ //debug(1, "localRead8(%d) -> %d", offset, _localData[offset]);
+ return _localData[offset];
+}
+
+void ScriptInterpreter::localWrite16(int16 offset, int16 value) {
+ //debug(1, "localWrite16(%d, %d)", offset, value);
+ WRITE_LE_UINT16(&_localData[offset], value);
+}
+
+int16 ScriptInterpreter::localRead16(int16 offset) {
+ //debug(1, "localRead16(%d) -> %d", offset, (int16)READ_LE_UINT16(&_localData[offset]));
+ return (int16)READ_LE_UINT16(&_localData[offset]);
+}
+
+byte *ScriptInterpreter::localPtr(int16 offset) {
+ //debug(1, "localPtr(%d)", offset);
+ return &_localData[offset];
+}
+
+void ScriptInterpreter::saveState(Common::WriteStream *out) {
+
+ // Save registers
+ out->writeUint16LE(_regs.reg0);
+ out->writeUint16LE(_regs.reg1);
+ out->writeUint16LE(_regs.reg2);
+ out->writeUint16LE(_regs.reg3);
+ out->writeUint16LE(_regs.reg4);
+ out->writeUint16LE(_regs.reg5);
+ out->writeUint16LE(_regs.reg6);
+ out->writeUint16LE(_regs.sp);
+ out->writeUint16LE(_regs.reg8);
+
+ // Save slots
+ for (int slot = 0; slot < kMaxScriptSlots; slot++) {
+ out->writeUint32LE(_slots[slot].size);
+ out->writeUint16LE(_slots[slot].resIndex);
+ if (_slots[slot].size > 0)
+ out->write(_slots[slot].data, _slots[slot].size);
+ }
+
+ // Save stack
+ out->write(_stack, kScriptStackSize);
+ out->writeUint16LE(_savedSp);
+
+ // Save IP
+ out->writeUint16LE((int16)(_code - getSlotData(_regs.reg4)));
+
+}
+
+void ScriptInterpreter::loadState(Common::ReadStream *in) {
+
+ // Load registers
+ _regs.reg0 = in->readUint16LE();
+ _regs.reg1 = in->readUint16LE();
+ _regs.reg2 = in->readUint16LE();
+ _regs.reg3 = in->readUint16LE();
+ _regs.reg4 = in->readUint16LE();
+ _regs.reg5 = in->readUint16LE();
+ _regs.reg6 = in->readUint16LE();
+ _regs.sp = in->readUint16LE();
+ _regs.reg8 = in->readUint16LE();
+
+ // Load slots
+ for (int slot = 0; slot < kMaxScriptSlots; slot++) {
+ _slots[slot].size = in->readUint32LE();
+ _slots[slot].resIndex = in->readUint16LE();
+ _slots[slot].data = NULL;
+ if (_slots[slot].size > 0) {
+ _slots[slot].data = new byte[_slots[slot].size];
+ in->read(_slots[slot].data, _slots[slot].size);
+ }
+ }
+
+ // Load stack
+ in->read(_stack, kScriptStackSize);
+ _savedSp = in->readUint16LE();
+
+ // Load IP
+ _code = getSlotData(_regs.reg4) + in->readUint16LE();
+
+}
+
+void ScriptInterpreter::sfNop() {
+ // NOP
+}
+
+void ScriptInterpreter::sfGetGameVar() {
+ int16 value = getGameVar(arg16(3));
+ localWrite16(arg16(5), value);
+}
+
+void ScriptInterpreter::sfSetGameVar() {
+ int16 varIndex = arg16(3);
+ VarType varType = getGameVarType(varIndex);
+ int16 value = 0;
+ if (varType == vtByte)
+ value = arg8(5);
+ else if (varType == vtWord)
+ value = arg16(5);
+ setGameVar(varIndex, value);
+}
+
+void ScriptInterpreter::sfUpdateScreen() {
+ _vm->updateScreen();
+}
+
+void ScriptInterpreter::sfGetRandomNumber() {
+ localWrite16(arg16(5), _vm->_rnd->getRandomNumber(arg16(3) - 1));
+}
+
+void ScriptInterpreter::sfDrawGuiTextMulti() {
+ _vm->_screen->drawGuiTextMulti((byte*)localPtr(arg16(3)));
+}
+
+void ScriptInterpreter::sfUpdateVerbLine() {
+ _vm->_screen->updateVerbLine(arg16(5), arg16(3));
+}
+
+void ScriptInterpreter::sfSetFontColor() {
+ _vm->_screen->_fontColor1 = 0;
+ _vm->_screen->_fontColor2 = arg8(3);
+}
+
+void ScriptInterpreter::sfGetTalkTextDuration() {
+ localWrite16(arg16(3), _vm->_screen->getTalkTextDuration());
+}
+
+void ScriptInterpreter::sfTalk() {
+ _vm->talk(arg16(5), arg16(3));
+}
+
+void ScriptInterpreter::sfFindPaletteFragment() {
+ localWrite16(arg16(5), _vm->_palette->findFragment(arg16(3)));
+}
+
+void ScriptInterpreter::sfClearPaletteFragments() {
+ _vm->_palette->clearFragments();
+}
+
+void ScriptInterpreter::sfAddPaletteFragment() {
+ _vm->_palette->addFragment(arg16(3), arg16(5));
+}
+
+void ScriptInterpreter::sfSetDeltaAnimPalette() {
+ _vm->_palette->setDeltaPalette(_vm->_palette->getAnimPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
+}
+
+void ScriptInterpreter::sfSetUnkPaletteEffect() {
+ // TODO
+ debug("ScriptInterpreter::sfSetUnkPaletteEffect");
+}
+
+void ScriptInterpreter::sfBuildColorTransTable() {
+ _vm->_palette->buildColorTransTable(arg8(4), (char)arg8(3), arg8(5));
+}
+
+void ScriptInterpreter::sfSetDeltaMainPalette() {
+ _vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
+}
+
+void ScriptInterpreter::sfLoadScript() {
+ int16 codeOfs = _code - getSlotData(_regs.reg4);
+ loadScript(arg16(4), arg8(3));
+ _code = getSlotData(_regs.reg4) + codeOfs;
+ _switchLocalDataNear = true;
+}
+
+void ScriptInterpreter::sfRegisterFont() {
+ _vm->_screen->registerFont(arg8(3), arg16(4));
+}
+
+void ScriptInterpreter::sfLoadAddPalette() {
+ _vm->_palette->loadAddPalette(arg16(4), arg8(3));
+}
+
+void ScriptInterpreter::sfLoadScene() {
+ if (arg8(3) == 0) {
+ // FIXME: Originally, this was stopSpeech(). However, we need to stop
+ // ALL sounds here (including sound effects and background sounds)
+ // before purgeCache() is called, otherwise the sound buffers will be
+ // invalidated. This is apparent when moving from a scene that has
+ // background sounds (such as the canyon at the beginning), to another
+ // one that doesn't (such as the map), and does not stop the sounds
+ // already playing. In this case, the engine will either crash or
+ // garbage will be heard through the speakers.
+ // TODO: We should either move purgeCache() elsewhere, or monitor
+ // which resources are still used before purging the cache.
+ _vm->_sound->stopAll();
+ _vm->_res->purgeCache();
+ _vm->loadScene(arg16(4));
+ } else {
+ _vm->_screen->loadMouseCursor(arg16(4));
+ }
+}
+
+void ScriptInterpreter::sfSetGuiHeight() {
+ _vm->setGuiHeight(arg8(3));
+}
+
+void ScriptInterpreter::sfFindMouseInRectIndex1() {
+ int16 index = -1;
+ if (_vm->_mouseY < _vm->_cameraHeight) {
+ int16 slotIndex = arg16(5);
+ index = _vm->findRectAtPoint(getSlotData(slotIndex) + arg16(3),
+ _vm->_mouseX + _vm->_cameraX,
+ _vm->_mouseY + _vm->_cameraY,
+ arg16(11) + 1, arg16(7),
+ getSlotData(slotIndex) + _slots[slotIndex].size);
+ }
+ localWrite16(arg16(9), index);
+}
+
+void ScriptInterpreter::sfFindMouseInRectIndex2() {
+ int16 index = -1;
+ if (_vm->_sceneResIndex != 0) {
+ if (_vm->_mouseY < _vm->_cameraHeight) {
+ int16 slotIndex = arg16(5);
+ index = _vm->findRectAtPoint(getSlotData(slotIndex) + arg16(3),
+ _vm->_mouseX + _vm->_cameraX,
+ _vm->_mouseY + _vm->_cameraY,
+ 0, arg16(7),
+ getSlotData(slotIndex) + _slots[slotIndex].size);
+ }
+ }
+ localWrite16(arg16(9), index);
+}
+
+void ScriptInterpreter::sfDrawGuiImage() {
+ _vm->_screen->drawGuiImage(arg16(5), arg16(3), arg16(7));
+}
+
+void ScriptInterpreter::sfAddAnimatedSpriteNoLoop() {
+ _vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte*)localPtr(0), (int16*)localPtr(arg16(9)), false, 2);
+}
+
+void ScriptInterpreter::sfAddAnimatedSprite() {
+ _vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte*)localPtr(0), (int16*)localPtr(arg16(9)), true, 2);
+}
+
+void ScriptInterpreter::sfAddStaticSprite() {
+ _vm->_screen->addStaticSprite(_subCode + 3);
+}
+
+void ScriptInterpreter::sfAddAnimatedSpriteScaled() {
+ _vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte*)localPtr(0), (int16*)localPtr(arg16(9)), true, 1);
+}
+
+void ScriptInterpreter::sfFindPath() {
+ _vm->_segmap->findPath((int16*)(getSlotData(arg16(13)) + arg16(11)), arg16(9), arg16(7), arg16(5), arg16(3));
+}
+
+void ScriptInterpreter::sfWalk() {
+ _vm->walk(getSlotData(arg16(5)) + arg16(3));
+}
+
+void ScriptInterpreter::sfScrollCameraUp() {
+ _vm->scrollCameraUp(4);
+}
+
+void ScriptInterpreter::sfScrollCameraDown() {
+ _vm->scrollCameraDown(4);
+}
+
+void ScriptInterpreter::sfScrollCameraLeft() {
+ _vm->scrollCameraLeft(4);
+}
+
+void ScriptInterpreter::sfScrollCameraRight() {
+ _vm->scrollCameraRight(4);
+}
+
+void ScriptInterpreter::sfScrollCameraUpEx() {
+ _vm->scrollCameraUp(arg16(3));
+}
+
+void ScriptInterpreter::sfScrollCameraDownEx() {
+ _vm->scrollCameraDown(arg16(3));
+}
+
+void ScriptInterpreter::sfScrollCameraLeftEx() {
+ _vm->scrollCameraLeft(arg16(3));
+}
+
+void ScriptInterpreter::sfScrollCameraRightEx() {
+ _vm->scrollCameraRight(arg16(3));
+}
+
+void ScriptInterpreter::sfSetCamera() {
+ _vm->setCamera(arg16(5), arg16(3));
+}
+
+void ScriptInterpreter::sfGetCameraChanged() {
+ localWrite16(arg16(3), _vm->getCameraChanged() ? 1 : 0);
+}
+
+void ScriptInterpreter::sfGetRgbModifiertAtPoint() {
+ byte *rgb = getSlotData(arg16(11)) + arg16(9);
+ _vm->_segmap->getRgbModifiertAtPoint(arg16(5), arg16(3), arg16(7), rgb[0], rgb[1], rgb[2]);
+}
+
+void ScriptInterpreter::sfStartAnim() {
+ _vm->_anim->start(arg16(3));
+}
+
+void ScriptInterpreter::sfAnimNextFrame() {
+ _vm->_anim->nextFrame();
+}
+
+void ScriptInterpreter::sfGetAnimFrameNumber() {
+ localWrite16(arg16(3), _vm->_anim->getFrameNumber());
+}
+
+void ScriptInterpreter::sfGetAnimStatus() {
+ int16 status = _vm->_anim->getStatus();
+ if (status == 0 || status == 1) {
+ // TODO mov screenFlag01, 0
+ }
+ localWrite16(arg16(3), status);
+}
+
+void ScriptInterpreter::sfStartShakeScreen() {
+ _vm->_screen->startShakeScreen(arg16(3));
+}
+
+void ScriptInterpreter::sfStopShakeScreen() {
+ _vm->_screen->stopShakeScreen();
+}
+
+void ScriptInterpreter::sfStartSequence() {
+ int16 sequenceResIndex = arg16(3);
+ //debug("ScriptInterpreter::sfStartSequence(%d)", sequenceResIndex);
+ if (sequenceResIndex >= 0) {
+ //_vm->_arc->dump(sequenceResIndex, "music"); // DEBUG: Dump music so we know what's in there
+
+ int32 resourceSize = _vm->_arc->getResourceSize(sequenceResIndex);
+ byte *data = new byte[resourceSize];
+ _vm->_arc->openResource(sequenceResIndex);
+ _vm->_arc->read(data, resourceSize);
+ _vm->_arc->closeResource();
+
+ if (!memcmp(data, "FORM", 4)) {
+ // TODO: It seems that music is always looping?
+ _vm->_musicPlayer->playMIDI(data, resourceSize, true);
+ } else {
+ // Sanity check: this should never occur
+ error("sfStartSequence: resource %d isn't XMIDI", sequenceResIndex);
+ }
+
+ delete[] data;
+ }
+}
+
+void ScriptInterpreter::sfEndSequence() {
+ //debug("ScriptInterpreter::sfEndSequence");
+ _vm->_musicPlayer->stopAndClear();
+}
+
+void ScriptInterpreter::sfSetSequenceVolume() {
+ // TODO
+ //debug("ScriptInterpreter::sfSetSequenceVolume");
+}
+
+void ScriptInterpreter::sfPlayPositionalSound() {
+ _vm->_sound->playSoundAtPos(arg16(3), arg16(9), arg16(7));
+}
+
+void ScriptInterpreter::sfPlaySound2() {
+ _vm->_sound->playSound(arg16(3), arg16(5), arg16(7));
+}
+
+void ScriptInterpreter::sfClearScreen() {
+ // TODO: Occurs on every scene change, but seems unneeded
+ //debug("ScriptInterpreter::sfClearScreen");
+}
+
+void ScriptInterpreter::sfHandleInput() {
+ int16 varOfs = arg16(3);
+ int16 keyCode = 0;
+ if (_vm->_rightButtonDown) {
+ keyCode = 1;
+ } else {
+ /* Convert keyboard scancode to IBM PC scancode
+ Only scancodes known to be used (so far) are converted
+ */
+ switch (_vm->_keyState.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ keyCode = 1;
+ break;
+ case Common::KEYCODE_F10:
+ keyCode = 68;
+ break;
+ default:
+ break;
+ }
+ }
+ localWrite16(varOfs, keyCode);
+}
+
+void ScriptInterpreter::sfRunOptionsScreen() {
+ _vm->_screen->loadMouseCursor(12);
+ _vm->_palette->loadAddPalette(9, 224);
+ _vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), 7, 0, 31, 224);
+ _vm->_screen->finishTalkTextItems();
+ _vm->_screen->clearSprites();
+ CursorMan.showMouse(true);
+ _vm->_menuSystem->run();
+ _vm->_keyState.reset();
+ _switchLocalDataNear = true;
+}
+
+/* NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and
+ sfDeletePrecachedFiles were used by the original engine to handle precaching
+ of data so the game doesn't stall while playing (due to the slow speed of
+ CD-Drives back then). This is not needed in ScummVM since all supported
+ systems are fast enough to load data in-game. */
+
+void ScriptInterpreter::sfPrecacheSprites() {
+ // See note above
+}
+
+void ScriptInterpreter::sfPrecacheSounds1() {
+ // See note above
+}
+
+void ScriptInterpreter::sfDeletePrecachedFiles() {
+ // See note above
+}
+
+void ScriptInterpreter::sfPrecacheSounds2() {
+ // See note above
+}
+
+void ScriptInterpreter::sfRestoreStackPtr() {
+ _regs.sp = _savedSp;
+}
+
+void ScriptInterpreter::sfSaveStackPtr() {
+ _savedSp = _regs.sp;
+}
+
+void ScriptInterpreter::sfPlayMovie() {
+ _vm->_moviePlayer->playMovie(arg16(3));
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/script.h b/engines/toltecs/script.h
new file mode 100644
index 0000000000..1a5a9c90ad
--- /dev/null
+++ b/engines/toltecs/script.h
@@ -0,0 +1,184 @@
+/* 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 TOLTECS_SCRIPT_H
+#define TOLTECS_SCRIPT_H
+
+#include "common/func.h"
+
+namespace Toltecs {
+
+const int kMaxScriptSlots = 50;
+const int kScriptStackSize = 4096 + 4;
+
+enum VarType {
+ vtByte,
+ vtWord
+};
+
+typedef Common::Functor0<void> ScriptFunction;
+
+class ScriptInterpreter {
+public:
+ ScriptInterpreter(ToltecsEngine *vm);
+ ~ScriptInterpreter();
+
+ void loadScript(uint resIndex, uint slotIndex);
+ void setMainScript(uint slotIndex);
+ void runScript();
+
+ byte *getSlotData(int slotIndex) const { return _slots[slotIndex].data; }
+
+ VarType getGameVarType(uint variable);
+ int16 getGameVar(uint variable);
+ void setGameVar(uint variable, int16 value);
+
+ void saveState(Common::WriteStream *out);
+ void loadState(Common::ReadStream *in);
+
+protected:
+
+ struct ScriptRegs {
+ int16 reg0;
+ int16 reg1;
+ int16 reg2;
+ int16 reg3;
+ int16 reg4;
+ int16 reg5;
+ int16 reg6;
+ int16 sp;
+ int16 reg8;
+ };
+
+ struct ScriptSlot {
+ byte *data;
+ int32 size;
+ uint resIndex;
+ };
+
+ ToltecsEngine *_vm;
+ Common::Array<const ScriptFunction*> _scriptFuncs;
+ Common::Array<const char *> _scriptFuncNames;
+
+ byte *_stack;
+
+ byte *_code, *_subCode;
+ byte *_localData;
+ bool _switchLocalDataNear, _switchLocalDataFar, _switchLocalDataToStack;
+ bool _cmpBitTest;
+
+ ScriptSlot _slots[kMaxScriptSlots];
+
+ ScriptRegs _regs;
+ int16 _savedSp;
+
+ byte readByte();
+ int16 readInt16();
+
+ void execOpcode(byte opcode);
+
+ void setupScriptFunctions();
+ void execScriptFunction(uint16 index);
+
+ byte arg8(int16 offset);
+ int16 arg16(int16 offset);
+
+ void pushInt16(int16 value);
+ int16 popInt16();
+
+ void localWrite8(int16 offset, byte value);
+ byte localRead8(int16 offset);
+ void localWrite16(int16 offset, int16 value);
+ int16 localRead16(int16 offset);
+ byte *localPtr(int16 offset);
+
+ void sfNop();
+ void sfGetGameVar();
+ void sfSetGameVar();
+ void sfUpdateScreen();
+ void sfGetRandomNumber();
+ void sfDrawGuiTextMulti();
+ void sfUpdateVerbLine();
+ void sfSetFontColor();
+ void sfGetTalkTextDuration();
+ void sfTalk();
+ void sfFindPaletteFragment();
+ void sfClearPaletteFragments();
+ void sfAddPaletteFragment();
+ void sfSetDeltaAnimPalette();
+ void sfSetUnkPaletteEffect();
+ void sfBuildColorTransTable();
+ void sfSetDeltaMainPalette();
+ void sfLoadScript();
+ void sfRegisterFont();
+ void sfLoadAddPalette();
+ void sfLoadScene();
+ void sfSetGuiHeight();
+ void sfFindMouseInRectIndex1();
+ void sfFindMouseInRectIndex2();
+ void sfDrawGuiImage();
+ void sfAddAnimatedSpriteNoLoop();
+ void sfAddAnimatedSprite();
+ void sfAddStaticSprite();
+ void sfAddAnimatedSpriteScaled();
+ void sfFindPath();
+ void sfWalk();
+ void sfScrollCameraUp();
+ void sfScrollCameraDown();
+ void sfScrollCameraLeft();
+ void sfScrollCameraRight();
+ void sfScrollCameraUpEx();
+ void sfScrollCameraDownEx();
+ void sfScrollCameraLeftEx();
+ void sfScrollCameraRightEx();
+ void sfSetCamera();
+ void sfGetCameraChanged();
+ void sfGetRgbModifiertAtPoint();
+ void sfStartAnim();
+ void sfAnimNextFrame();
+ void sfGetAnimFrameNumber();
+ void sfGetAnimStatus();
+ void sfStartShakeScreen();
+ void sfStopShakeScreen();
+ void sfStartSequence();
+ void sfEndSequence();
+ void sfSetSequenceVolume();
+ void sfPlayPositionalSound();
+ void sfPlaySound2();
+ void sfClearScreen();
+ void sfHandleInput();
+ void sfRunOptionsScreen();
+ void sfPrecacheSprites();
+ void sfPrecacheSounds1();
+ void sfDeletePrecachedFiles();
+ void sfPrecacheSounds2();
+ void sfRestoreStackPtr();
+ void sfSaveStackPtr();
+ void sfPlayMovie();
+
+};
+
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_H */
diff --git a/engines/toltecs/segmap.cpp b/engines/toltecs/segmap.cpp
new file mode 100644
index 0000000000..75b0c633cd
--- /dev/null
+++ b/engines/toltecs/segmap.cpp
@@ -0,0 +1,408 @@
+/* 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 "toltecs/toltecs.h"
+#include "toltecs/render.h"
+#include "toltecs/segmap.h"
+
+namespace Toltecs {
+
+SegmentMap::SegmentMap(ToltecsEngine *vm) : _vm(vm) {
+}
+
+SegmentMap::~SegmentMap() {
+ freeSegmapMaskRectSurfaces();
+}
+
+void SegmentMap::load(byte *source) {
+
+ freeSegmapMaskRectSurfaces();
+ _maskRects.clear();
+ _pathRects.clear();
+ _infoRects.clear();
+
+ // Load mask rects
+ byte *maskData = source + 2;
+ uint16 maskSize = READ_LE_UINT16(source);
+ source += 2;
+ uint16 maskRectCount = READ_LE_UINT16(source);
+ source += 2;
+ uint16 maskRectDataSize = maskRectCount * 12 + 2;
+
+ debug(0, "SegmentMap::load() maskRectCount = %d", maskRectCount);
+
+ for (uint16 i = 0; i < maskRectCount; i++) {
+ SegmapMaskRect maskRect;
+ int16 maskOffset;
+ maskRect.y = READ_LE_UINT16(source);
+ maskRect.x = READ_LE_UINT16(source + 2);
+ maskRect.height = READ_LE_UINT16(source + 4);
+ maskRect.width = READ_LE_UINT16(source + 6);
+ maskOffset = READ_LE_UINT16(source + 8);
+ maskRect.priority = READ_LE_UINT16(source + 10);
+ loadSegmapMaskRectSurface(maskData + maskOffset, maskRect);
+
+ debug(0, "SegmentMap::load() (%d, %d, %d, %d, %04X, %d)",
+ maskRect.x, maskRect.y, maskRect.width, maskRect.height, maskOffset, maskRect.priority);
+
+ source += 12;
+ _maskRects.push_back(maskRect);
+ }
+
+ source += maskSize - maskRectDataSize;
+
+ // Load path rects
+
+ source += 2; // skip rects array size
+
+ uint16 pathRectCount = READ_LE_UINT16(source);
+ source += 2;
+
+ debug(0, "SegmentMap::load() pathRectCount = %d", pathRectCount);
+
+ for (uint16 i = 0; i < pathRectCount; i++) {
+ SegmapPathRect pathRect;
+ pathRect.y1 = READ_LE_UINT16(source);
+ pathRect.x1 = READ_LE_UINT16(source + 2);
+ pathRect.y2 = pathRect.y1 + READ_LE_UINT16(source + 4);
+ pathRect.x2 = pathRect.x1 + READ_LE_UINT16(source + 6);
+
+ debug(0, "SegmentMap::load() (%d, %d, %d, %d)", pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2);
+
+ source += 8;
+ _pathRects.push_back(pathRect);
+ }
+
+ // Load info rects
+
+ source += 2; // skip rects array size
+
+ uint16 infoRectCount = READ_LE_UINT16(source);
+ source += 2;
+ debug(0, "SegmentMap::load() infoRectCount = %d", infoRectCount);
+ for (uint16 i = 0; i < infoRectCount; i++) {
+ SegmapInfoRect infoRect;
+ infoRect.y = READ_LE_UINT16(source);
+ infoRect.x = READ_LE_UINT16(source + 2);
+ infoRect.height = READ_LE_UINT16(source + 4);
+ infoRect.width = READ_LE_UINT16(source + 6);
+ infoRect.id = source[8];
+ infoRect.a = source[9];
+ infoRect.b = source[10];
+ infoRect.c = source[11];
+
+ debug(0, "SegmentMap::load() (%d, %d, %d, %d) (%d, %d, %d, %d)",
+ infoRect.x, infoRect.y, infoRect.width, infoRect.height,
+ infoRect.id, (int8)infoRect.a, (int8)infoRect.b, (int8)infoRect.c);
+
+ source += 12;
+ _infoRects.push_back(infoRect);
+ }
+
+ // TODO Other stuff
+
+
+}
+
+int16 SegmentMap::findPathRectAtPoint(int16 x, int16 y) {
+ for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
+ if (y >= _pathRects[rectIndex].y1 && y <= _pathRects[rectIndex].y2 &&
+ x >= _pathRects[rectIndex].x1 && x <= _pathRects[rectIndex].x2) {
+ return rectIndex;
+ }
+ }
+ return -1;
+}
+
+void SegmentMap::adjustPathPoint(int16 &x, int16 &y) {
+
+ if (findPathRectAtPoint(x, y) != -1)
+ return;
+
+ uint32 minDistance = 0xFFFFFFFF, distance;
+ int16 adjustedX = 0, adjustedY = 0, x2, y2;
+
+ for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
+
+ if (x >= _pathRects[rectIndex].x1 && x < _pathRects[rectIndex].x2) {
+ x2 = x;
+ } else if (ABS(x - _pathRects[rectIndex].x1) >= ABS(x - _pathRects[rectIndex].x2)) {
+ x2 = _pathRects[rectIndex].x2;
+ } else {
+ x2 = _pathRects[rectIndex].x1;
+ }
+
+ if (ABS(y - _pathRects[rectIndex].y1) >= ABS(y - _pathRects[rectIndex].y2)) {
+ y2 = _pathRects[rectIndex].y2;
+ } else {
+ y2 = _pathRects[rectIndex].y1;
+ }
+
+ distance = ABS(y - y2) + ABS(x - x2);
+ if (distance < minDistance) {
+ if (x >= _pathRects[rectIndex].x1 && x <= _pathRects[rectIndex].x2) {
+ adjustedX = x;
+ } else {
+ adjustedX = x2;
+ }
+ if (y >= _pathRects[rectIndex].y1 && y <= _pathRects[rectIndex].y2) {
+ adjustedY = y;
+ } else {
+ adjustedY = y2;
+ }
+ minDistance = distance;
+ }
+
+ }
+
+ x = adjustedX;
+ y = adjustedY;
+
+}
+
+int16 SegmentMap::findNextPathRect(int16 srcRectIndex, int16 destX, int16 destY) {
+
+ int16 result;
+ uint16 minDistance, distance;
+ int16 x1, y1, x2, y2;
+ int16 xmin, xmax, ymax, ymin;
+
+ result = -1;
+ minDistance = 0xFFFF;
+
+ x1 = _pathRects[srcRectIndex].x1;
+ y1 = _pathRects[srcRectIndex].y1;
+ x2 = _pathRects[srcRectIndex].x2;
+ y2 = _pathRects[srcRectIndex].y2;
+
+ for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
+
+ int16 nodeX = -1, nodeY = -1;
+
+ // Check if the current rectangle is connected to the source rectangle
+ if (x1 == _pathRects[rectIndex].x2 && y1 < _pathRects[rectIndex].y2 && y2 > _pathRects[rectIndex].y1) {
+ nodeX = x1;
+ } else if (x2 == _pathRects[rectIndex].x1 && y1 < _pathRects[rectIndex].y2 && y2 > _pathRects[rectIndex].y1) {
+ nodeX = x2 - 1;
+ } else if (y1 == _pathRects[rectIndex].y2 && x1 < _pathRects[rectIndex].x2 && x2 > _pathRects[rectIndex].x1) {
+ nodeY = y1;
+ } else if (y2 == _pathRects[rectIndex].y1 && x1 < _pathRects[rectIndex].x2 && x2 > _pathRects[rectIndex].x1) {
+ nodeY = y2 - 1;
+ } else
+ continue;
+
+ if (nodeX == -1) {
+ xmin = MAX<int16>(x1, _pathRects[rectIndex].x1);
+ xmax = MIN<int16>(x2, _pathRects[rectIndex].x2) - 1;
+ if (destX > xmin && destX < xmax) {
+ nodeX = destX;
+ } else if (ABS(destX - xmin) >= ABS(destX - xmax)) {
+ nodeX = xmax - 1;
+ } else {
+ nodeX = xmin;
+ }
+ }
+
+ if (nodeY == -1) {
+ ymin = MAX<int16>(y1, _pathRects[rectIndex].y1);
+ ymax = MIN<int16>(y2, _pathRects[rectIndex].y2) - 1;
+ if (destY > ymin && destY < ymax) {
+ nodeY = destY;
+ } else if (ABS(destY - ymin) >= ABS(destY - ymax)) {
+ nodeY = ymax - 1;
+ } else {
+ nodeY = ymin;
+ }
+ }
+
+ distance = ABS(destX - nodeX) + ABS(destY - nodeY);
+
+ for (uint i = 0; i < _closedPathRectsCount; i++) {
+ if (rectIndex == _closedPathRects[i]) {
+ distance = minDistance;
+ break;
+ }
+ }
+
+ for (uint i = 0; i < _deadEndPathRectsCount; i++) {
+ if (rectIndex == _deadEndPathRects[i]) {
+ distance = minDistance;
+ break;
+ }
+ }
+
+ if (distance < minDistance) {
+ result = rectIndex;
+ minDistance = distance;
+ _pathNodes[_pathNodesCount].x = nodeX;
+ _pathNodes[_pathNodesCount].y = nodeY;
+ }
+
+ }
+
+ return result;
+}
+
+struct LineData {
+ int pitch;
+ byte *surf;
+};
+
+void plotProc(int x, int y, int color, void *data) {
+ LineData *ld = (LineData*)data;
+ ld->surf[x + y * ld->pitch] = color;
+}
+
+void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 sourceX, int16 sourceY) {
+
+ // TODO: Writes to pointsArray aren't endian-safe yet
+
+ int16 currentRectIndex, destRectIndex;
+ int16 pointsCount;
+
+ debug(0, "SegmentMap::findPath(fromX: %d; fromY: %d; toX: %d; toY: %d)", sourceX, sourceY, destX, destY);
+
+ _deadEndPathRectsCount = 0;
+ _closedPathRectsCount = 0;
+ _pathNodesCount = 0;
+
+ pointsCount = 2;
+
+ adjustPathPoint(sourceX, sourceY);
+ currentRectIndex = findPathRectAtPoint(sourceX, sourceY);
+
+ adjustPathPoint(destX, destY);
+ destRectIndex = findPathRectAtPoint(destX, destY);
+
+ if (currentRectIndex != -1) {
+ if (destRectIndex != currentRectIndex) {
+ while (1) {
+ do {
+ _closedPathRects[_closedPathRectsCount++] = currentRectIndex;
+ currentRectIndex = findNextPathRect(currentRectIndex, destX, destY);
+ _pathNodesCount++;
+ } while (currentRectIndex != -1 && currentRectIndex != destRectIndex);
+ if (currentRectIndex != -1 && currentRectIndex == destRectIndex)
+ break;
+ _deadEndPathRects[_deadEndPathRectsCount++] = _closedPathRects[--_closedPathRectsCount];
+ _pathNodesCount -= 2;
+ currentRectIndex = _closedPathRects[--_closedPathRectsCount];
+ }
+ for (int16 i = 0; i < _pathNodesCount; i++) {
+ pointsArray[pointsCount++] = _pathNodes[i].y;
+ pointsArray[pointsCount++] = _pathNodes[i].x;
+ }
+ }
+
+ pointsArray[pointsCount++] = destY;
+ pointsArray[pointsCount++] = destX;
+
+ pointsArray[0] = 0;
+ pointsArray[1] = _pathNodesCount + 1;
+ }
+
+ debug(0, "SegmentMap::findPath() count = %d", pointsArray[1]);
+
+#if 0 // DEBUG: Draw the path we found
+ int sx = sourceX, sy = sourceY;
+ LineData ld;
+ ld.pitch = _vm->_sceneWidth;
+ ld.surf = _vm->_screen->_backScreen;
+ for (int16 i = 0; i < pointsArray[1] * 2; i+=2) {
+ debug(0, "x = %d; y = %d", pointsArray[3+i], pointsArray[2+i]);
+ Graphics::drawLine(sx, sy, pointsArray[3+i], pointsArray[2+i], 0xFF, plotProc, &ld);
+ sx = pointsArray[3+i];
+ sy = pointsArray[2+i];
+ }
+#endif
+
+}
+
+int8 SegmentMap::getScalingAtPoint(int16 x, int16 y) {
+ int8 scaling = 0;
+ for (uint i = 0; i < _infoRects.size(); i++) {
+ if (_infoRects[i].id == 0 && _infoRects[i].isPointInside(x, y)) {
+ int8 topScaling = (int8)_infoRects[i].b;
+ int8 bottomScaling = (int8)_infoRects[i].c;
+ if (y - _infoRects[i].y != 0) {
+ scaling = (ABS(y - _infoRects[i].y) * (bottomScaling - topScaling) / _infoRects[i].height) + topScaling;
+ }
+ }
+ }
+ debug(0, "SegmentMap::getScalingAtPoint(%d, %d) %d", x, y, scaling);
+ return scaling;
+}
+
+void SegmentMap::getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b) {
+ r = 0;
+ g = 0;
+ b = 0;
+ for (uint i = 0; i < _infoRects.size(); i++) {
+ if (_infoRects[i].id == id && _infoRects[i].isPointInside(x, y)) {
+ r = _infoRects[i].a;
+ g = _infoRects[i].b;
+ b = _infoRects[i].c;
+ }
+ }
+ debug(0, "SegmentMap::getRgbModifiertAtPoint() r: %d; g: %d; b: %d", r, g, b);
+}
+
+void SegmentMap::loadSegmapMaskRectSurface(byte *maskData, SegmapMaskRect &maskRect) {
+
+ maskRect.surface = new Graphics::Surface();
+ maskRect.surface->create(maskRect.width, maskRect.height, Graphics::PixelFormat::createFormatCLUT8());
+
+ byte *backScreen = _vm->_screen->_backScreen + maskRect.x + (maskRect.y * _vm->_sceneWidth);
+ byte *dest = (byte*)maskRect.surface->getBasePtr(0, 0);
+
+ for (int16 h = 0; h < maskRect.height; h++) {
+ int16 w = maskRect.width;
+ while (w > 0) {
+ byte mask = *maskData++;
+ byte count = mask & 0x7F;
+ if (mask & 0x80)
+ memcpy(dest, backScreen, count);
+ else
+ memset(dest, 0xFF, count);
+ w -= count;
+ dest += count;
+ backScreen += count;
+ }
+ backScreen += _vm->_sceneWidth - maskRect.width;
+ }
+
+}
+
+void SegmentMap::freeSegmapMaskRectSurfaces() {
+ for (uint i = 0; i < _maskRects.size(); i++) {
+ delete _maskRects[i].surface;
+ }
+}
+
+void SegmentMap::addMasksToRenderQueue() {
+ for (uint i = 0; i < _maskRects.size(); i++) {
+ _vm->_screen->_renderQueue->addMask(_maskRects[i]);
+ }
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/segmap.h b/engines/toltecs/segmap.h
new file mode 100644
index 0000000000..30182a6b71
--- /dev/null
+++ b/engines/toltecs/segmap.h
@@ -0,0 +1,116 @@
+/* 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 TOLTECS_SEGMAP_H
+#define TOLTECS_SEGMAP_H
+
+#include "common/array.h"
+
+#include "toltecs/screen.h"
+
+namespace Toltecs {
+
+struct ScriptWalk {
+ int16 y, x;
+ int16 y1, x1, y2, x2;
+ int16 yerror, xerror;
+ int16 mulValue;
+ int16 scaling;
+};
+
+struct SegmapMaskRect {
+ int16 x, y;
+ int16 width, height;
+ int16 priority;
+ Graphics::Surface *surface;
+};
+
+class SegmentMap {
+public:
+ SegmentMap(ToltecsEngine *vm);
+ ~SegmentMap();
+
+ void load(byte *source);
+
+ int16 findPathRectAtPoint(int16 x, int16 y);
+ void adjustPathPoint(int16 &x, int16 &y);
+
+ void findPath(int16 *pointsArray, int16 destX, int16 destY, int16 sourceX, int16 sourceY);
+
+ int8 getScalingAtPoint(int16 x, int16 y);
+ void getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b);
+
+ void addMasksToRenderQueue();
+
+//protected:
+public: // for debugging purposes
+
+ struct SegmapPathRect {
+ int16 x1, y1, x2, y2;
+ };
+
+ struct SegmapInfoRect {
+ int16 y, x;
+ int16 height, width;
+ byte id;
+ byte a, b, c;
+ inline bool isPointInside(int16 px, int16 py) {
+ return py >= y && py <= y + height && px >= x && px <= x + width;
+ }
+ };
+
+ struct PathPoint {
+ int16 y, x;
+ };
+
+ typedef Common::Array<SegmapMaskRect> SegmapMaskRectArray;
+ typedef Common::Array<SegmapPathRect> SegmapPathRectArray;
+ typedef Common::Array<SegmapInfoRect> SegmapInfoRectArray;
+
+ ToltecsEngine *_vm;
+
+ SegmapMaskRectArray _maskRects;
+ byte *_maskRectData;
+
+ SegmapPathRectArray _pathRects;
+ SegmapInfoRectArray _infoRects;
+
+ int16 _deadEndPathRects[1000];
+ uint _deadEndPathRectsCount;
+
+ int16 _closedPathRects[1000];
+ uint _closedPathRectsCount;
+
+ PathPoint _pathNodes[1000];
+ int16 _pathNodesCount;
+
+ int16 findNextPathRect(int16 srcRectIndex, int16 destX, int16 destY);
+
+ void loadSegmapMaskRectSurface(byte *maskData, SegmapMaskRect &maskRect);
+ void freeSegmapMaskRectSurfaces();
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_SEGMAP_H */
diff --git a/engines/toltecs/sound.cpp b/engines/toltecs/sound.cpp
new file mode 100644
index 0000000000..47d52637fb
--- /dev/null
+++ b/engines/toltecs/sound.cpp
@@ -0,0 +1,188 @@
+/* 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 "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "audio/decoders/raw.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/resource.h"
+#include "toltecs/segmap.h"
+#include "toltecs/sound.h"
+
+namespace Toltecs {
+
+Sound::Sound(ToltecsEngine *vm) : _vm(vm) {
+ for (int i = 0; i < kMaxChannels; i++) {
+ channels[i].type = kChannelTypeEmpty;
+ channels[i].resIndex = -1;
+ }
+}
+
+Sound::~Sound() {
+}
+
+void Sound::playSpeech(int16 resIndex) {
+ debug(0, "playSpeech(%d)", resIndex);
+ internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0);
+}
+
+void Sound::playSound(int16 resIndex, int16 type, int16 volume) {
+
+ // TODO: Use the right volumes
+
+ debug(0, "playSound(%d, %d, %d)", resIndex, type, volume);
+
+ if (volume == -1 || type == -2) {
+ if (type == kChannelTypeBackground) {
+ internalPlaySound(resIndex, type, 50 /*TODO*/, 0);
+ } else {
+ internalPlaySound(resIndex, type, 100 /*TODO*/, 0);
+ }
+ } else {
+ internalPlaySound(resIndex, type, 100 /*TODO*/, 0);
+ }
+
+}
+
+void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) {
+
+ debug(0, "playSoundAtPos(%d, %d, %d)", resIndex, x, y);
+
+ int16 volume, panning = 0, deltaX = 0;
+ int8 scaling = _vm->_segmap->getScalingAtPoint(x, y);
+
+ if (scaling >= 0)
+ volume = 50 + ABS(scaling) / 2;
+ else
+ volume = 50 - ABS(scaling) / 2;
+
+ if (_vm->_cameraX > x)
+ deltaX = _vm->_cameraX - x;
+ else if (_vm->_cameraX + 640 < x)
+ deltaX = x - (_vm->_cameraX + 640);
+ if (deltaX > 600)
+ deltaX = 600;
+
+ volume = ((100 - deltaX / 6) * volume) / 100;
+
+ if (_vm->_cameraX + 320 != x) {
+ panning = CLIP(x - (_vm->_cameraX + 320), -381, 381) / 3;
+ }
+
+ internalPlaySound(resIndex, 1, volume, panning);
+
+}
+
+void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning) {
+
+ if (resIndex == -1) {
+ // Stop all sounds
+ _vm->_mixer->stopAll();
+ _vm->_screen->keepTalkTextItemsAlive();
+ for (int i = 0; i < kMaxChannels; i++) {
+ channels[i].type = kChannelTypeEmpty;
+ channels[i].resIndex = -1;
+ }
+ } else if (type == -2) {
+ // Stop sounds with specified resIndex
+ for (int i = 0; i < kMaxChannels; i++) {
+ if (channels[i].resIndex == resIndex) {
+ _vm->_mixer->stopHandle(channels[i].handle);
+ channels[i].type = kChannelTypeEmpty;
+ channels[i].resIndex = -1;
+ }
+ }
+ } else {
+
+ if (type == -3) {
+ // Stop speech and play new sound
+ stopSpeech();
+ }
+
+ // Play new sound in empty channel
+ int freeChannel = -1;
+ for (int i = 0; i < kMaxChannels; i++) {
+ if (channels[i].type == kChannelTypeEmpty || !_vm->_mixer->isSoundHandleActive(channels[i].handle)) {
+ freeChannel = i;
+ break;
+ }
+ }
+
+ // If all channels are in use no new sound will be played
+ if (freeChannel >= 0) {
+
+ Resource *soundResource = _vm->_res->load(resIndex);
+
+ Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
+ Audio::makeRawStream(soundResource->data, soundResource->size, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO),
+ type == kChannelTypeBackground ? 0 : 1);
+
+ channels[freeChannel].type = type;
+ channels[freeChannel].resIndex = resIndex;
+
+ Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
+ /*
+ switch (type) {
+ }
+ */
+
+ _vm->_mixer->playStream(soundType, &channels[freeChannel].handle,
+ stream, -1, volume, panning);
+
+ }
+
+ }
+
+}
+
+void Sound::updateSpeech() {
+ for (int i = 0; i < kMaxChannels; i++) {
+ if (channels[i].type == kChannelTypeSpeech && _vm->_mixer->isSoundHandleActive(channels[i].handle)) {
+ _vm->_screen->keepTalkTextItemsAlive();
+ break;
+ }
+ }
+}
+
+void Sound::stopSpeech() {
+ for (int i = 0; i < kMaxChannels; i++) {
+ if (channels[i].type == kChannelTypeSpeech) {
+ _vm->_mixer->stopHandle(channels[i].handle);
+ _vm->_screen->keepTalkTextItemsAlive();
+ channels[i].type = kChannelTypeEmpty;
+ channels[i].resIndex = -1;
+ }
+ }
+}
+
+void Sound::stopAll() {
+ for (int i = 0; i < kMaxChannels; i++) {
+ _vm->_mixer->stopHandle(channels[i].handle);
+ _vm->_screen->keepTalkTextItemsAlive();
+ channels[i].type = kChannelTypeEmpty;
+ channels[i].resIndex = -1;
+ }
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/sound.h b/engines/toltecs/sound.h
new file mode 100644
index 0000000000..e4580effa1
--- /dev/null
+++ b/engines/toltecs/sound.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 TOLTECS_SOUND_H
+#define TOLTECS_SOUND_H
+
+#include "audio/mixer.h" // for Audio::SoundHandle
+
+#include "toltecs/toltecs.h"
+
+namespace Toltecs {
+
+// 0x1219
+
+enum SoundChannelType {
+ kChannelTypeEmpty = 0,
+ kChannelTypeBackground = -1,
+ kChannelTypeSfx = -2,
+ kChannelTypeSpeech = -3
+};
+
+struct SoundChannel {
+ int16 resIndex;
+ int16 type;
+ Audio::SoundHandle handle;
+};
+
+const int kMaxChannels = 4;
+
+class Sound {
+public:
+ Sound(ToltecsEngine *vm);
+ ~Sound();
+
+ void playSpeech(int16 resIndex);
+ void playSound(int16 resIndex, int16 type, int16 volume);
+ void playSoundAtPos(int16 resIndex, int16 x, int16 y);
+ void updateSpeech();
+ void stopSpeech();
+ void stopAll();
+
+protected:
+ ToltecsEngine *_vm;
+
+ SoundChannel channels[kMaxChannels];
+
+ void internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning);
+
+};
+
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_SOUND_H */
diff --git a/engines/toltecs/sprite.cpp b/engines/toltecs/sprite.cpp
new file mode 100644
index 0000000000..7a02663793
--- /dev/null
+++ b/engines/toltecs/sprite.cpp
@@ -0,0 +1,509 @@
+/* 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 "toltecs/toltecs.h"
+#include "toltecs/palette.h"
+#include "toltecs/render.h"
+#include "toltecs/resource.h"
+
+namespace Toltecs {
+
+class SpriteReader : public SpriteFilter {
+public:
+ SpriteReader(byte *source, const SpriteDrawItem &sprite) : SpriteFilter(sprite), _source(source) {
+ _curWidth = _sprite->origWidth;
+ _curHeight = _sprite->origHeight;
+ }
+ SpriteReaderStatus readPacket(PixelPacket &packet) {
+ if (_sprite->flags & 0x40) {
+ // shadow sprite
+ packet.count = _source[0] & 0x7F;
+ if (_source[0] & 0x80)
+ packet.pixel = 1;
+ else
+ packet.pixel = 0;
+ _source++;
+ } else if (_sprite->flags & 0x10) {
+ // 256-color sprite
+ packet.pixel = *_source++;
+ packet.count = *_source++;
+ } else {
+ // 16-color sprite
+ packet.count = _source[0] & 0x0F;
+ packet.pixel = (_source[0] & 0xF0) >> 4;
+ _source++;
+ }
+ _curWidth -= packet.count;
+ if (_curWidth <= 0) {
+ _curHeight--;
+ if (_curHeight == 0) {
+ return kSrsEndOfSprite;
+ } else {
+ _curWidth = _sprite->origWidth;
+ return kSrsEndOfLine;
+ }
+ } else {
+ return kSrsPixelsLeft;
+ }
+ }
+ byte *getSource() {
+ return _source;
+ }
+ void setSource(byte *source) {
+ _source = source;
+ _curHeight++;
+ }
+protected:
+ byte *_source;
+ int16 _curWidth, _curHeight;
+};
+
+class SpriteFilterScaleDown : public SpriteFilter {
+public:
+ SpriteFilterScaleDown(const SpriteDrawItem &sprite, SpriteReader *reader) : SpriteFilter(sprite), _reader(reader) {
+ _height = _sprite->height;
+ _yerror = _sprite->yerror;
+ _origHeight = _sprite->origHeight;
+ _scalerStatus = 0;
+ }
+ SpriteReaderStatus readPacket(PixelPacket &packet) {
+ SpriteReaderStatus status = kSrsPixelsLeft;
+ if (_scalerStatus == 0) {
+ _xerror = _sprite->xdelta;
+ _yerror -= 100;
+ while (_yerror <= 0) {
+ do {
+ status = _reader->readPacket(packet);
+ } while (status == kSrsPixelsLeft);
+ _yerror += _sprite->ydelta - 100;
+ }
+ if (status == kSrsEndOfSprite)
+ return kSrsEndOfSprite;
+ _scalerStatus = 1;
+ }
+ if (_scalerStatus == 1) {
+ status = _reader->readPacket(packet);
+ byte updcount = packet.count;
+ while (updcount--) {
+ _xerror -= 100;
+ if (_xerror <= 0) {
+ if (packet.count > 0)
+ packet.count--;
+ _xerror += _sprite->xdelta;
+ }
+ }
+ if (status == kSrsEndOfLine) {
+ if (--_height == 0)
+ return kSrsEndOfSprite;
+ _scalerStatus = 0;
+ return kSrsEndOfLine;
+ }
+ }
+ return kSrsPixelsLeft;
+ }
+protected:
+ SpriteReader *_reader;
+ int16 _xerror, _yerror;
+ int16 _height;
+ int16 _origHeight;
+ int _scalerStatus;
+};
+
+class SpriteFilterScaleUp : public SpriteFilter {
+public:
+ SpriteFilterScaleUp(const SpriteDrawItem &sprite, SpriteReader *reader) : SpriteFilter(sprite), _reader(reader) {
+ _height = _sprite->height;
+ _yerror = _sprite->yerror;
+ _origHeight = _sprite->origHeight;
+ _scalerStatus = 0;
+ }
+ SpriteReaderStatus readPacket(PixelPacket &packet) {
+ SpriteReaderStatus status;
+ if (_scalerStatus == 0) {
+ _xerror = _sprite->xdelta;
+ _sourcep = _reader->getSource();
+ _scalerStatus = 1;
+ }
+ if (_scalerStatus == 1) {
+ status = _reader->readPacket(packet);
+ byte updcount = packet.count;
+ while (updcount--) {
+ _xerror -= 100;
+ if (_xerror <= 0) {
+ packet.count++;
+ _xerror += _sprite->xdelta;
+ }
+ }
+ if (status == kSrsEndOfLine) {
+ if (--_height == 0)
+ return kSrsEndOfSprite;
+ _yerror -= 100;
+ if (_yerror <= 0) {
+ _reader->setSource(_sourcep);
+ _yerror += _sprite->ydelta + 100;
+ }
+ _scalerStatus = 0;
+ return kSrsEndOfLine;
+ }
+ }
+ return kSrsPixelsLeft;
+ }
+protected:
+ SpriteReader *_reader;
+ byte *_sourcep;
+ int16 _xerror, _yerror;
+ int16 _height;
+ int16 _origHeight;
+ int _scalerStatus;
+};
+
+bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem &sprite) {
+ int16 scaleValueX, scaleValueY;
+ int16 xoffs, yoffs;
+ byte *spriteData;
+ int16 frameNum;
+
+ memset(&sprite, 0, sizeof(SpriteDrawItem));
+
+ if (drawRequest.flags == 0xFFFF)
+ return false;
+
+ frameNum = drawRequest.flags & 0x0FFF;
+
+ sprite.flags = 0;
+ sprite.baseColor = drawRequest.baseColor;
+ sprite.x = drawRequest.x;
+ sprite.y = drawRequest.y;
+ sprite.priority = drawRequest.y;
+ sprite.resIndex = drawRequest.resIndex;
+ sprite.frameNum = frameNum;
+
+ spriteData = _vm->_res->load(drawRequest.resIndex)->data;
+
+ if (drawRequest.flags & 0x1000) {
+ sprite.flags |= 4;
+ }
+
+ if (drawRequest.flags & 0x2000) {
+ sprite.flags |= 0x10;
+ }
+
+ if (drawRequest.flags & 0x4000) {
+ sprite.flags |= 0x40;
+ }
+
+ // First initialize the sprite item with the values from the sprite resource
+
+ SpriteFrameEntry spriteFrameEntry(spriteData + frameNum * 12);
+
+ if (spriteFrameEntry.w == 0 || spriteFrameEntry.h == 0)
+ return false;
+
+ sprite.offset = spriteFrameEntry.offset;
+
+ sprite.width = spriteFrameEntry.w;
+ sprite.height = spriteFrameEntry.h;
+ sprite.origWidth = spriteFrameEntry.w;
+ sprite.origHeight = spriteFrameEntry.h;
+
+ if (drawRequest.flags & 0x1000) {
+ xoffs = spriteFrameEntry.w - spriteFrameEntry.x;
+ } else {
+ xoffs = spriteFrameEntry.x;
+ }
+
+ yoffs = spriteFrameEntry.y;
+
+ // If the sprite should be scaled we need to initialize some values now
+
+ if (drawRequest.scaling != 0) {
+
+ byte scaleValue = ABS(drawRequest.scaling);
+
+ scaleValueX = scaleValue * sprite.origWidth;
+ sprite.xdelta = (10000 * sprite.origWidth) / scaleValueX;
+ scaleValueX /= 100;
+
+ scaleValueY = scaleValue * sprite.origHeight;
+ sprite.ydelta = (10000 * sprite.origHeight) / scaleValueY;
+ scaleValueY /= 100;
+
+ if (drawRequest.scaling > 0) {
+ sprite.flags |= 2;
+ sprite.width = sprite.origWidth + scaleValueX;
+ sprite.height = sprite.origHeight + scaleValueY;
+ xoffs += (xoffs * scaleValue) / 100;
+ yoffs += (yoffs * scaleValue) / 100;
+ } else {
+ sprite.flags |= 1;
+ sprite.width = sprite.origWidth - scaleValueX;
+ sprite.height = sprite.origHeight - 1 - scaleValueY;
+ if (sprite.width <= 0 || sprite.height <= 0)
+ return false;
+ xoffs -= (xoffs * scaleValue) / 100;
+ yoffs -= (yoffs * scaleValue) / 100;
+ }
+
+ }
+
+ sprite.x -= xoffs;
+ sprite.y -= yoffs;
+
+ sprite.yerror = sprite.ydelta;
+
+ // Now we check if the sprite needs to be clipped
+
+ // Clip Y
+ if (sprite.y - _vm->_cameraY < 0) {
+
+ int16 clipHeight = ABS(sprite.y - _vm->_cameraY);
+ int16 skipHeight = clipHeight;
+ byte *spriteFrameData;
+
+ sprite.height -= clipHeight;
+ if (sprite.height <= 0)
+ return false;
+
+ sprite.y = _vm->_cameraY;
+
+ // If the sprite is scaled
+ if (sprite.flags & 3) {
+ int16 chopHeight = sprite.ydelta;
+ if ((sprite.flags & 2) == 0) {
+ do {
+ chopHeight -= 100;
+ if (chopHeight <= 0) {
+ skipHeight++;
+ chopHeight += sprite.ydelta;
+ } else {
+ clipHeight--;
+ }
+ } while (clipHeight > 0);
+ } else {
+ do {
+ chopHeight -= 100;
+ if (chopHeight < 0) {
+ skipHeight--;
+ chopHeight += sprite.ydelta + 100;
+ }
+ clipHeight--;
+ } while (clipHeight > 0);
+ }
+ sprite.yerror = chopHeight;
+ }
+
+ spriteFrameData = spriteData + sprite.offset;
+ // Now the sprite's offset is adjusted to point to the starting line
+ if ((sprite.flags & 0x10) == 0) {
+ while (skipHeight--) {
+ int16 lineWidth = 0;
+ while (lineWidth < sprite.origWidth) {
+ sprite.offset++;
+ lineWidth += spriteFrameData[0] & 0x0F;
+ spriteFrameData++;
+ }
+ }
+ } else {
+ while (skipHeight--) {
+ int16 lineWidth = 0;
+ while (lineWidth < sprite.origWidth) {
+ sprite.offset += 2;
+ lineWidth += spriteFrameData[1];
+ spriteFrameData += 2;
+ }
+ }
+ }
+
+ }
+
+ if (sprite.y + sprite.height - _vm->_cameraY - _vm->_cameraHeight > 0)
+ sprite.height -= sprite.y + sprite.height - _vm->_cameraY - _vm->_cameraHeight;
+ if (sprite.height <= 0)
+ return false;
+
+ sprite.skipX = 0;
+
+ if (drawRequest.flags & 0x1000) {
+ // Left border
+ if (sprite.x - _vm->_cameraX < 0) {
+ sprite.width -= ABS(sprite.x - _vm->_cameraX);
+ sprite.x = _vm->_cameraX;
+ }
+ // Right border
+ if (sprite.x + sprite.width - _vm->_cameraX - 640 > 0) {
+ sprite.flags |= 8;
+ sprite.skipX = sprite.x + sprite.width - _vm->_cameraX - 640;
+ sprite.width -= sprite.skipX;
+ }
+ } else {
+ // Left border
+ if (sprite.x - _vm->_cameraX < 0) {
+ sprite.flags |= 8;
+ sprite.skipX = ABS(sprite.x - _vm->_cameraX);
+ sprite.width -= sprite.skipX;
+ sprite.x = _vm->_cameraX;
+ }
+ // Right border
+ if (sprite.x + sprite.width - _vm->_cameraX - 640 > 0) {
+ sprite.flags |= 8;
+ sprite.width -= sprite.x + sprite.width - _vm->_cameraX - 640;
+ }
+ }
+
+ if (sprite.width <= 0)
+ return false;
+
+ return true;
+}
+
+void Screen::addDrawRequest(const DrawRequest &drawRequest) {
+ SpriteDrawItem sprite;
+ if (createSpriteDrawItem(drawRequest, sprite))
+ _renderQueue->addSprite(sprite);
+}
+
+void Screen::drawSprite(const SpriteDrawItem &sprite) {
+
+ debug(0, "Screen::drawSprite() x = %d; y = %d; flags = %04X; resIndex = %d; offset = %08X; drawX = %d; drawY = %d",
+ sprite.x, sprite.y, sprite.flags, sprite.resIndex, sprite.offset,
+ sprite.x - _vm->_cameraX, sprite.y - _vm->_cameraY);
+ debug(0, "Screen::drawSprite() width = %d; height = %d; origWidth = %d; origHeight = %d",
+ sprite.width, sprite.height, sprite.origWidth, sprite.origHeight);
+
+ byte *source = _vm->_res->load(sprite.resIndex)->data + sprite.offset;
+ byte *dest = _frontScreen + sprite.x + sprite.y * 640;
+
+ SpriteReader spriteReader(source, sprite);
+
+ if (sprite.flags & 0x40) {
+ // Shadow sprites
+ if (sprite.flags & 1) {
+ SpriteFilterScaleDown spriteScaler(sprite, &spriteReader);
+ drawSpriteCore(dest, spriteScaler, sprite);
+ } else if (sprite.flags & 2) {
+ SpriteFilterScaleUp spriteScaler(sprite, &spriteReader);
+ drawSpriteCore(dest, spriteScaler, sprite);
+ } else {
+ drawSpriteCore(dest, spriteReader, sprite);
+ }
+ } else if (sprite.flags & 0x10) {
+ // 256 color sprite
+ drawSpriteCore(dest, spriteReader, sprite);
+ } else {
+ // 16 color sprite
+ if (sprite.flags & 1) {
+ SpriteFilterScaleDown spriteScaler(sprite, &spriteReader);
+ drawSpriteCore(dest, spriteScaler, sprite);
+ } else if (sprite.flags & 2) {
+ SpriteFilterScaleUp spriteScaler(sprite, &spriteReader);
+ drawSpriteCore(dest, spriteScaler, sprite);
+ } else {
+ drawSpriteCore(dest, spriteReader, sprite);
+ }
+ }
+
+ debug(0, "Screen::drawSprite() ok");
+
+}
+
+void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawItem &sprite) {
+
+ int16 destInc;
+
+ if (sprite.flags & 4) {
+ destInc = -1;
+ dest += sprite.width;
+ } else {
+ destInc = 1;
+ }
+
+ SpriteReaderStatus status;
+ PixelPacket packet;
+
+ byte *destp = dest;
+ int16 skipX = sprite.skipX;
+
+ int16 w = sprite.width;
+ int16 h = sprite.height;
+
+ do {
+ status = reader.readPacket(packet);
+
+ if (skipX > 0) {
+ while (skipX > 0) {
+ skipX -= packet.count;
+ if (skipX < 0) {
+ packet.count = ABS(skipX);
+ break;
+ }
+ status = reader.readPacket(packet);
+ }
+ }
+
+ if (w - packet.count < 0)
+ packet.count = w;
+
+ w -= packet.count;
+
+ if (((sprite.flags & 0x40) && (packet.pixel != 0)) ||
+ ((sprite.flags & 0x10) && (packet.pixel != 0xFF)) ||
+ (!(sprite.flags & 0x10) && (packet.pixel != 0)))
+ {
+ if (sprite.flags & 0x40) {
+ while (packet.count--) {
+ *dest = _vm->_palette->getColorTransPixel(*dest);
+ dest += destInc;
+ }
+ } else {
+ if (sprite.flags & 0x10) {
+ packet.pixel = ((packet.pixel << 4) & 0xF0) | ((packet.pixel >> 4) & 0x0F);
+ } else {
+ packet.pixel += sprite.baseColor - 1;
+ }
+ while (packet.count--) {
+ *dest = packet.pixel;
+ dest += destInc;
+ }
+ }
+ } else {
+ dest += packet.count * destInc;
+ }
+
+ if (status == kSrsEndOfLine || w <= 0) {
+ if (w <= 0) {
+ while (status == kSrsPixelsLeft) {
+ status = reader.readPacket(packet);
+ }
+ }
+ dest = destp + 640;
+ destp = dest;
+ skipX = sprite.skipX;
+ w = sprite.width;
+ h--;
+ }
+
+ } while (status != kSrsEndOfSprite && h > 0);
+
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp
new file mode 100644
index 0000000000..046578cffd
--- /dev/null
+++ b/engines/toltecs/toltecs.cpp
@@ -0,0 +1,654 @@
+/* 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/config-manager.h"
+#include "common/events.h"
+#include "common/random.h"
+#include "common/str.h"
+#include "common/error.h"
+#include "common/textconsole.h"
+
+#include "base/plugins.h"
+#include "base/version.h"
+
+#include "graphics/cursorman.h"
+
+#include "engines/util.h"
+
+#include "audio/mixer.h"
+
+#include "toltecs/toltecs.h"
+#include "toltecs/animation.h"
+#include "toltecs/menu.h"
+#include "toltecs/movie.h"
+#include "toltecs/music.h"
+#include "toltecs/palette.h"
+#include "toltecs/render.h"
+#include "toltecs/resource.h"
+#include "toltecs/script.h"
+#include "toltecs/screen.h"
+#include "toltecs/segmap.h"
+#include "toltecs/sound.h"
+#include "toltecs/microtiles.h"
+
+namespace Toltecs {
+
+struct GameSettings {
+ const char *gameid;
+ const char *description;
+ byte id;
+ uint32 features;
+ const char *detectname;
+};
+
+ToltecsEngine::ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
+
+ // Setup mixer
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+
+ _rnd = new Common::RandomSource("toltecs");
+}
+
+ToltecsEngine::~ToltecsEngine() {
+ delete _rnd;
+}
+
+void ToltecsEngine::syncSoundSettings() {
+ /*
+ _music->setVolume(ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ */
+}
+
+Common::Error ToltecsEngine::run() {
+ initGraphics(640, 400, true);
+
+ _isSaveAllowed = true;
+
+ _counter01 = 0;
+ _counter02 = 0;
+ _movieSceneFlag = false;
+ _flag01 = 0;
+
+ _saveLoadRequested = 0;
+
+ _cameraX = 0;
+ _cameraY = 0;
+ _newCameraX = 0;
+ _newCameraY = 0;
+ _cameraHeight = 0;
+
+ _guiHeight = 26;
+
+ _sceneWidth = 0;
+ _sceneHeight = 0;
+
+ _doSpeech = true;
+ _doText = true;
+
+ _walkSpeedY = 5;
+ _walkSpeedX = 1;
+
+ _mouseX = 0;
+ _mouseY = 0;
+ _mouseDblClickTicks = 60;
+ _mouseWaitForRelease = false;
+ _mouseButton = 0;
+ _mouseDisabled = 0;
+ _leftButtonDown = false;
+ _rightButtonDown = false;
+
+ _arc = new ArchiveReader();
+ _arc->openArchive("WESTERN");
+
+ _res = new ResourceCache(this);
+
+ _screen = new Screen(this);
+
+ _script = new ScriptInterpreter(this);
+ _anim = new AnimationPlayer(this);
+ _palette = new Palette(this);
+ _segmap = new SegmentMap(this);
+ _moviePlayer = new MoviePlayer(this);
+ _musicPlayer = new MusicPlayer();
+ _menuSystem = new MenuSystem(this);
+
+ _sound = new Sound(this);
+
+ syncSoundSettings();
+
+ CursorMan.showMouse(true);
+
+ setupSysStrings();
+
+//#define TEST_MENU
+#ifdef TEST_MENU
+ _screen->registerFont(0, 0x0D);
+ _screen->registerFont(1, 0x0E);
+ _screen->loadMouseCursor(12);
+ _palette->loadAddPalette(9, 224);
+ _palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224);
+ _screen->finishTalkTextItems();
+ _screen->clearSprites();
+ _menuSystem->run();
+ /*
+ while (1) {
+ //updateInput();
+ _menuSystem->update();
+ updateScreen();
+ }
+ */
+ return Common::kNoError;
+#endif
+
+ // Start main game loop
+ _script->loadScript(0, 0);
+ _script->setMainScript(0);
+ if (ConfMan.hasKey("save_slot")) {
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot >= 0 && saveSlot <= 99) {
+ _screen->loadMouseCursor(12);
+ loadGameState(saveSlot);
+ }
+ }
+ _script->runScript();
+
+ _musicPlayer->stopAndClear();
+ _sound->stopAll();
+
+ delete _arc;
+ delete _res;
+ delete _screen;
+ delete _script;
+ delete _anim;
+ delete _palette;
+ delete _segmap;
+ delete _musicPlayer;
+ delete _moviePlayer;
+ delete _menuSystem;
+
+ delete _sound;
+
+ return Common::kNoError;
+}
+
+void ToltecsEngine::setupSysStrings() {
+ Resource *sysStringsResource = _res->load(15);
+ const char *sysStrings = (const char*)sysStringsResource->data;
+ for (int i = 0; i < kSysStrCount; i++) {
+ debug(1, "sysStrings[%d] = [%s]", i, sysStrings);
+ _sysStrings[i] = sysStrings;
+ sysStrings += strlen(sysStrings) + 1;
+ }
+ // TODO: Set yes/no chars
+}
+
+void ToltecsEngine::requestSavegame(int slotNum, Common::String &description) {
+ _saveLoadRequested = 2;
+ _saveLoadSlot = slotNum;
+ _saveLoadDescription = description;
+}
+
+void ToltecsEngine::requestLoadgame(int slotNum) {
+ _saveLoadRequested = 1;
+ _saveLoadSlot = slotNum;
+}
+
+void ToltecsEngine::loadScene(uint resIndex) {
+
+ Resource *sceneResource = _res->load(resIndex);
+ byte *scene = sceneResource->data;
+
+ uint32 imageSize = READ_LE_UINT32(scene);
+ _sceneResIndex = resIndex;
+ _sceneHeight = READ_LE_UINT16(scene + 4);
+ _sceneWidth = READ_LE_UINT16(scene + 6);
+
+ // Load scene palette
+ _palette->loadAddPaletteFrom(scene + 8, 0, 128);
+
+ // Load scene background
+ byte *source = scene + 392;
+ byte *destp = _screen->_backScreen;
+ byte *destEnd = destp + _sceneWidth * _sceneHeight;
+ while (destp < destEnd) {
+ int count = 1;
+ byte pixel = *source++;
+ if (pixel & 0x80) {
+ pixel &= 0x7F;
+ count = *source++;
+ count += 2;
+ }
+ memset(destp, pixel, count);
+ destp += count;
+ }
+
+ debug(0, "_sceneWidth = %d; _sceneHeight = %d", _sceneWidth, _sceneHeight);
+
+ // Load scene segmap
+ _segmap->load(scene + imageSize + 4);
+
+ _screen->_fullRefresh = true;
+ _screen->_renderQueue->clear();
+
+}
+
+void ToltecsEngine::updateScreen() {
+
+ _sound->updateSpeech();
+
+ _screen->updateShakeScreen();
+
+ // TODO: Set quit flag
+ if (shouldQuit())
+ return;
+
+ if (!_movieSceneFlag)
+ updateInput();
+ else
+ _mouseButton = 0;
+
+ // TODO? Check keyb
+
+ _counter01--;
+ if (_counter01 <= 0) {
+ _counter01 = MIN(_counter02, 30);
+ _counter02 = 0;
+ drawScreen();
+ _flag01 = 1;
+ _counter02 = 1;
+ } else {
+ _screen->clearSprites();
+ _flag01 = 0;
+ }
+
+ static uint32 prevUpdateTime = 0;
+ uint32 currUpdateTime;
+ do {
+ currUpdateTime = _system->getMillis();
+ _counter02 = (currUpdateTime - prevUpdateTime) / 13;
+ } while (_counter02 == 0);
+ prevUpdateTime = currUpdateTime;
+
+}
+
+void ToltecsEngine::drawScreen() {
+
+ // FIXME: Quick hack, sometimes cameraY was negative (the code in updateCamera was at fault)
+ if (_cameraY < 0) _cameraY = 0;
+
+ _segmap->addMasksToRenderQueue();
+ _screen->addTalkTextItemsToRenderQueue();
+
+ _screen->_renderQueue->update();
+
+ //debug("_guiHeight = %d\n", _guiHeight);
+
+ if (_screen->_guiRefresh && _guiHeight > 0 && _cameraHeight > 0) {
+ // Update the GUI when needed and it's visible
+ _system->copyRectToScreen((const byte *)_screen->_frontScreen + _cameraHeight * 640,
+ 640, 0, _cameraHeight, 640, _guiHeight);
+ _screen->_guiRefresh = false;
+ }
+
+ _system->updateScreen();
+
+ updateCamera();
+
+}
+
+void ToltecsEngine::updateInput() {
+
+ Common::Event event;
+ Common::EventManager *eventMan = _system->getEventManager();
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ _keyState = event.kbd;
+
+ //debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode);
+
+ // FIXME: This is just for debugging
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_F7:
+ savegame("toltecs.001", "Quicksave");
+ break;
+ case Common::KEYCODE_F9:
+ loadgame("toltecs.001");
+ break;
+ case Common::KEYCODE_ESCAPE:
+ // Skip current dialog line, if a dialog is active
+ if (_screen->getTalkTextDuration() > 0) {
+ _sound->stopSpeech();
+ _screen->finishTalkTextItems();
+ _keyState.reset(); // event consumed
+ }
+ break;
+ default:
+ break;
+ }
+
+ break;
+ case Common::EVENT_KEYUP:
+ _keyState.reset();
+ break;
+ case Common::EVENT_QUIT:
+ quitGame();
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _leftButtonDown = true;
+ break;
+ case Common::EVENT_LBUTTONUP:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _leftButtonDown = false;
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _rightButtonDown = true;
+ break;
+ case Common::EVENT_RBUTTONUP:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _rightButtonDown = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!_mouseDisabled) {
+
+ if (_mouseDblClickTicks > 0)
+ _mouseDblClickTicks--;
+
+ byte mouseButtons = 0;
+ if (_leftButtonDown)
+ mouseButtons |= 1;
+ if (_rightButtonDown)
+ mouseButtons |= 2;
+
+ if (mouseButtons != 0) {
+ if (!_mouseWaitForRelease) {
+ _mouseButton = mouseButtons;
+ if (_mouseDblClickTicks > 0)
+ _mouseButton = 0x80;
+ //if (_mouseButton == 0x80) debug("DBL!");
+ _mouseDblClickTicks = 30; // maybe TODO
+ _mouseWaitForRelease = true;
+ } else {
+ _mouseButton = 0;
+ }
+ } else {
+ _mouseWaitForRelease = false;
+ _mouseButton = 0;
+ }
+
+ }
+
+}
+
+void ToltecsEngine::setGuiHeight(int16 guiHeight) {
+ if (guiHeight != _guiHeight) {
+ _guiHeight = guiHeight;
+ _cameraHeight = 400 - _guiHeight;
+ _screen->_guiRefresh = true;
+ debug(0, "ToltecsEngine::setGuiHeight() _guiHeight = %d; _cameraHeight = %d", _guiHeight, _cameraHeight);
+ // TODO: clearScreen();
+ }
+}
+
+void ToltecsEngine::setCamera(int16 x, int16 y) {
+
+ _screen->finishTalkTextItems();
+
+ _screen->clearSprites();
+
+ _cameraX = x;
+ _newCameraX = x;
+
+ _cameraY = y;
+ _newCameraY = y;
+
+}
+
+bool ToltecsEngine::getCameraChanged() {
+ return _cameraX != _newCameraX || _cameraY != _newCameraY;
+}
+
+void ToltecsEngine::scrollCameraUp(int16 delta) {
+ if (_newCameraY > 0) {
+ if (_newCameraY < delta)
+ _newCameraY = 0;
+ else
+ _newCameraY -= delta;
+ }
+}
+
+void ToltecsEngine::scrollCameraDown(int16 delta) {
+ debug(0, "ToltecsEngine::scrollCameraDown(%d)", delta);
+ if (_newCameraY != _sceneHeight - _cameraHeight) {
+ if (_sceneHeight - _cameraHeight < _newCameraY + delta)
+ delta += (_sceneHeight - _cameraHeight) - (delta + _newCameraY);
+ _newCameraY += delta;
+ debug(0, "ToltecsEngine::scrollCameraDown() _newCameraY = %d; delta = %d", _newCameraY, delta);
+ }
+}
+
+void ToltecsEngine::scrollCameraLeft(int16 delta) {
+ if (_newCameraX > 0) {
+ if (_newCameraX < delta)
+ _newCameraX = 0;
+ else
+ _newCameraX -= delta;
+ }
+}
+
+void ToltecsEngine::scrollCameraRight(int16 delta) {
+ debug(0, "ToltecsEngine::scrollCameraRight(%d)", delta);
+ if (_newCameraX != _sceneWidth - 640) {
+ if (_sceneWidth - 640 < delta + _newCameraX)
+ delta += (_sceneWidth - 640) - (delta + _newCameraX);
+ _newCameraX += delta;
+ debug(0, "ToltecsEngine::scrollCameraRight() _newCameraX = %d; delta = %d", _newCameraY, delta);
+ }
+}
+
+void ToltecsEngine::updateCamera() {
+
+ if (_cameraX != _newCameraX) {
+ _cameraX = _newCameraX;
+ _screen->_fullRefresh = true;
+ _screen->finishTalkTextItems();
+ }
+
+ if (_cameraY != _newCameraY) {
+ _cameraY = _newCameraY;
+ _screen->_fullRefresh = true;
+ _screen->finishTalkTextItems();
+ }
+
+ //debug(0, "ToltecsEngine::updateCamera() _cameraX = %d; _cameraY = %d", _cameraX, _cameraY);
+
+}
+
+void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) {
+
+ byte *scanData = _script->getSlotData(slotIndex) + slotOffset;
+
+ while (*scanData < 0xF0) {
+ if (*scanData == 0x19) {
+ scanData++;
+ } else if (*scanData == 0x14) {
+ scanData++;
+ } else if (*scanData == 0x0A) {
+ scanData += 4;
+ } else if (*scanData < 0x0A) {
+ scanData++;
+ }
+ scanData++;
+ }
+
+ if (*scanData == 0xFE) {
+ if (_doSpeech) {
+ int16 resIndex = READ_LE_UINT16(scanData + 1);
+ debug(0, "ToltecsEngine::talk() playSound(resIndex: %d)", resIndex);
+ _sound->playSpeech(resIndex);
+ }
+ if (_doText) {
+ _screen->updateTalkText(slotIndex, slotOffset);
+ } else {
+ _screen->keepTalkTextItemsAlive();
+ }
+ } else {
+ _screen->updateTalkText(slotIndex, slotOffset);
+ }
+
+}
+
+void ToltecsEngine::walk(byte *walkData) {
+
+ int16 xdelta, ydelta, v8, v10, v11;
+ int16 xstep, ystep;
+ ScriptWalk walkInfo;
+
+ walkInfo.y = READ_LE_UINT16(walkData + 0);
+ walkInfo.x = READ_LE_UINT16(walkData + 2);
+ walkInfo.y1 = READ_LE_UINT16(walkData + 4);
+ walkInfo.x1 = READ_LE_UINT16(walkData + 6);
+ walkInfo.y2 = READ_LE_UINT16(walkData + 8);
+ walkInfo.x2 = READ_LE_UINT16(walkData + 10);
+ walkInfo.yerror = READ_LE_UINT16(walkData + 12);
+ walkInfo.xerror = READ_LE_UINT16(walkData + 14);
+ walkInfo.mulValue = READ_LE_UINT16(walkData + 16);
+ walkInfo.scaling = READ_LE_UINT16(walkData + 18);
+
+ walkInfo.scaling = -_segmap->getScalingAtPoint(walkInfo.x, walkInfo.y);
+
+ if (walkInfo.y1 < walkInfo.y2)
+ ystep = -1;
+ else
+ ystep = 1;
+ ydelta = ABS(walkInfo.y1 - walkInfo.y2) * _walkSpeedY;
+
+ if (walkInfo.x1 < walkInfo.x2)
+ xstep = -1;
+ else
+ xstep = 1;
+ xdelta = ABS(walkInfo.x1 - walkInfo.x2) * _walkSpeedX;
+
+ debug(0, "ToltecsEngine::walk() xdelta = %d; ydelta = %d", xdelta, ydelta);
+
+ if (xdelta > ydelta)
+ SWAP(xdelta, ydelta);
+
+ v8 = 100 * xdelta;
+ if (v8 != 0) {
+ if (walkInfo.scaling > 0)
+ v8 -= v8 * ABS(walkInfo.scaling) / 100;
+ else
+ v8 += v8 * ABS(walkInfo.scaling) / 100;
+ if (ydelta != 0)
+ v8 /= ydelta;
+ }
+
+ if (ydelta > ABS(walkInfo.x1 - walkInfo.x2) * _walkSpeedX) {
+ v10 = 100 - walkInfo.scaling;
+ v11 = v8;
+ } else {
+ v10 = v8;
+ v11 = 100 - walkInfo.scaling;
+ }
+
+ walkInfo.yerror += walkInfo.mulValue * v10;
+ while (walkInfo.yerror >= 100 * _walkSpeedY) {
+ walkInfo.yerror -= 100 * _walkSpeedY;
+ if (walkInfo.y == walkInfo.y1) {
+ walkInfo.x = walkInfo.x1;
+ break;
+ }
+ walkInfo.y += ystep;
+ }
+
+ walkInfo.xerror += walkInfo.mulValue * v11;
+ while (walkInfo.xerror >= 100 * _walkSpeedX) {
+ walkInfo.xerror -= 100 * _walkSpeedX;
+ if (walkInfo.x == walkInfo.x1) {
+ walkInfo.y = walkInfo.y1;
+ break;
+ }
+ walkInfo.x += xstep;
+ }
+
+ WRITE_LE_UINT16(walkData + 0, walkInfo.y);
+ WRITE_LE_UINT16(walkData + 2, walkInfo.x);
+ WRITE_LE_UINT16(walkData + 4, walkInfo.y1);
+ WRITE_LE_UINT16(walkData + 6, walkInfo.x1);
+ WRITE_LE_UINT16(walkData + 8, walkInfo.y2);
+ WRITE_LE_UINT16(walkData + 10, walkInfo.x2);
+ WRITE_LE_UINT16(walkData + 12, walkInfo.yerror);
+ WRITE_LE_UINT16(walkData + 14, walkInfo.xerror);
+ WRITE_LE_UINT16(walkData + 16, walkInfo.mulValue);
+ WRITE_LE_UINT16(walkData + 18, walkInfo.scaling);
+
+}
+
+int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
+ byte *rectDataEnd) {
+
+ rectData += index * itemSize;
+
+ while (rectData < rectDataEnd) {
+ int16 rectY = READ_LE_UINT16(rectData);
+ if (rectY == -10)
+ break;
+ int16 rectX = READ_LE_UINT16(rectData + 2);
+ int16 rectH = READ_LE_UINT16(rectData + 4);
+ int16 rectW = READ_LE_UINT16(rectData + 6);
+
+ debug(0, "x = %d; y = %d; x1 = %d; y2 = %d; w = %d; h = %d",
+ x, y, rectX, rectY, rectW, rectH);
+
+ if (x >= rectX && x <= rectX + rectW && y >= rectY && y <= rectY + rectH) {
+ return index;
+ }
+ index++;
+ rectData += itemSize;
+ }
+
+ return -1;
+
+}
+
+} // End of namespace Toltecs
diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h
new file mode 100644
index 0000000000..279016cce8
--- /dev/null
+++ b/engines/toltecs/toltecs.h
@@ -0,0 +1,213 @@
+/* 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 TOLTECS_H
+#define TOLTECS_H
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/keyboard.h"
+#include "common/random.h"
+#include "common/textconsole.h"
+
+#include "engines/engine.h"
+
+#include "graphics/surface.h"
+
+namespace Toltecs {
+
+struct ToltecsGameDescription;
+
+class AnimationPlayer;
+class ArchiveReader;
+class Input;
+class MenuSystem;
+class MoviePlayer;
+class MusicPlayer;
+class Palette;
+class ResourceCache;
+class ScriptInterpreter;
+class Screen;
+class SegmentMap;
+class Sound;
+
+enum SysString {
+ kStrLoadingPleaseWait,
+ kStrWhatCanIDoForYou,
+ kStrLoad,
+ kStrSave,
+ kStrTextOn,
+ kStrTextOff,
+ kStrVoicesOn,
+ kStrVoicesOff,
+ kStrVolume,
+ kStrPlay,
+ kStrQuit,
+ kStrLoadGame,
+ kStrSaveGame,
+ kStrAdjustVolume,
+ kStrMaster,
+ kStrVoices,
+ kStrMusic,
+ kStrSoundFx,
+ kStrBackground,
+ kStrCancel,
+ kStrDone,
+ kStrAreYouSure,
+ kStrYes,
+ kStrNo,
+ kSysStrCount
+};
+
+class ToltecsEngine : public ::Engine {
+ Common::KeyState _keyPressed;
+
+protected:
+ Common::Error run();
+// void shutdown();
+
+public:
+ ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc);
+ virtual ~ToltecsEngine();
+
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual void syncSoundSettings();
+
+ Common::RandomSource *_rnd;
+ const ToltecsGameDescription *_gameDescription;
+ uint32 getFeatures() const;
+ Common::Language getLanguage() const;
+ const Common::String& getTargetName() const { return _targetName; }
+
+ void setupSysStrings();
+ void requestSavegame(int slotNum, Common::String &description);
+ void requestLoadgame(int slotNum);
+
+ void loadScene(uint resIndex);
+
+ void updateScreen();
+ void drawScreen();
+ void updateInput();
+
+ void setGuiHeight(int16 guiHeight);
+
+ void setCamera(int16 x, int16 y);
+ bool getCameraChanged();
+ void scrollCameraUp(int16 delta);
+ void scrollCameraDown(int16 delta);
+ void scrollCameraLeft(int16 delta);
+ void scrollCameraRight(int16 delta);
+ void updateCamera();
+
+ void talk(int16 slotIndex, int16 slotOffset);
+
+ void walk(byte *walkData);
+
+ int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
+ byte *rectDataEnd);
+
+public:
+
+ AnimationPlayer *_anim;
+ ArchiveReader *_arc;
+ Input *_input;
+ MenuSystem *_menuSystem;
+ MoviePlayer *_moviePlayer;
+ MusicPlayer *_musicPlayer;
+ Palette *_palette;
+ ResourceCache *_res;
+ ScriptInterpreter *_script;
+ Screen *_screen;
+ SegmentMap *_segmap;
+ Sound *_sound;
+
+ Common::String _sysStrings[kSysStrCount];
+
+ int _saveLoadRequested;
+ int _saveLoadSlot;
+ Common::String _saveLoadDescription;
+
+ uint _sceneResIndex;
+ int16 _sceneWidth, _sceneHeight;
+
+ int _counter01, _counter02;
+ bool _movieSceneFlag;
+ byte _flag01;
+
+ int16 _cameraX, _cameraY;
+ int16 _newCameraX, _newCameraY;
+ int16 _cameraHeight;
+ int16 _guiHeight;
+
+ bool _doSpeech, _doText;
+
+ int16 _walkSpeedY, _walkSpeedX;
+
+ Common::KeyState _keyState;
+ int16 _mouseX, _mouseY;
+ int16 _mouseDblClickTicks;
+ bool _mouseWaitForRelease;
+ byte _mouseButton;
+ int16 _mouseDisabled;
+ bool _leftButtonDown, _rightButtonDown;
+
+ const char *getSysString(int index) const { return _sysStrings[index].c_str(); }
+
+ /* Save/load */
+
+ enum kReadSaveHeaderError {
+ kRSHENoError = 0,
+ kRSHEInvalidType = 1,
+ kRSHEInvalidVersion = 2,
+ kRSHEIoError = 3
+ };
+
+ struct SaveHeader {
+ Common::String description;
+ uint32 version;
+ byte gameID;
+ uint32 flags;
+ Graphics::Surface *thumbnail;
+ };
+
+ bool _isSaveAllowed;
+
+ bool canLoadGameStateCurrently() { return _isSaveAllowed; }
+ bool canSaveGameStateCurrently() { return _isSaveAllowed; }
+ Common::Error loadGameState(int slot);
+ Common::Error saveGameState(int slot, const Common::String &description);
+ void savegame(const char *filename, const char *description);
+ void loadgame(const char *filename);
+
+ const char *getSavegameFilename(int num);
+ static Common::String getSavegameFilename(const Common::String &target, int num);
+
+ static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header);
+
+};
+
+} // End of namespace Toltecs
+
+#endif /* TOLTECS_H */