aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorTobia Tesan2016-02-29 15:39:42 +0100
committerTobia Tesan2016-03-01 20:40:46 +0100
commita120aa855990f045d38d3b603306490305a48a39 (patch)
treec29f829e36ea762de60f683b23dbefd725717d16 /engines
parentd5d25b0e89faebe4b1c5961d7b1ab872339e4a03 (diff)
downloadscummvm-rg350-a120aa855990f045d38d3b603306490305a48a39.tar.gz
scummvm-rg350-a120aa855990f045d38d3b603306490305a48a39.tar.bz2
scummvm-rg350-a120aa855990f045d38d3b603306490305a48a39.zip
WINTERMUTE: Add Watch functionality
Diffstat (limited to 'engines')
-rw-r--r--engines/wintermute/base/scriptables/debuggable/debuggable_script.cpp42
-rw-r--r--engines/wintermute/base/scriptables/debuggable/debuggable_script.h5
-rw-r--r--engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.cpp1
-rw-r--r--engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h62
-rw-r--r--engines/wintermute/debugger.cpp70
-rw-r--r--engines/wintermute/debugger.h22
-rw-r--r--engines/wintermute/debugger/debugger_controller.cpp60
-rw-r--r--engines/wintermute/debugger/debugger_controller.h13
-rw-r--r--engines/wintermute/debugger/script_monitor.h3
-rw-r--r--engines/wintermute/debugger/watch.cpp42
-rw-r--r--engines/wintermute/debugger/watch.h51
-rw-r--r--engines/wintermute/debugger/watch_instance.cpp53
-rw-r--r--engines/wintermute/debugger/watch_instance.h44
-rw-r--r--engines/wintermute/module.mk2
14 files changed, 463 insertions, 7 deletions
diff --git a/engines/wintermute/base/scriptables/debuggable/debuggable_script.cpp b/engines/wintermute/base/scriptables/debuggable/debuggable_script.cpp
index 1aa318bdbf..73ecd3fc89 100644
--- a/engines/wintermute/base/scriptables/debuggable/debuggable_script.cpp
+++ b/engines/wintermute/base/scriptables/debuggable/debuggable_script.cpp
@@ -22,18 +22,28 @@
#include "common/tokenizer.h"
#include "debuggable_script.h"
-#include "engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h"
#include "engines/wintermute/base/scriptables/script_stack.h"
#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h"
#include "engines/wintermute/debugger/breakpoint.h"
#include "engines/wintermute/debugger/script_monitor.h"
+#include "engines/wintermute/debugger/watch_instance.h"
namespace Wintermute {
-DebuggableScript::DebuggableScript(BaseGame *inGame, DebuggableScEngine *engine) : ScScript(inGame, engine), _engine(engine), _stepDepth(kDefaultStepDepth) {}
-
-DebuggableScript::~DebuggableScript() {}
+DebuggableScript::DebuggableScript(BaseGame *inGame, DebuggableScEngine *engine) : ScScript(inGame, engine), _engine(engine), _stepDepth(kDefaultStepDepth) {
+ _engine->_watches.subscribe(this);
+ for (uint i = 0; i < _engine->_watches.size(); i++) {
+ _watchInstances.push_back(new WatchInstance(_engine->_watches[i], this));
+ }
+}
+DebuggableScript::~DebuggableScript() {
+ for (uint i = 0; i < _watchInstances.size(); i++) {
+ delete _watchInstances[i];
+ }
+ _engine->_watches.unsubscribe(this);
+}
void DebuggableScript::preInstHook(uint32 inst) {}
void DebuggableScript::postInstHook(uint32 inst) {
@@ -46,6 +56,11 @@ void DebuggableScript::postInstHook(uint32 inst) {
_engine->_monitor->notifyStep(this);
}
}
+
+ for (uint i = 0; i < _watchInstances.size(); i++) {
+ this->_watchInstances[i]->evaluate();
+ }
+
}
void DebuggableScript::setStepDepth(int depth) {
@@ -110,5 +125,24 @@ Common::String DebuggableScript::dbgGetFilename() const {
return _filename;
}
+void DebuggableScript::updateWatches() {
+ // We drop obsolete watches
+ for (uint i = 0; i < _watchInstances.size(); i++) {
+ Watch *findMe = _watchInstances[i]->_watch;
+ if (Common::find(_engine->_watches.begin(), _engine->_watches.end(), findMe) == _engine->_watches.end()) {
+ // Not found on engine-wide list, must have been removed from watches. Must remove it from local list.
+ _watchInstances.remove_at(i);
+ }
+ }
+
+ // We add any new watches
+ for (uint i = 0; i < _engine->_watches.size(); i++) {
+ Watch *findMe = _engine->_watches[i];
+ if (Common::find(_engine->_watches.begin(), _engine->_watches.end(), findMe) == _engine->_watches.end()) {
+ // Not found on local list, must be a new one.
+ _watchInstances.push_back(new WatchInstance(_engine->_watches[i], this));
+ }
+ }
+}
} // End of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/debuggable/debuggable_script.h b/engines/wintermute/base/scriptables/debuggable/debuggable_script.h
index c5c0896f11..b32a5ca4af 100644
--- a/engines/wintermute/base/scriptables/debuggable/debuggable_script.h
+++ b/engines/wintermute/base/scriptables/debuggable/debuggable_script.h
@@ -22,17 +22,19 @@
#ifndef DEBUGGABLE_SCRIPT_H_
#define DEBUGGABLE_SCRIPT_H_
-
#include "engines/wintermute/base/scriptables/script.h"
namespace Wintermute {
class ScriptMonitor;
+class Watch;
+class WatchInstance;
class DebuggableScEngine;
class DebuggableScript : public ScScript {
static const int kDefaultStepDepth = -2;
int32 _stepDepth;
DebuggableScEngine *_engine;
+ BaseArray<WatchInstance *> _watchInstances;
virtual void preInstHook(uint32 inst) override;
virtual void postInstHook(uint32 inst) override;
void setStepDepth(int depth);
@@ -57,6 +59,7 @@ public:
* Continue execution until the activation record on top of the stack is popped
*/
void stepFinish();
+ void updateWatches();
};
} // End of namespace Wintermute
diff --git a/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.cpp b/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.cpp
index f1f1bf776e..28a00cd4ae 100644
--- a/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.cpp
+++ b/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.cpp
@@ -22,6 +22,7 @@
#include "debuggable_script_engine.h"
#include "debuggable_script.h"
+#include "engines/wintermute/debugger/watch_instance.h"
namespace Wintermute {
diff --git a/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h b/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h
index 8469ecdc25..a4d9d2bfe7 100644
--- a/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h
+++ b/engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h
@@ -22,21 +22,78 @@
#ifndef DEBUGGABLE_SCRIPT_ENGINE_H_
#define DEBUGGABLE_SCRIPT_ENGINE_H_
-
-#include "engines/wintermute/base/scriptables/debuggable/debuggable_script.h"
#include "engines/wintermute/base/scriptables/script_engine.h"
#include "engines/wintermute/coll_templ.h"
#include "common/algorithm.h"
+#include "engines/wintermute/base/scriptables/debuggable/debuggable_script.h"
namespace Wintermute {
class Breakpoint;
+class Watch;
class DebuggableScript;
class DebuggableScEngine;
class ScriptMonitor;
+class PublisherWArray : private Common::Array<Watch *> {
+ Common::Array<DebuggableScript *> _subscribers;
+ void notifySubscribers() {
+ for (uint i = 0; i < _subscribers.size(); i++) {
+ _subscribers[i]->updateWatches();
+ }
+ }
+public:
+ void subscribe(DebuggableScript *script) {
+ if (Common::find(_subscribers.begin(), _subscribers.end(), script) == _subscribers.end()) {
+ // If not already contained
+ _subscribers.push_back(script);
+ }
+ }
+
+ void unsubscribe(DebuggableScript *script) {
+ int location = -1;
+ for (uint i = 0; i < _subscribers.size() && location == -1; i++) {
+ if (_subscribers[i] == script) {
+ location = i;
+ }
+ }
+ if (location >= 0) {
+ _subscribers.remove_at(location);
+ } else {
+ // TODO: If this happens... it's funny. Some script out there forgot to subscribe.
+ }
+ }
+
+ void push_back(Watch *newElement) {
+ Common::Array<Watch *>::push_back(newElement);
+ notifySubscribers();
+ }
+
+ size_type size() {
+ return Common::Array<Watch *>::size();
+ }
+
+ iterator begin() {
+ return Common::Array<Watch *>::begin();
+ }
+
+ iterator end() {
+ return Common::Array<Watch *>::end();
+ }
+
+ Watch *&operator[](size_type idx) {
+ return Common::Array<Watch *>::operator[](idx);
+ }
+ Watch *remove_at(size_type idx) {
+ Watch *res = Common::Array<Watch *>::remove_at(idx);
+ notifySubscribers();
+ return res;
+ }
+};
+
class DebuggableScEngine : public ScEngine {
Common::Array<Breakpoint *> _breakpoints;
+ PublisherWArray _watches;
ScriptMonitor *_monitor;
public:
DebuggableScEngine(BaseGame *inGame);
@@ -45,6 +102,7 @@ public:
friend class DebuggerController;
friend class DebuggableScript;
friend class ScScript;
+ friend class WatchableScriptArray;
};
} // End of namespace Wintermute
diff --git a/engines/wintermute/debugger.cpp b/engines/wintermute/debugger.cpp
index 6a349c070f..8b46552e12 100644
--- a/engines/wintermute/debugger.cpp
+++ b/engines/wintermute/debugger.cpp
@@ -41,11 +41,15 @@ Console::Console(WintermuteEngine *vm) : GUI::Debugger(), _engineRef(vm) {
registerCmd(STEP_CMD, WRAP_METHOD(Console, Cmd_Step));
registerCmd(CONTINUE_CMD, WRAP_METHOD(Console, Cmd_Continue));
registerCmd(FINISH_CMD, WRAP_METHOD(Console, Cmd_Finish));
+ registerCmd(WATCH_CMD, WRAP_METHOD(Console, Cmd_Watch));
registerCmd(BREAK_CMD, WRAP_METHOD(Console, Cmd_AddBreakpoint));
registerCmd(LIST_CMD, WRAP_METHOD(Console, Cmd_List));
registerCmd(REMOVE_BREAKPOINT_CMD, WRAP_METHOD(Console, Cmd_RemoveBreakpoint));
registerCmd(DISABLE_BREAKPOINT_CMD, WRAP_METHOD(Console, Cmd_DisableBreakpoint));
registerCmd(ENABLE_BREAKPOINT_CMD, WRAP_METHOD(Console, Cmd_EnableBreakpoint));
+ registerCmd(REMOVE_WATCH_CMD, WRAP_METHOD(Console, Cmd_RemoveWatch));
+ registerCmd(DISABLE_WATCH_CMD, WRAP_METHOD(Console, Cmd_DisableWatch));
+ registerCmd(ENABLE_WATCH_CMD, WRAP_METHOD(Console, Cmd_EnableWatch));
registerCmd(PRINT_CMD, WRAP_METHOD(Console, Cmd_Print));
registerCmd(SET_CMD, WRAP_METHOD(Console, Cmd_Set));
registerCmd(INFO_CMD, WRAP_METHOD(Console, Cmd_Info));
@@ -76,14 +80,26 @@ void Console::printUsage(const Common::String &command) {
debugPrintf("Usage: %s <id> to enable breakpoint #id\n", command.c_str());
} else if (command.equals(DISABLE_BREAKPOINT_CMD)) {
debugPrintf("Usage: %s <id> to disable breakpoint #id\n", command.c_str());
+ } else if (command.equals(REMOVE_WATCH_CMD)) {
+ debugPrintf("Usage: %s <id> to remove watchpoint #id\n", command.c_str());
+ } else if (command.equals(ENABLE_WATCH_CMD)) {
+ debugPrintf("Usage: %s <id> to enable watchpoint #id\n", command.c_str());
+ } else if (command.equals(DISABLE_WATCH_CMD)) {
+ debugPrintf("Usage: %s <id> to disable watchpoint #id\n", command.c_str());
} else if (command.equals(INFO_CMD)) {
debugPrintf("Usage: %s [watch|breakpoints]\n", command.c_str());
+ } else if (command.equals(WATCH_CMD)) {
+ debugPrintf("Usage: %s <file path> <name> to watch for <name> in file <file path>\n", command.c_str());
} else if (command.equals(STEP_CMD)) {
debugPrintf("Usage: %s to step\n", command.c_str());
} else if (command.equals(CONTINUE_CMD)) {
debugPrintf("Usage: %s to continue\n", command.c_str());
} else if (command.equals(FINISH_CMD)) {
debugPrintf("Usage: %s to finish\n", command.c_str());
+ } else if (command.equals(PRINT_CMD)) {
+ debugPrintf("Usage: %s <name> to print value of <name>\n", command.c_str());
+ } else if (command.equals(SET_CMD)) {
+ debugPrintf("Usage: %s <name> = <value> to set <name> to <value>\n", command.c_str());
} else {
debugPrintf("No help about this command, sorry.");
}
@@ -129,6 +145,47 @@ bool Console::Cmd_DisableBreakpoint(int argc, const char **argv) {
return true;
}
+bool Console::Cmd_RemoveWatch(int argc, const char **argv) {
+ if (argc == 2) {
+ Error error = CONTROLLER->removeWatchpoint(atoi(argv[1]));
+ printError(argv[0], error);
+ } else {
+ printUsage(argv[0]);
+ }
+
+ return true;
+}
+
+bool Console::Cmd_EnableWatch(int argc, const char **argv) {
+ if (argc == 2) {
+ Error error = CONTROLLER->enableWatchpoint(atoi(argv[1]));
+ printError(argv[0], error);
+ } else {
+ printUsage(argv[0]);
+ }
+ return true;
+}
+
+bool Console::Cmd_DisableWatch(int argc, const char **argv) {
+ if (argc == 2) {
+ Error error = CONTROLLER->disableWatchpoint(atoi(argv[1]));
+ printError(argv[0], error);
+ } else {
+ printUsage(argv[0]);
+ }
+ return true;
+}
+
+bool Console::Cmd_Watch(int argc, const char **argv) {
+ if (argc == 3) {
+ Error error = CONTROLLER->addWatch(argv[1], argv[2]);
+ printError(argv[0], error);
+ } else {
+ printUsage(argv[0]);
+ }
+ return true;
+}
+
bool Console::Cmd_Info(int argc, const char **argv) {
if (argc == 2 && !strncmp(argv[1], "breakpoints", 10)) {
Common::Array<BreakpointInfo> breakpoints = CONTROLLER->getBreakpoints();
@@ -136,6 +193,12 @@ bool Console::Cmd_Info(int argc, const char **argv) {
debugPrintf("%d %s:%d x%d, enabled: %d \n", i, breakpoints[i]._filename.c_str(), breakpoints[i]._line, breakpoints[i]._hits, breakpoints[i]._enabled);
}
return 1;
+ } else if (argc == 2 && !strncmp(argv[1], WATCH_CMD, 5)) {
+ Common::Array<WatchInfo>watchlist = CONTROLLER->getWatchlist();
+ for (uint i = 0; i < watchlist.size(); i++) {
+ debugPrintf("%d %s:%s x%d \n", i, watchlist[i]._filename.c_str(), watchlist[i]._symbol.c_str(), watchlist[i]._hits);
+ }
+ return 1;
} else {
printUsage(argv[0]);
return 1;
@@ -307,6 +370,13 @@ void Console::notifyStep(const char *filename, int line) {
onFrame();
}
+void Console::notifyWatch(const char *filename, const char *symbol, const char *newValue) {
+ debugPrintf("Watch: %s:%s <---- %s\n", filename, symbol, newValue);
+ printSource(0);
+ attach();
+ onFrame();
+}
+
Error Console::printSource(int n) {
Error* error = nullptr;
diff --git a/engines/wintermute/debugger.h b/engines/wintermute/debugger.h
index 542403c6ea..e5008bee3b 100644
--- a/engines/wintermute/debugger.h
+++ b/engines/wintermute/debugger.h
@@ -35,14 +35,19 @@
#define DEFAULT_SOURCE_PADDING 5
+
#define STEP_CMD "step"
#define CONTINUE_CMD "continue"
#define FINISH_CMD "finish"
+#define WATCH_CMD "watch"
#define BREAK_CMD "break"
#define LIST_CMD "list"
#define REMOVE_BREAKPOINT_CMD "del"
#define DISABLE_BREAKPOINT_CMD "disable"
#define ENABLE_BREAKPOINT_CMD "enable"
+#define REMOVE_WATCH_CMD "delw"
+#define DISABLE_WATCH_CMD "disablew"
+#define ENABLE_WATCH_CMD "enablew"
#define INFO_CMD "info"
#define SET_CMD "set"
#define PRINT_CMD "print"
@@ -88,6 +93,17 @@ public:
bool Cmd_EnableBreakpoint(int argc, const char **argv);
bool Cmd_DisableBreakpoint(int argc, const char **argv);
/**
+ * Add a watch.
+ *
+ * It monitors the value of some variable x against its
+ * last known state and it breaks if it has changed since.
+ *
+ */
+ bool Cmd_Watch(int argc, const char **argv);
+ bool Cmd_RemoveWatch(int argc, const char **argv);
+ bool Cmd_EnableWatch(int argc, const char **argv);
+ bool Cmd_DisableWatch(int argc, const char **argv);
+ /**
* Print info re:watch and breakpoints.
* This differs from e.g. gdb in that we have separate lists.
*/
@@ -123,6 +139,12 @@ public:
*/
void notifyBreakpoint(const char *filename, int line);
void notifyStep(const char *filename, int line);
+ /**
+ * To be called by the adapter when a watched variable
+ * is changed.
+ * Opens a console and prints info and listing if available.
+ */
+ void notifyWatch(const char *filename, const char *symbol, const char *newValue);
#endif
private:
diff --git a/engines/wintermute/debugger/debugger_controller.cpp b/engines/wintermute/debugger/debugger_controller.cpp
index 6fe49f2af2..2e021530f8 100644
--- a/engines/wintermute/debugger/debugger_controller.cpp
+++ b/engines/wintermute/debugger/debugger_controller.cpp
@@ -32,6 +32,7 @@
#include "engines/wintermute/base/scriptables/script_stack.h"
#include "engines/wintermute/debugger/breakpoint.h"
#include "engines/wintermute/debugger/debugger_controller.h"
+#include "engines/wintermute/debugger/watch.h"
#include "engines/wintermute/debugger/listing_providers/blank_listing_provider.h"
#include "engines/wintermute/debugger/listing_providers/cached_source_listing_provider.h"
#include "engines/wintermute/debugger/listing_providers/source_listing.h"
@@ -99,6 +100,47 @@ Error DebuggerController::enableBreakpoint(uint id) {
}
}
+Error DebuggerController::removeWatchpoint(uint id) {
+ assert(SCENGINE);
+ if (SCENGINE->_watches.size() > id) {
+ SCENGINE->_watches.remove_at(id);
+ return Error(SUCCESS, OK);
+ } else {
+ return Error(ERROR, NO_SUCH_BREAKPOINT, id);
+ }
+}
+
+
+Error DebuggerController::disableWatchpoint(uint id) {
+ assert(SCENGINE);
+ if (SCENGINE->_watches.size() > id) {
+ SCENGINE->_watches[id]->disable();
+ return Error(SUCCESS, OK);
+ } else {
+ return Error(ERROR, NO_SUCH_BREAKPOINT, id);
+ }
+}
+
+Error DebuggerController::enableWatchpoint(uint id) {
+ assert(SCENGINE);
+ if (SCENGINE->_watches.size() > id) {
+ SCENGINE->_watches[id]->enable();
+ return Error(SUCCESS, OK);
+ } else {
+ return Error(ERROR, NO_SUCH_BREAKPOINT, id);
+ }
+
+}
+
+Error DebuggerController::addWatch(const char *filename, const char *symbol) {
+ assert(SCENGINE);
+ if (!bytecodeExists(filename)) {
+ return Error(ERROR, NO_SUCH_BYTECODE, filename);
+ }
+ SCENGINE->_watches.push_back(new Watch(filename, symbol, this));
+ return Error(SUCCESS, OK, "Watchpoint added");
+}
+
void DebuggerController::onBreakpoint(const Breakpoint *breakpoint, DebuggableScript *script) {
_lastScript = script;
_lastLine = script->_currentLine;
@@ -111,6 +153,13 @@ void DebuggerController::notifyStep(DebuggableScript *script) override {
DEBUGGER->notifyStep(script->dbgGetFilename().c_str(), script->_currentLine);
}
+void DebuggerController::onWatch(const Watch *watch, DebuggableScript *script) {
+ _lastScript = script; // If script has changed do we still care?
+ _lastLine = script->_currentLine;
+ Common::String symbol = watch->getSymbol();
+ DEBUGGER->notifyWatch(script->dbgGetFilename().c_str(), symbol.c_str(), script->resolveName(symbol)->getString());
+}
+
Error DebuggerController::step() {
if (!_lastScript) {
return Error(ERROR, NOT_ALLOWED);
@@ -225,6 +274,17 @@ Common::Array<BreakpointInfo> DebuggerController::getBreakpoints() const {
return breakpoints;
}
+Common::Array<WatchInfo> DebuggerController::getWatchlist() const {
+ Common::Array<WatchInfo> watchlist;
+ for (uint i = 0; i < SCENGINE->_watches.size(); i++) {
+ WatchInfo watchInfo;
+ watchInfo._filename = SCENGINE->_watches[i]->getFilename();
+ watchInfo._symbol = SCENGINE->_watches[i]->getSymbol();
+ watchlist.push_back(watchInfo);
+ }
+ return watchlist;
+}
+
uint32 DebuggerController::getLastLine() const {
return _lastLine;
}
diff --git a/engines/wintermute/debugger/debugger_controller.h b/engines/wintermute/debugger/debugger_controller.h
index 87b4945f27..8c720281db 100644
--- a/engines/wintermute/debugger/debugger_controller.h
+++ b/engines/wintermute/debugger/debugger_controller.h
@@ -43,6 +43,13 @@ struct BreakpointInfo {
bool _enabled;
};
+struct WatchInfo {
+ Common::String _filename;
+ Common::String _symbol;
+ int _hits;
+ bool _enabled;
+};
+
struct TopEntry {
bool current;
Common::String filename;
@@ -70,7 +77,12 @@ public:
Error removeBreakpoint(uint id);
Error disableBreakpoint(uint id);
Error enableBreakpoint(uint id);
+ Error addWatch(const char *filename, const char *symbol);
+ Error removeWatchpoint(uint id);
+ Error disableWatchpoint(uint id);
+ Error enableWatchpoint(uint id);
Common::Array<BreakpointInfo> getBreakpoints() const;
+ Common::Array<WatchInfo> getWatchlist() const;
/**
* @brief step one instruction
*/
@@ -99,6 +111,7 @@ public:
* Inherited from ScriptMonitor
*/
void onBreakpoint(const Breakpoint *breakpoint, DebuggableScript *script);
+ void onWatch(const Watch *watch, DebuggableScript *script);
void notifyStep(DebuggableScript *script);
};
}
diff --git a/engines/wintermute/debugger/script_monitor.h b/engines/wintermute/debugger/script_monitor.h
index 5f3327692b..e9559e2ade 100644
--- a/engines/wintermute/debugger/script_monitor.h
+++ b/engines/wintermute/debugger/script_monitor.h
@@ -27,12 +27,15 @@ namespace Wintermute {
class DebuggableScript;
class Breakpoint;
+class Watch;
class ScriptMonitor {
public:
+
virtual ~ScriptMonitor() {};
virtual void notifyStep(DebuggableScript* script) = 0;
virtual void onBreakpoint(const Breakpoint* breakpoint, DebuggableScript* script) = 0;
+ virtual void onWatch(const Watch* watch, DebuggableScript* script) = 0;
};
} // End of namespace Wintermute
diff --git a/engines/wintermute/debugger/watch.cpp b/engines/wintermute/debugger/watch.cpp
new file mode 100644
index 0000000000..410756fdc8
--- /dev/null
+++ b/engines/wintermute/debugger/watch.cpp
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "watch.h"
+#include "watch_instance.h"
+#include "script_monitor.h"
+
+namespace Wintermute {
+
+Watch::Watch(const Common::String &filename, const Common::String &symbol, ScriptMonitor* monitor) : _enabled(false), _filename(filename), _symbol(symbol), _monitor(monitor) {}
+
+Watch::~Watch() { /* Nothing to take care of in here */ }
+
+void Watch::trigger(WatchInstance* instance) {
+ _monitor->onWatch(this, instance->_script);
+}
+
+Common::String Watch::getFilename() const { return _filename; }
+Common::String Watch::getSymbol() const { return _symbol; }
+bool Watch::isEnabled() const { return _enabled; }
+void Watch::enable() { _enabled = true; }
+void Watch::disable() { _enabled = false; }
+}
diff --git a/engines/wintermute/debugger/watch.h b/engines/wintermute/debugger/watch.h
new file mode 100644
index 0000000000..cbffe43b41
--- /dev/null
+++ b/engines/wintermute/debugger/watch.h
@@ -0,0 +1,51 @@
+/* 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 WATCH_H_
+#define WATCH_H_
+
+#include "common/str.h"
+
+namespace Wintermute {
+
+class ScValue;
+class ScScript;
+class WatchInstance;
+class ScriptMonitor;
+
+class Watch {
+ const Common::String _filename;
+ const Common::String _symbol;
+ int _enabled;
+ ScriptMonitor *_monitor;
+public:
+ Watch(const Common::String &filename, const Common::String &symbol, ScriptMonitor*);
+ Common::String getFilename() const;
+ Common::String getSymbol() const;
+ bool isEnabled() const;
+ void enable();
+ void disable();
+ void trigger(WatchInstance*);
+ virtual ~Watch();
+};
+}
+#endif /* WATCH_H_ */
diff --git a/engines/wintermute/debugger/watch_instance.cpp b/engines/wintermute/debugger/watch_instance.cpp
new file mode 100644
index 0000000000..2d31221ad4
--- /dev/null
+++ b/engines/wintermute/debugger/watch_instance.cpp
@@ -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.
+ *
+ */
+
+#include "watch_instance.h"
+#include "engines/wintermute/base/scriptables/script_value.h"
+#include "engines/wintermute/base/scriptables/debuggable/debuggable_script.h"
+#include "engines/wintermute/debugger/watch.h"
+
+namespace Wintermute {
+
+WatchInstance::WatchInstance(Watch* watch, DebuggableScript* script) : _watch(watch), _script(script), _lastValue(nullptr) {}
+WatchInstance::~WatchInstance() { delete _lastValue; }
+
+void WatchInstance::evaluate() {
+ if (_watch->isEnabled()) {
+ if (!_watch->getFilename().compareTo(_script->_filename)) {
+
+ if(_lastValue == nullptr) {
+ _lastValue = new ScValue(_script->_gameRef);
+ // ^^ This here is NULL by default
+ }
+ ScValue* currentValue = _script->resolveName(_watch->getSymbol());
+ if(ScValue::compare(
+ currentValue,
+ _lastValue
+ )) {
+ _lastValue->copy(currentValue);
+ _watch->trigger(this);
+ }
+ delete currentValue;
+ }
+ }
+}
+} // End of namespace Wintermute
diff --git a/engines/wintermute/debugger/watch_instance.h b/engines/wintermute/debugger/watch_instance.h
new file mode 100644
index 0000000000..84fb62968d
--- /dev/null
+++ b/engines/wintermute/debugger/watch_instance.h
@@ -0,0 +1,44 @@
+/* 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 WATCH_INSTANCE_H_
+#define WATCH_INSTANCE_H_
+
+namespace Wintermute {
+class Watch;
+class ScValue;
+class DebuggableScript;
+
+class WatchInstance {
+ Watch* _watch;
+ ScValue *_lastValue;
+ DebuggableScript* _script;
+public:
+ WatchInstance (Watch* watch, DebuggableScript* script);
+ ~WatchInstance();
+ void evaluate();
+friend class DebuggableScript;
+friend class Watch;
+};
+} // End of namespace Wintermute
+
+#endif /* WATCH_INSTANCE_H_ */
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 22b3609c5f..2c9c2e0180 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -100,6 +100,8 @@ MODULE_OBJS := \
debugger/listing_providers/source_listing.o \
debugger/listing.o \
debugger/script_monitor.o \
+ debugger/watch.o \
+ debugger/watch_instance.o \
detection.o \
math/math_util.o \
math/matrix4.o \