diff options
Diffstat (limited to 'engines/wintermute/debugger/debugger_controller.cpp')
-rw-r--r-- | engines/wintermute/debugger/debugger_controller.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/engines/wintermute/debugger/debugger_controller.cpp b/engines/wintermute/debugger/debugger_controller.cpp new file mode 100644 index 0000000000..38f862365d --- /dev/null +++ b/engines/wintermute/debugger/debugger_controller.cpp @@ -0,0 +1,325 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/algorithm.h" +#include "common/str.h" +#include "common/tokenizer.h" +#include "engines/wintermute/debugger.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#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" +#define SCENGINE _engine->_game->_scEngine +#define DEBUGGER _engine->_debugger + +namespace Wintermute { + +DebuggerController::~DebuggerController() { + delete _sourceListingProvider; +} + +DebuggerController::DebuggerController(WintermuteEngine *vm) : _engine(vm) { + _sourceListingProvider = new CachedSourceListingProvider(); + clear(); +} + +bool DebuggerController::bytecodeExists(const Common::String &filename) { + uint32 compSize; + byte *compBuffer = SCENGINE->getCompiledScript(filename.c_str(), &compSize); + if (!compBuffer) { + return false; + } else { + return true; + } +} + +Error DebuggerController::addBreakpoint(const char *filename, int line) { + assert(SCENGINE); + if (bytecodeExists(filename)) { + SCENGINE->_breakpoints.push_back(new Breakpoint(filename, line, this)); + return Error(SUCCESS, OK); + } else { + return Error(ERROR, NO_SUCH_BYTECODE); + } +} + +Error DebuggerController::removeBreakpoint(uint id) { + assert(SCENGINE); + if (SCENGINE->_breakpoints.size() > id) { + SCENGINE->_breakpoints.remove_at(id); + return Error(SUCCESS, OK); + } else { + return Error(ERROR, NO_SUCH_BREAKPOINT, id); + } +} + +Error DebuggerController::disableBreakpoint(uint id) { + assert(SCENGINE); + if (SCENGINE->_breakpoints.size() > id) { + SCENGINE->_breakpoints[id]->disable(); + return Error(SUCCESS, OK); + } else { + return Error(ERROR, NO_SUCH_BREAKPOINT, id); + } +} + +Error DebuggerController::enableBreakpoint(uint id) { + assert(SCENGINE); + if (SCENGINE->_breakpoints.size() > id) { + SCENGINE->_breakpoints[id]->enable(); + return Error(SUCCESS, OK); + } else { + return Error(ERROR, NO_SUCH_BREAKPOINT, 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; + DEBUGGER->notifyBreakpoint(script->dbgGetFilename().c_str(), script->_currentLine); +} + +void DebuggerController::notifyStep(DebuggableScript *script) { + _lastScript = script; + _lastLine = script->_currentLine; + 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); + } + _lastScript->step(); + clear(); + return Error(SUCCESS, OK); +} + +Error DebuggerController::stepContinue() { + if (!_lastScript) { + return Error(ERROR, NOT_ALLOWED); + } + _lastScript->stepContinue(); + return Error(SUCCESS, OK); +} + +Error DebuggerController::stepFinish() { + if (!_lastScript) { + return Error(ERROR, NOT_ALLOWED); + } + _lastScript->stepFinish(); + clear(); + return Error(SUCCESS, OK); +} + +void DebuggerController::clear() { + _lastScript = nullptr; + _lastLine = -1; +} + +Common::String DebuggerController::readValue(const Common::String &name, Error *error) { + if (!_lastScript) { + delete error; + error = new Error(ERROR, NOT_ALLOWED); + return Common::String(); + } + char cstr[256]; // TODO not pretty + Common::strlcpy(cstr, name.c_str(), name.size() + 1); + cstr[255] = '\0'; // We 0-terminate it just in case it's longer than 255. + return _lastScript->resolveName(cstr)->getString(); +} + +Error DebuggerController::setValue(const Common::String &name, const Common::String &value, ScValue *&var) { + if (!_lastScript) { + return Error(ERROR, NOT_ALLOWED); + } + + Common::String trimmed = value; + trimmed.trim(); + char cstr[256]; + Common::strlcpy(cstr, name.c_str(), name.size() + 1); // TODO not pretty + + var = _lastScript->getVar(cstr); + if (var->_type == VAL_INT) { + char *endptr; + int res = strtol(trimmed.c_str(), &endptr, 10); // TODO: Hex too? + if (endptr == trimmed.c_str()) { + return Error(ERROR, PARSE_ERROR); + } else if (endptr == trimmed.c_str() + trimmed.size()) { + // We've parsed all of it, have we? + var->setInt(res); + } else { + assert(false); + return Error(ERROR, PARSE_ERROR); + // Something funny happened here. + } + } else if (var->_type == VAL_FLOAT) { + char *endptr; + float res = (float)strtod(trimmed.c_str(), &endptr); + if (endptr == trimmed.c_str()) { + return Error(ERROR, PARSE_ERROR); + } else if (endptr == trimmed.c_str() + trimmed.size()) { + // We've parsed all of it, have we? + var->setFloat(res); + } else { + return Error(ERROR, PARSE_ERROR); + assert(false); + // Something funny happened here. + } + } else if (var->_type == VAL_BOOL) { + Common::String str = Common::String(trimmed); + bool valAsBool; + if (Common::parseBool(trimmed, valAsBool)) { + var->setBool(valAsBool); + } else { + return Error(ERROR, PARSE_ERROR); + } + } else if (var->_type == VAL_STRING) { + var->setString(trimmed); + } else { + return Error(ERROR, NOT_YET_IMPLEMENTED); + } + return Error(SUCCESS, OK); +} + +void DebuggerController::showFps(bool show) { + _engine->_game->setShowFPS(show); +} + +Common::Array<BreakpointInfo> DebuggerController::getBreakpoints() const { + assert(SCENGINE); + Common::Array<BreakpointInfo> breakpoints; + for (uint i = 0; i < SCENGINE->_breakpoints.size(); i++) { + BreakpointInfo bpInfo; + bpInfo._filename = SCENGINE->_breakpoints[i]->getFilename(); + bpInfo._line = SCENGINE->_breakpoints[i]->getLine(); + bpInfo._hits = SCENGINE->_breakpoints[i]->getHits(); + bpInfo._enabled = SCENGINE->_breakpoints[i]->isEnabled(); + breakpoints.push_back(bpInfo); + } + 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; +} + +Common::String DebuggerController::getSourcePath() const { + return _sourceListingProvider->getPath(); +} + +Error DebuggerController::setSourcePath(const Common::String &sourcePath) { + ErrorCode err = _sourceListingProvider->setPath(sourcePath); + return Error((err == OK ? SUCCESS : ERROR), err); +} + +Listing* DebuggerController::getListing(Error* &error) { + delete (error); + if (_lastScript == nullptr) { + error = new Error(ERROR, NOT_ALLOWED); + return nullptr; + } + ErrorCode err; + Listing* res = _sourceListingProvider->getListing(SCENGINE->_currentScript->_filename, err); + error = new Error(err == OK ? SUCCESS : ERROR, err); + return res; +} + +Common::Array<TopEntry> DebuggerController::getTop() const { + Common::Array<TopEntry> res; + assert(SCENGINE); + for (uint i = 0; i < SCENGINE->_scripts.size(); i++) { + TopEntry entry; + entry.filename = SCENGINE->_scripts[i]->_filename; + entry.current = (SCENGINE->_scripts[i] == SCENGINE->_currentScript); + res.push_back(entry); + } + return res; +} + +} // end of namespace Wintermute |