From a120aa855990f045d38d3b603306490305a48a39 Mon Sep 17 00:00:00 2001 From: Tobia Tesan Date: Mon, 29 Feb 2016 15:39:42 +0100 Subject: WINTERMUTE: Add Watch functionality --- .../scriptables/debuggable/debuggable_script.cpp | 42 +++++++++++-- .../scriptables/debuggable/debuggable_script.h | 5 +- .../debuggable/debuggable_script_engine.cpp | 1 + .../debuggable/debuggable_script_engine.h | 62 ++++++++++++++++++- engines/wintermute/debugger.cpp | 70 ++++++++++++++++++++++ engines/wintermute/debugger.h | 22 +++++++ .../wintermute/debugger/debugger_controller.cpp | 60 +++++++++++++++++++ engines/wintermute/debugger/debugger_controller.h | 13 ++++ engines/wintermute/debugger/script_monitor.h | 3 + engines/wintermute/debugger/watch.cpp | 42 +++++++++++++ engines/wintermute/debugger/watch.h | 51 ++++++++++++++++ engines/wintermute/debugger/watch_instance.cpp | 53 ++++++++++++++++ engines/wintermute/debugger/watch_instance.h | 44 ++++++++++++++ engines/wintermute/module.mk | 2 + 14 files changed, 463 insertions(+), 7 deletions(-) create mode 100644 engines/wintermute/debugger/watch.cpp create mode 100644 engines/wintermute/debugger/watch.h create mode 100644 engines/wintermute/debugger/watch_instance.cpp create mode 100644 engines/wintermute/debugger/watch_instance.h (limited to 'engines') 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 _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 { + Common::Array _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::push_back(newElement); + notifySubscribers(); + } + + size_type size() { + return Common::Array::size(); + } + + iterator begin() { + return Common::Array::begin(); + } + + iterator end() { + return Common::Array::end(); + } + + Watch *&operator[](size_type idx) { + return Common::Array::operator[](idx); + } + Watch *remove_at(size_type idx) { + Watch *res = Common::Array::remove_at(idx); + notifySubscribers(); + return res; + } +}; + class DebuggableScEngine : public ScEngine { Common::Array _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 to enable breakpoint #id\n", command.c_str()); } else if (command.equals(DISABLE_BREAKPOINT_CMD)) { debugPrintf("Usage: %s to disable breakpoint #id\n", command.c_str()); + } else if (command.equals(REMOVE_WATCH_CMD)) { + debugPrintf("Usage: %s to remove watchpoint #id\n", command.c_str()); + } else if (command.equals(ENABLE_WATCH_CMD)) { + debugPrintf("Usage: %s to enable watchpoint #id\n", command.c_str()); + } else if (command.equals(DISABLE_WATCH_CMD)) { + debugPrintf("Usage: %s 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 to watch for in file \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 to print value of \n", command.c_str()); + } else if (command.equals(SET_CMD)) { + debugPrintf("Usage: %s = to set to \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 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::Arraywatchlist = 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" @@ -87,6 +92,17 @@ public: bool Cmd_RemoveBreakpoint(int argc, const char **argv); 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 DebuggerController::getBreakpoints() const { return breakpoints; } +Common::Array DebuggerController::getWatchlist() const { + Common::Array 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 getBreakpoints() const; + Common::Array 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 \ -- cgit v1.2.3