aboutsummaryrefslogtreecommitdiff
path: root/gui/debugger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/debugger.cpp')
-rw-r--r--gui/debugger.cpp360
1 files changed, 270 insertions, 90 deletions
diff --git a/gui/debugger.cpp b/gui/debugger.cpp
index 35627dd584..c9b435963d 100644
--- a/gui/debugger.cpp
+++ b/gui/debugger.cpp
@@ -8,12 +8,12 @@
* 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.
@@ -23,9 +23,17 @@
// NB: This is really only necessary if USE_READLINE is defined
#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/system.h"
+#ifndef DISABLE_MD5
+#include "common/md5.h"
+#include "common/archive.h"
+#include "common/macresman.h"
+#include "common/stream.h"
+#endif
+
#include "engines/engine.h"
#include "gui/debugger.h"
@@ -51,19 +59,24 @@ Debugger::Debugger() {
#endif
// Register variables
- DVar_Register("debug_countdown", &_frameCountdown, DVAR_INT, 0);
+ registerVar("debug_countdown", &_frameCountdown, DVAR_INT, 0);
// Register commands
- //DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
- DCmd_Register("exit", WRAP_METHOD(Debugger, Cmd_Exit));
- DCmd_Register("quit", WRAP_METHOD(Debugger, Cmd_Exit));
-
- DCmd_Register("help", WRAP_METHOD(Debugger, Cmd_Help));
- DCmd_Register("openlog", WRAP_METHOD(Debugger, Cmd_OpenLog));
+ //registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
+ registerCmd("exit", WRAP_METHOD(Debugger, cmdExit));
+ registerCmd("quit", WRAP_METHOD(Debugger, cmdExit));
+
+ registerCmd("help", WRAP_METHOD(Debugger, cmdHelp));
+ registerCmd("openlog", WRAP_METHOD(Debugger, cmdOpenLog));
+#ifndef DISABLE_MD5
+ registerCmd("md5", WRAP_METHOD(Debugger, cmdMd5));
+ registerCmd("md5mac", WRAP_METHOD(Debugger, cmdMd5Mac));
+#endif
- DCmd_Register("debugflag_list", WRAP_METHOD(Debugger, Cmd_DebugFlagsList));
- DCmd_Register("debugflag_enable", WRAP_METHOD(Debugger, Cmd_DebugFlagEnable));
- DCmd_Register("debugflag_disable", WRAP_METHOD(Debugger, Cmd_DebugFlagDisable));
+ registerCmd("debuglevel", WRAP_METHOD(Debugger, cmdDebugLevel));
+ registerCmd("debugflag_list", WRAP_METHOD(Debugger, cmdDebugFlagsList));
+ registerCmd("debugflag_enable", WRAP_METHOD(Debugger, cmdDebugFlagEnable));
+ registerCmd("debugflag_disable", WRAP_METHOD(Debugger, cmdDebugFlagDisable));
}
Debugger::~Debugger() {
@@ -74,7 +87,20 @@ Debugger::~Debugger() {
// Initialisation Functions
-int Debugger::DebugPrintf(const char *format, ...) {
+int Debugger::getCharsPerLine() {
+#ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
+ const int charsPerLine = _debuggerDialog->getCharsPerLine();
+#elif defined(USE_READLINE)
+ int charsPerLine, rows;
+ rl_get_screen_size(&rows, &charsPerLine);
+#else
+ // Can we do better?
+ const int charsPerLine = 80;
+#endif
+ return charsPerLine;
+}
+
+int Debugger::debugPrintf(const char *format, ...) {
va_list argptr;
va_start(argptr, format);
@@ -88,6 +114,37 @@ int Debugger::DebugPrintf(const char *format, ...) {
return count;
}
+void Debugger::debugPrintColumns(const Common::StringArray &list) {
+ uint maxLength = 0;
+ uint i, j;
+
+ for (i = 0; i < list.size(); i++) {
+ if (list[i].size() > maxLength)
+ maxLength = list[i].size();
+ }
+
+ uint charsPerLine = getCharsPerLine();
+ uint columnWidth = maxLength + 2;
+ uint columns = charsPerLine / columnWidth;
+
+ uint lines = list.size() / columns;
+
+ if (list.size() % columns)
+ lines++;
+
+ // This won't always use all available columns, but even if it did the
+ // number of lines should be the same so that's good enough.
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ uint pos = i + j * lines;
+ if (pos < list.size()) {
+ debugPrintf("%*s", -columnWidth, list[pos].c_str());
+ }
+ }
+ debugPrintf("\n");
+ }
+}
+
void Debugger::preEnter() {
g_engine->pauseEngine(true);
}
@@ -133,6 +190,14 @@ Debugger *g_readline_debugger;
char *readline_completionFunction(const char *text, int state) {
return g_readline_debugger->readlineComplete(text, state);
}
+
+#ifdef USE_READLINE_INT_COMPLETION
+typedef int RLCompFunc_t(const char *, int);
+#else
+typedef char *RLCompFunc_t(const char *, int);
+#endif
+
+
} // end of anonymous namespace
#endif
@@ -143,13 +208,13 @@ void Debugger::enter() {
#ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
if (_firstTime) {
- DebugPrintf("Debugger started, type 'exit' to return to the game.\n");
- DebugPrintf("Type 'help' to see a little list of commands and variables.\n");
+ debugPrintf("Debugger started, type 'exit' to return to the game.\n");
+ debugPrintf("Type 'help' to see a little list of commands and variables.\n");
_firstTime = false;
}
if (_errStr) {
- DebugPrintf("ERROR: %s\n\n", _errStr);
+ debugPrintf("ERROR: %s\n\n", _errStr);
free(_errStr);
_errStr = NULL;
}
@@ -162,7 +227,7 @@ void Debugger::enter() {
// TODO: add support for saving/loading history?
g_readline_debugger = this;
- rl_completion_entry_function = &readline_completionFunction;
+ rl_completion_entry_function = (RLCompFunc_t *)&readline_completionFunction;
char *line_read = 0;
do {
@@ -232,82 +297,82 @@ bool Debugger::parseCommand(const char *inputOrig) {
}
// It's not a command, so things get a little tricky for variables. Do fuzzy matching to ignore things like subscripts.
- for (uint i = 0; i < _dvars.size(); i++) {
- if (!strncmp(_dvars[i].name.c_str(), param[0], _dvars[i].name.size())) {
+ for (uint i = 0; i < _vars.size(); i++) {
+ if (!strncmp(_vars[i].name.c_str(), param[0], _vars[i].name.size())) {
if (num_params > 1) {
// Alright, we need to check the TYPE of the variable to deref and stuff... the array stuff is a bit ugly :)
- switch (_dvars[i].type) {
+ switch (_vars[i].type) {
// Integer
case DVAR_BYTE:
- *(byte *)_dvars[i].variable = atoi(param[1]);
- DebugPrintf("byte%s = %d\n", param[0], *(byte *)_dvars[i].variable);
+ *(byte *)_vars[i].variable = atoi(param[1]);
+ debugPrintf("byte%s = %d\n", param[0], *(byte *)_vars[i].variable);
break;
case DVAR_INT:
- *(int32 *)_dvars[i].variable = atoi(param[1]);
- DebugPrintf("(int)%s = %d\n", param[0], *(int32 *)_dvars[i].variable);
+ *(int32 *)_vars[i].variable = atoi(param[1]);
+ debugPrintf("(int)%s = %d\n", param[0], *(int32 *)_vars[i].variable);
break;
case DVAR_BOOL:
- if (Common::parseBool(param[1], *(bool *)_dvars[i].variable))
- DebugPrintf("(bool)%s = %s\n", param[0], *(bool *)_dvars[i].variable ? "true" : "false");
+ if (Common::parseBool(param[1], *(bool *)_vars[i].variable))
+ debugPrintf("(bool)%s = %s\n", param[0], *(bool *)_vars[i].variable ? "true" : "false");
else
- DebugPrintf("Invalid value for boolean variable. Valid values are \"true\", \"false\", \"1\", \"0\", \"yes\", \"no\"\n");
+ debugPrintf("Invalid value for boolean variable. Valid values are \"true\", \"false\", \"1\", \"0\", \"yes\", \"no\"\n");
break;
// Integer Array
case DVAR_INTARRAY: {
const char *chr = strchr(param[0], '[');
if (!chr) {
- DebugPrintf("You must access this array as %s[element]\n", param[0]);
+ debugPrintf("You must access this array as %s[element]\n", param[0]);
} else {
int element = atoi(chr+1);
- int32 *var = *(int32 **)_dvars[i].variable;
- if (element >= _dvars[i].arraySize) {
- DebugPrintf("%s is out of range (array is %d elements big)\n", param[0], _dvars[i].arraySize);
+ int32 *var = *(int32 **)_vars[i].variable;
+ if (element >= _vars[i].arraySize) {
+ debugPrintf("%s is out of range (array is %d elements big)\n", param[0], _vars[i].arraySize);
} else {
var[element] = atoi(param[1]);
- DebugPrintf("(int)%s = %d\n", param[0], var[element]);
+ debugPrintf("(int)%s = %d\n", param[0], var[element]);
}
}
}
break;
default:
- DebugPrintf("Failed to set variable %s to %s - unknown type\n", _dvars[i].name.c_str(), param[1]);
+ debugPrintf("Failed to set variable %s to %s - unknown type\n", _vars[i].name.c_str(), param[1]);
break;
}
} else {
// And again, type-dependent prints/defrefs. The array one is still ugly.
- switch (_dvars[i].type) {
+ switch (_vars[i].type) {
// Integer
case DVAR_BYTE:
- DebugPrintf("(byte)%s = %d\n", param[0], *(const byte *)_dvars[i].variable);
+ debugPrintf("(byte)%s = %d\n", param[0], *(const byte *)_vars[i].variable);
break;
case DVAR_INT:
- DebugPrintf("(int)%s = %d\n", param[0], *(const int32 *)_dvars[i].variable);
+ debugPrintf("(int)%s = %d\n", param[0], *(const int32 *)_vars[i].variable);
break;
case DVAR_BOOL:
- DebugPrintf("(bool)%s = %s\n", param[0], *(const bool *)_dvars[i].variable ? "true" : "false");
+ debugPrintf("(bool)%s = %s\n", param[0], *(const bool *)_vars[i].variable ? "true" : "false");
break;
// Integer array
case DVAR_INTARRAY: {
const char *chr = strchr(param[0], '[');
if (!chr) {
- DebugPrintf("You must access this array as %s[element]\n", param[0]);
+ debugPrintf("You must access this array as %s[element]\n", param[0]);
} else {
int element = atoi(chr+1);
- const int32 *var = *(const int32 **)_dvars[i].variable;
- if (element >= _dvars[i].arraySize) {
- DebugPrintf("%s is out of range (array is %d elements big)\n", param[0], _dvars[i].arraySize);
+ const int32 *var = *(const int32 **)_vars[i].variable;
+ if (element >= _vars[i].arraySize) {
+ debugPrintf("%s is out of range (array is %d elements big)\n", param[0], _vars[i].arraySize);
} else {
- DebugPrintf("(int)%s = %d\n", param[0], var[element]);
+ debugPrintf("(int)%s = %d\n", param[0], var[element]);
}
}
}
break;
// String
case DVAR_STRING:
- DebugPrintf("(string)%s = %s\n", param[0], ((Common::String *)_dvars[i].variable)->c_str());
+ debugPrintf("(string)%s = %s\n", param[0], ((Common::String *)_vars[i].variable)->c_str());
break;
default:
- DebugPrintf("%s = (unknown type)\n", param[0]);
+ debugPrintf("%s = (unknown type)\n", param[0]);
break;
}
}
@@ -317,7 +382,7 @@ bool Debugger::parseCommand(const char *inputOrig) {
}
}
- DebugPrintf("Unknown command or variable\n");
+ debugPrintf("Unknown command or variable\n");
free(input);
return true;
}
@@ -396,49 +461,41 @@ char *Debugger::readlineComplete(const char *input, int state) {
#endif
// Variable registration function
-void Debugger::DVar_Register(const Common::String &varname, void *pointer, VarType type, int arraySize) {
+void Debugger::registerVar(const Common::String &varname, void *pointer, VarType type, int arraySize) {
// TODO: Filter out duplicates
// TODO: Sort this list? Then we can do binary search later on when doing lookups.
assert(pointer);
- DVar tmp;
+ Var tmp;
tmp.name = varname;
tmp.type = type;
tmp.variable = pointer;
tmp.arraySize = arraySize;
- _dvars.push_back(tmp);
+ _vars.push_back(tmp);
}
// Command registration function
-void Debugger::DCmd_Register(const Common::String &cmdname, Debuglet *debuglet) {
+void Debugger::registerCmd(const Common::String &cmdname, Debuglet *debuglet) {
assert(debuglet && debuglet->isValid());
_cmds[cmdname] = Common::SharedPtr<Debuglet>(debuglet);
}
// Detach ("exit") the debugger
-bool Debugger::Cmd_Exit(int argc, const char **argv) {
+bool Debugger::cmdExit(int argc, const char **argv) {
detach();
return false;
}
// Print a list of all registered commands (and variables, if any),
// nicely word-wrapped.
-bool Debugger::Cmd_Help(int argc, const char **argv) {
-#ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
- const int charsPerLine = _debuggerDialog->getCharsPerLine();
-#elif defined(USE_READLINE)
- int charsPerLine, rows;
- rl_get_screen_size(&rows, &charsPerLine);
-#else
- // Can we do better?
- const int charsPerLine = 80;
-#endif
+bool Debugger::cmdHelp(int argc, const char **argv) {
+ const int charsPerLine = getCharsPerLine();
int width, size;
uint i;
- DebugPrintf("Commands are:\n");
+ debugPrintf("Commands are:\n");
// Obtain a list of sorted command names
Common::Array<Common::String> cmds;
@@ -454,84 +511,207 @@ bool Debugger::Cmd_Help(int argc, const char **argv) {
size = cmds[i].size() + 1;
if ((width + size) >= charsPerLine) {
- DebugPrintf("\n");
+ debugPrintf("\n");
width = size;
} else
width += size;
- DebugPrintf("%s ", cmds[i].c_str());
+ debugPrintf("%s ", cmds[i].c_str());
}
- DebugPrintf("\n");
+ debugPrintf("\n");
- if (!_dvars.empty()) {
- DebugPrintf("\n");
- DebugPrintf("Variables are:\n");
+ if (!_vars.empty()) {
+ debugPrintf("\n");
+ debugPrintf("Variables are:\n");
width = 0;
- for (i = 0; i < _dvars.size(); i++) {
- size = _dvars[i].name.size() + 1;
+ for (i = 0; i < _vars.size(); i++) {
+ size = _vars[i].name.size() + 1;
if ((width + size) >= charsPerLine) {
- DebugPrintf("\n");
+ debugPrintf("\n");
width = size;
} else
width += size;
- DebugPrintf("%s ", _dvars[i].name.c_str());
+ debugPrintf("%s ", _vars[i].name.c_str());
}
- DebugPrintf("\n");
+ debugPrintf("\n");
}
return true;
}
-bool Debugger::Cmd_OpenLog(int argc, const char **argv) {
+bool Debugger::cmdOpenLog(int argc, const char **argv) {
if (g_system->hasFeature(OSystem::kFeatureDisplayLogFile))
g_system->displayLogFile();
else
- DebugPrintf("Opening the log file not supported on this system\n");
+ debugPrintf("Opening the log file not supported on this system\n");
+ return true;
+}
+
+#ifndef DISABLE_MD5
+struct ArchiveMemberLess {
+ bool operator()(const Common::ArchiveMemberPtr &x, const Common::ArchiveMemberPtr &y) const {
+ return (*x).getDisplayName().compareToIgnoreCase((*y).getDisplayName()) < 0;
+ }
+};
+
+bool Debugger::cmdMd5(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("md5 [-n length] <filename | pattern>\n");
+ } else {
+ uint32 length = 0;
+ uint paramOffset = 0;
+
+ // If the user supplied an -n parameter, set the bytes to read
+ if (!strcmp(argv[1], "-n")) {
+ // Make sure that we have at least two more parameters
+ if (argc < 4) {
+ debugPrintf("md5 [-n length] <filename | pattern>\n");
+ return true;
+ }
+ length = atoi(argv[2]);
+ paramOffset = 2;
+ }
+
+ // Assume that spaces are part of a single filename.
+ Common::String filename = argv[1 + paramOffset];
+ for (int i = 2 + paramOffset; i < argc; i++) {
+ filename = filename + " " + argv[i];
+ }
+ Common::ArchiveMemberList list;
+ SearchMan.listMatchingMembers(list, filename);
+ if (list.empty()) {
+ debugPrintf("File '%s' not found\n", filename.c_str());
+ } else {
+ sort(list.begin(), list.end(), ArchiveMemberLess());
+ for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
+ Common::SeekableReadStream *stream = (*iter)->createReadStream();
+ Common::String md5 = Common::computeStreamMD5AsString(*stream, length);
+ debugPrintf("%s %s %d\n", md5.c_str(), (*iter)->getDisplayName().c_str(), stream->size());
+ delete stream;
+ }
+ }
+ }
+ return true;
+}
+
+bool Debugger::cmdMd5Mac(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("md5mac [-n length] <base filename>\n");
+ } else {
+ uint32 length = 0;
+ uint paramOffset = 0;
+
+ // If the user supplied an -n parameter, set the bytes to read
+ if (!strcmp(argv[1], "-n")) {
+ // Make sure that we have at least two more parameters
+ if (argc < 4) {
+ debugPrintf("md5mac [-n length] <base filename>\n");
+ return true;
+ }
+ length = atoi(argv[2]);
+ paramOffset = 2;
+ }
+
+ // Assume that spaces are part of a single filename.
+ Common::String filename = argv[1 + paramOffset];
+ for (int i = 2 + paramOffset; i < argc; i++) {
+ filename = filename + " " + argv[i];
+ }
+ Common::MacResManager macResMan;
+ // FIXME: There currently isn't any way to tell the Mac resource
+ // manager to open a specific file. Instead, it takes a "base name"
+ // and constructs a file name out of that. While usually a desirable
+ // thing, it's not ideal here.
+ if (!macResMan.open(filename)) {
+ debugPrintf("Resource file '%s' not found\n", filename.c_str());
+ } else {
+ if (!macResMan.hasResFork() && !macResMan.hasDataFork()) {
+ debugPrintf("'%s' has neither data not resource fork\n", macResMan.getBaseFileName().c_str());
+ } else {
+ // The resource fork is probably the most relevant one.
+ if (macResMan.hasResFork()) {
+ Common::String md5 = macResMan.computeResForkMD5AsString(length);
+ debugPrintf("%s %s (resource) %d\n", md5.c_str(), macResMan.getBaseFileName().c_str(), macResMan.getResForkDataSize());
+ }
+ if (macResMan.hasDataFork()) {
+ Common::SeekableReadStream *stream = macResMan.getDataFork();
+ Common::String md5 = Common::computeStreamMD5AsString(*stream, length);
+ debugPrintf("%s %s (data) %d\n", md5.c_str(), macResMan.getBaseFileName().c_str(), stream->size());
+ }
+ }
+ macResMan.close();
+ }
+ }
return true;
}
+#endif
+
+bool Debugger::cmdDebugLevel(int argc, const char **argv) {
+ if (argc == 1) { // print level
+ debugPrintf("Debugging is currently %s (set at level %d)\n", (gDebugLevel >= 0) ? "enabled" : "disabled", gDebugLevel);
+ debugPrintf("Usage: %s <n> where n is 0 to 10 or -1 to disable debugging\n", argv[0]);
+ } else { // set level
+ gDebugLevel = atoi(argv[1]);
+ if (gDebugLevel >= 0 && gDebugLevel < 11) {
+ debugPrintf("Debug level set to level %d\n", gDebugLevel);
+ } else if (gDebugLevel < 0) {
+ debugPrintf("Debugging is now disabled\n");
+ } else {
+ debugPrintf("Invalid debug level value\n");
+ debugPrintf("Usage: %s <n> where n is 0 to 10 or -1 to disable debugging\n", argv[0]);
+ }
+ }
+ return true;
+}
-bool Debugger::Cmd_DebugFlagsList(int argc, const char **argv) {
+bool Debugger::cmdDebugFlagsList(int argc, const char **argv) {
const Common::DebugManager::DebugChannelList &debugLevels = DebugMan.listDebugChannels();
- DebugPrintf("Engine debug levels:\n");
- DebugPrintf("--------------------\n");
+ debugPrintf("Engine debug levels:\n");
+ debugPrintf("--------------------\n");
if (debugLevels.empty()) {
- DebugPrintf("No engine debug levels\n");
+ debugPrintf("No engine debug levels\n");
return true;
}
for (Common::DebugManager::DebugChannelList::const_iterator i = debugLevels.begin(); i != debugLevels.end(); ++i) {
- DebugPrintf("%c%s - %s (%s)\n", i->enabled ? '+' : ' ',
+ debugPrintf("%c%s - %s (%s)\n", i->enabled ? '+' : ' ',
i->name.c_str(), i->description.c_str(),
i->enabled ? "enabled" : "disabled");
}
- DebugPrintf("\n");
+ debugPrintf("\n");
return true;
}
-bool Debugger::Cmd_DebugFlagEnable(int argc, const char **argv) {
+bool Debugger::cmdDebugFlagEnable(int argc, const char **argv) {
if (argc < 2) {
- DebugPrintf("debugflag_enable <flag>\n");
+ debugPrintf("debugflag_enable [<flag> | all]\n");
} else {
- if (DebugMan.enableDebugChannel(argv[1])) {
- DebugPrintf("Enabled debug flag '%s'\n", argv[1]);
+ if (!scumm_stricmp(argv[1], "all")) {
+ debugPrintf("Enabled all debug flags\n");
+ DebugMan.enableAllDebugChannels();
+ } else if (DebugMan.enableDebugChannel(argv[1])) {
+ debugPrintf("Enabled debug flag '%s'\n", argv[1]);
} else {
- DebugPrintf("Failed to enable debug flag '%s'\n", argv[1]);
+ debugPrintf("Failed to enable debug flag '%s'\n", argv[1]);
}
}
return true;
}
-bool Debugger::Cmd_DebugFlagDisable(int argc, const char **argv) {
+bool Debugger::cmdDebugFlagDisable(int argc, const char **argv) {
if (argc < 2) {
- DebugPrintf("debugflag_disable <flag>\n");
+ debugPrintf("debugflag_disable [<flag> | all]\n");
} else {
- if (DebugMan.disableDebugChannel(argv[1])) {
- DebugPrintf("Disabled debug flag '%s'\n", argv[1]);
+ if (!scumm_stricmp(argv[1], "all")) {
+ debugPrintf("Disabled all debug flags\n");
+ DebugMan.disableAllDebugChannels();
+ } else if (DebugMan.disableDebugChannel(argv[1])) {
+ debugPrintf("Disabled debug flag '%s'\n", argv[1]);
} else {
- DebugPrintf("Failed to disable debug flag '%s'\n", argv[1]);
+ debugPrintf("Failed to disable debug flag '%s'\n", argv[1]);
}
}
return true;