ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

gui/debugger.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // NB: This is really only necessary if USE_READLINE is defined
00024 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00025 
00026 #include "common/debug.h"
00027 #include "common/debug-channels.h"
00028 #include "common/system.h"
00029 
00030 #ifndef DISABLE_MD5
00031 #include "common/md5.h"
00032 #include "common/archive.h"
00033 #include "common/macresman.h"
00034 #include "common/stream.h"
00035 #endif
00036 
00037 #include "engines/engine.h"
00038 
00039 #include "gui/debugger.h"
00040 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00041     #include "gui/console.h"
00042 #elif defined(USE_READLINE)
00043     #include <readline/readline.h>
00044     #include <readline/history.h>
00045     #include "common/events.h"
00046 #endif
00047 
00048 
00049 namespace GUI {
00050 
00051 Debugger::Debugger() {
00052     _frameCountdown = 0;
00053     _isActive = false;
00054     _firstTime = true;
00055 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00056     _debuggerDialog = new GUI::ConsoleDialog(1.0f, 0.67f);
00057     _debuggerDialog->setInputCallback(debuggerInputCallback, this);
00058     _debuggerDialog->setCompletionCallback(debuggerCompletionCallback, this);
00059 #endif
00060 
00061     // Register variables
00062     registerVar("debug_countdown", &_frameCountdown, DVAR_INT, 0);
00063 
00064     // Register commands
00065     //registerCmd("continue",           WRAP_METHOD(Debugger, cmdExit));
00066     registerCmd("exit",             WRAP_METHOD(Debugger, cmdExit));
00067     registerCmd("quit",             WRAP_METHOD(Debugger, cmdExit));
00068 
00069     registerCmd("help",             WRAP_METHOD(Debugger, cmdHelp));
00070     registerCmd("openlog",          WRAP_METHOD(Debugger, cmdOpenLog));
00071 #ifndef DISABLE_MD5
00072     registerCmd("md5",              WRAP_METHOD(Debugger, cmdMd5));
00073     registerCmd("md5mac",           WRAP_METHOD(Debugger, cmdMd5Mac));
00074 #endif
00075 
00076     registerCmd("debuglevel",       WRAP_METHOD(Debugger, cmdDebugLevel));
00077     registerCmd("debugflag_list",       WRAP_METHOD(Debugger, cmdDebugFlagsList));
00078     registerCmd("debugflag_enable", WRAP_METHOD(Debugger, cmdDebugFlagEnable));
00079     registerCmd("debugflag_disable",    WRAP_METHOD(Debugger, cmdDebugFlagDisable));
00080 }
00081 
00082 Debugger::~Debugger() {
00083 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00084     delete _debuggerDialog;
00085 #endif
00086 }
00087 
00088 
00089 // Initialisation Functions
00090 int Debugger::getCharsPerLine() {
00091 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00092     const int charsPerLine = _debuggerDialog->getCharsPerLine();
00093 #elif defined(USE_READLINE)
00094     int charsPerLine, rows;
00095     rl_get_screen_size(&rows, &charsPerLine);
00096 #else
00097     // Can we do better?
00098     const int charsPerLine = 80;
00099 #endif
00100     return charsPerLine;
00101 }
00102 
00103 int Debugger::debugPrintf(const char *format, ...) {
00104     va_list argptr;
00105 
00106     va_start(argptr, format);
00107     int count;
00108 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00109     count = _debuggerDialog->vprintFormat(1, format, argptr);
00110 #else
00111     count = ::vprintf(format, argptr);
00112     ::fflush(stdout);
00113 #endif
00114     va_end (argptr);
00115     return count;
00116 }
00117 
00118 void Debugger::debugPrintColumns(const Common::StringArray &list) {
00119     uint maxLength = 0;
00120     uint i, j;
00121 
00122     for (i = 0; i < list.size(); i++) {
00123         if (list[i].size() > maxLength)
00124             maxLength = list[i].size();
00125     }
00126 
00127     uint charsPerLine = getCharsPerLine();
00128     uint columnWidth = maxLength + 2;
00129     uint columns = charsPerLine / columnWidth;
00130 
00131     uint lines = list.size() / columns;
00132 
00133     if (list.size() % columns)
00134         lines++;
00135 
00136     // This won't always use all available columns, but even if it did the
00137     // number of lines should be the same so that's good enough.
00138     for (i = 0; i < lines; i++) {
00139         for (j = 0; j < columns; j++) {
00140             uint pos = i + j * lines;
00141             if (pos < list.size()) {
00142                 debugPrintf("%*s", -columnWidth, list[pos].c_str());
00143             }
00144         }
00145         debugPrintf("\n");
00146     }
00147 }
00148 
00149 void Debugger::preEnter() {
00150     g_engine->pauseEngine(true);
00151 }
00152 
00153 void Debugger::postEnter() {
00154     g_engine->pauseEngine(false);
00155 }
00156 
00157 void Debugger::attach(const char *entry) {
00158     g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
00159 
00160     // Set error string (if any)
00161     _errStr = entry ? entry : "";
00162 
00163     // Reset frame countdown (i.e. attach immediately)
00164     _frameCountdown = 1;
00165 }
00166 
00167 void Debugger::detach() {
00168     g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
00169 }
00170 
00171 // Temporary execution handler
00172 void Debugger::onFrame() {
00173     // Count down until 0 is reached
00174     if (_frameCountdown > 0) {
00175         --_frameCountdown;
00176         if (_frameCountdown == 0) {
00177             _isActive = true;
00178             preEnter();
00179             enter();
00180             postEnter();
00181             _isActive = false;
00182         }
00183     }
00184 }
00185 
00186 #if defined(USE_TEXT_CONSOLE_FOR_DEBUGGER) && defined(USE_READLINE)
00187 namespace {
00188 Debugger *g_readline_debugger;
00189 
00190 char *readline_completionFunction(const char *text, int state) {
00191     return g_readline_debugger->readlineComplete(text, state);
00192 }
00193 
00194 void readline_eventFunction() {
00195     Common::EventManager *eventMan = g_system->getEventManager();
00196 
00197     Common::Event event;
00198     while (eventMan->pollEvent(event)) {
00199         // drop all events
00200     }
00201 }
00202 
00203 #ifdef USE_READLINE_INT_COMPLETION
00204 typedef int RLCompFunc_t(const char *, int);
00205 #else
00206 typedef char *RLCompFunc_t(const char *, int);
00207 #endif
00208 
00209 
00210 } // end of anonymous namespace
00211 #endif
00212 
00213 // Main Debugger Loop
00214 void Debugger::enter() {
00215     // TODO: Having three I/O methods #ifdef-ed in this file is not the
00216     // cleanest approach to this...
00217 
00218 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00219     if (_firstTime) {
00220         debugPrintf("Debugger started, type 'exit' to return to the game.\n");
00221         debugPrintf("Type 'help' to see a little list of commands and variables.\n");
00222         _firstTime = false;
00223     }
00224 
00225     if (_errStr.size()) {
00226         debugPrintf("ERROR: %s\n\n", _errStr.c_str());
00227         _errStr.clear();
00228     }
00229 
00230     _debuggerDialog->runModal();
00231 #else
00232     printf("Debugger entered, please switch to this console for input.\n");
00233 
00234 #ifdef USE_READLINE
00235     // TODO: add support for saving/loading history?
00236 
00237     g_readline_debugger = this;
00238     rl_completion_entry_function = (RLCompFunc_t *)&readline_completionFunction;
00239     rl_event_hook = (rl_hook_func_t *)&readline_eventFunction;
00240 
00241     char *line_read = 0;
00242     do {
00243         free(line_read);
00244         line_read = readline("debug> ");
00245 
00246         if (line_read && line_read[0])
00247             add_history(line_read);
00248 
00249     } while (line_read && parseCommand(line_read));
00250 
00251     free(line_read);
00252     line_read = 0;
00253 
00254 #else
00255     int i;
00256     char buf[256];
00257 
00258     do {
00259         printf("debug> ");
00260         if (!fgets(buf, sizeof(buf), stdin))
00261             return;
00262 
00263         i = strlen(buf);
00264         while (i > 0 && buf[i - 1] == '\n')
00265             buf[--i] = 0;
00266 
00267         if (i == 0)
00268             continue;
00269     } while (parseCommand(buf));
00270 #endif
00271 
00272 #endif
00273 }
00274 
00275 bool Debugger::handleCommand(int argc, const char **argv, bool &result) {
00276     assert(argc > 0);
00277 
00278     if (_cmds.contains(argv[0])) {
00279         assert(_cmds[argv[0]]);
00280         result = (*_cmds[argv[0]])(argc, argv);
00281         return true;
00282     }
00283 
00284     return false;
00285 }
00286 
00287 // Command execution loop
00288 bool Debugger::parseCommand(const char *inputOrig) {
00289     int num_params = 0;
00290     const char *param[256];
00291 
00292     // Parse out any params
00293     Common::String input(inputOrig);
00294     splitCommand(input, num_params, &param[0]);
00295 
00296     if (num_params == 0) {
00297         return true;
00298     }
00299 
00300     // Handle commands first
00301     bool result;
00302     if (handleCommand(num_params, param, result)) {
00303         return result;
00304     }
00305 
00306     // It's not a command, so things get a little tricky for variables. Do fuzzy matching to ignore things like subscripts.
00307     for (uint i = 0; i < _vars.size(); i++) {
00308         if (!strncmp(_vars[i].name.c_str(), param[0], _vars[i].name.size())) {
00309             if (num_params > 1) {
00310                 // Alright, we need to check the TYPE of the variable to deref and stuff... the array stuff is a bit ugly :)
00311                 switch (_vars[i].type) {
00312                 // Integer
00313                 case DVAR_BYTE:
00314                     *(byte *)_vars[i].variable = atoi(param[1]);
00315                     debugPrintf("byte%s = %d\n", param[0], *(byte *)_vars[i].variable);
00316                     break;
00317                 case DVAR_INT:
00318                     *(int32 *)_vars[i].variable = atoi(param[1]);
00319                     debugPrintf("(int)%s = %d\n", param[0], *(int32 *)_vars[i].variable);
00320                     break;
00321                 case DVAR_BOOL:
00322                     if (Common::parseBool(param[1], *(bool *)_vars[i].variable))
00323                         debugPrintf("(bool)%s = %s\n", param[0], *(bool *)_vars[i].variable ? "true" : "false");
00324                     else
00325                         debugPrintf("Invalid value for boolean variable. Valid values are \"true\", \"false\", \"1\", \"0\", \"yes\", \"no\"\n");
00326                     break;
00327                 // Integer Array
00328                 case DVAR_INTARRAY: {
00329                     const char *chr = strchr(param[0], '[');
00330                     if (!chr) {
00331                         debugPrintf("You must access this array as %s[element]\n", param[0]);
00332                     } else {
00333                         int element = atoi(chr+1);
00334                         int32 *var = *(int32 **)_vars[i].variable;
00335                         if (element >= _vars[i].arraySize) {
00336                             debugPrintf("%s is out of range (array is %d elements big)\n", param[0], _vars[i].arraySize);
00337                         } else {
00338                             var[element] = atoi(param[1]);
00339                             debugPrintf("(int)%s = %d\n", param[0], var[element]);
00340                         }
00341                     }
00342                     }
00343                     break;
00344                 default:
00345                     debugPrintf("Failed to set variable %s to %s - unknown type\n", _vars[i].name.c_str(), param[1]);
00346                     break;
00347                 }
00348             } else {
00349                 // And again, type-dependent prints/defrefs. The array one is still ugly.
00350                 switch (_vars[i].type) {
00351                 // Integer
00352                 case DVAR_BYTE:
00353                     debugPrintf("(byte)%s = %d\n", param[0], *(const byte *)_vars[i].variable);
00354                     break;
00355                 case DVAR_INT:
00356                     debugPrintf("(int)%s = %d\n", param[0], *(const int32 *)_vars[i].variable);
00357                     break;
00358                 case DVAR_BOOL:
00359                     debugPrintf("(bool)%s = %s\n", param[0], *(const bool *)_vars[i].variable ? "true" : "false");
00360                     break;
00361                 // Integer array
00362                 case DVAR_INTARRAY: {
00363                     const char *chr = strchr(param[0], '[');
00364                     if (!chr) {
00365                         debugPrintf("You must access this array as %s[element]\n", param[0]);
00366                     } else {
00367                         int element = atoi(chr+1);
00368                         const int32 *var = *(const int32 **)_vars[i].variable;
00369                         if (element >= _vars[i].arraySize) {
00370                             debugPrintf("%s is out of range (array is %d elements big)\n", param[0], _vars[i].arraySize);
00371                         } else {
00372                             debugPrintf("(int)%s = %d\n", param[0], var[element]);
00373                         }
00374                     }
00375                 }
00376                 break;
00377                 // String
00378                 case DVAR_STRING:
00379                     debugPrintf("(string)%s = %s\n", param[0], ((Common::String *)_vars[i].variable)->c_str());
00380                     break;
00381                 default:
00382                     debugPrintf("%s = (unknown type)\n", param[0]);
00383                     break;
00384                 }
00385             }
00386 
00387             return true;
00388         }
00389     }
00390 
00391     debugPrintf("Unknown command or variable\n");
00392     return true;
00393 }
00394 
00395 void Debugger::splitCommand(Common::String &input, int &argc, const char **argv) {
00396     byte c;
00397     enum states { DULL, IN_WORD, IN_STRING } state = DULL;
00398     const char *paramStart = nullptr;
00399 
00400     argc = 0;
00401     for (Common::String::iterator p = input.begin(); *p; ++p) {
00402         c = (byte)*p;
00403 
00404         switch (state) {
00405         case DULL:
00406             // not in a word, not in a double quoted string
00407             if (isspace(c))
00408                 break;
00409 
00410             // not a space -- if it's a double quote we go to IN_STRING, else to IN_WORD
00411             if (c == '"') {
00412                 state = IN_STRING;
00413                 paramStart = p + 1; // word starts at *next* char, not this one
00414             } else {
00415                 state = IN_WORD;
00416                 paramStart = p;     // word starts here
00417             }
00418             break;
00419 
00420         case IN_STRING:
00421             // we're in a double quoted string, so keep going until we hit a close "
00422             if (c == '"') {
00423                 // Add entire quoted string to parameter list
00424                 *p = '\0';
00425                 argv[argc++] = paramStart;
00426                 state = DULL;   // back to "not in word, not in string" state
00427             }
00428             break;
00429 
00430         case IN_WORD:
00431             // we're in a word, so keep going until we get to a space
00432             if (isspace(c)) {
00433                 *p = '\0';
00434                 argv[argc++] = paramStart;
00435                 state = DULL;   // back to "not in word, not in string" state
00436             }
00437             break;
00438         }
00439     }
00440 
00441     if (state != DULL)
00442         // Add in final parameter
00443         argv[argc++] = paramStart;
00444 }
00445 
00446 // returns true if something has been completed
00447 // completion has to be delete[]-ed then
00448 bool Debugger::tabComplete(const char *input, Common::String &completion) const {
00449     // very basic tab completion
00450     // for now it just supports command completions
00451 
00452     // adding completions of command parameters would be nice (but hard) :-)
00453     // maybe also give a list of possible command completions?
00454     //   (but this will require changes to console)
00455 
00456     if (strchr(input, ' '))
00457         return false; // already finished the first word
00458 
00459     const uint inputlen = strlen(input);
00460 
00461     completion.clear();
00462 
00463     CommandsMap::const_iterator i, e = _cmds.end();
00464     for (i = _cmds.begin(); i != e; ++i) {
00465         if (i->_key.hasPrefixIgnoreCase(input)) {
00466             uint commandlen = i->_key.size();
00467             if (commandlen == inputlen) { // perfect match, so no tab completion possible
00468                 return false;
00469             }
00470             if (commandlen > inputlen) { // possible match
00471                 // no previous match
00472                 if (completion.empty()) {
00473                     completion = i->_key.c_str() + inputlen;
00474                 } else {
00475                     // take common prefix of previous match and this command
00476                     for (uint j = 0; j < completion.size(); j++) {
00477                         if (inputlen + j >= i->_key.size() ||
00478                                 completion[j] != i->_key[inputlen + j]) {
00479                             completion = Common::String(completion.begin(), completion.begin() + j);
00480                             // If there is no unambiguous completion, abort
00481                             if (completion.empty())
00482                                 return false;
00483                             break;
00484                         }
00485                     }
00486                 }
00487             }
00488         }
00489     }
00490     if (completion.empty())
00491         return false;
00492 
00493     return true;
00494 }
00495 
00496 #if defined(USE_TEXT_CONSOLE_FOR_DEBUGGER) && defined(USE_READLINE)
00497 char *Debugger::readlineComplete(const char *input, int state) {
00498     static CommandsMap::const_iterator iter;
00499 
00500     // We assume that _cmds isn't changed between calls to readlineComplete,
00501     // unless state is 0.
00502     if (state == 0) {
00503         iter = _cmds.begin();
00504     } else {
00505         ++iter;
00506     }
00507 
00508     for (; iter != _cmds.end(); ++iter) {
00509         if (iter->_key.hasPrefix(input)) {
00510             char *ret = (char *)malloc(iter->_key.size() + 1);
00511             strcpy(ret, iter->_key.c_str());
00512             return ret;
00513         }
00514     }
00515     return 0;
00516 }
00517 #endif
00518 
00519 // Variable registration function
00520 void Debugger::registerVar(const Common::String &varname, void *pointer, VarType type, int arraySize) {
00521     // TODO: Filter out duplicates
00522     // TODO: Sort this list? Then we can do binary search later on when doing lookups.
00523     assert(pointer);
00524 
00525     Var tmp;
00526     tmp.name = varname;
00527     tmp.type = type;
00528     tmp.variable = pointer;
00529     tmp.arraySize = arraySize;
00530 
00531     _vars.push_back(tmp);
00532 }
00533 
00534 // Command registration function
00535 void Debugger::registerCmd(const Common::String &cmdname, Debuglet *debuglet) {
00536     assert(debuglet && debuglet->isValid());
00537     _cmds[cmdname] = Common::SharedPtr<Debuglet>(debuglet);
00538 }
00539 
00540 
00541 // Detach ("exit") the debugger
00542 bool Debugger::cmdExit(int argc, const char **argv) {
00543     detach();
00544     return false;
00545 }
00546 
00547 // Print a list of all registered commands (and variables, if any),
00548 // nicely word-wrapped.
00549 bool Debugger::cmdHelp(int argc, const char **argv) {
00550     const int charsPerLine = getCharsPerLine();
00551     int width, size;
00552     uint i;
00553 
00554     debugPrintf("Commands are:\n");
00555 
00556     // Obtain a list of sorted command names
00557     Common::Array<Common::String> cmds;
00558     CommandsMap::const_iterator iter, e = _cmds.end();
00559     for (iter = _cmds.begin(); iter != e; ++iter) {
00560         cmds.push_back(iter->_key);
00561     }
00562     sort(cmds.begin(), cmds.end());
00563 
00564     // Print them all
00565     width = 0;
00566     for (i = 0; i < cmds.size(); i++) {
00567         size = cmds[i].size() + 1;
00568 
00569         if ((width + size) >= charsPerLine) {
00570             debugPrintf("\n");
00571             width = size;
00572         } else
00573             width += size;
00574 
00575         debugPrintf("%s ", cmds[i].c_str());
00576     }
00577     debugPrintf("\n");
00578 
00579     if (!_vars.empty()) {
00580         debugPrintf("\n");
00581         debugPrintf("Variables are:\n");
00582         width = 0;
00583         for (i = 0; i < _vars.size(); i++) {
00584             size = _vars[i].name.size() + 1;
00585 
00586             if ((width + size) >= charsPerLine) {
00587                 debugPrintf("\n");
00588                 width = size;
00589             } else
00590                 width += size;
00591 
00592             debugPrintf("%s ", _vars[i].name.c_str());
00593         }
00594         debugPrintf("\n");
00595     }
00596 
00597     return true;
00598 }
00599 
00600 bool Debugger::cmdOpenLog(int argc, const char **argv) {
00601     if (g_system->hasFeature(OSystem::kFeatureDisplayLogFile))
00602         g_system->displayLogFile();
00603     else
00604         debugPrintf("Opening the log file not supported on this system\n");
00605     return true;
00606 }
00607 
00608 #ifndef DISABLE_MD5
00609 struct ArchiveMemberLess {
00610     bool operator()(const Common::ArchiveMemberPtr &x, const Common::ArchiveMemberPtr &y) const {
00611         return (*x).getDisplayName().compareToIgnoreCase((*y).getDisplayName()) < 0;
00612     }
00613 };
00614 
00615 bool Debugger::cmdMd5(int argc, const char **argv) {
00616     if (argc < 2) {
00617         debugPrintf("md5 [-n length] <filename | pattern>\n");
00618     } else {
00619         uint32 length = 0;
00620         uint paramOffset = 0;
00621 
00622         // If the user supplied an -n parameter, set the bytes to read
00623         if (!strcmp(argv[1], "-n")) {
00624             // Make sure that we have at least two more parameters
00625             if (argc < 4) {
00626                 debugPrintf("md5 [-n length] <filename | pattern>\n");
00627                 return true;
00628             }
00629             length = atoi(argv[2]);
00630             paramOffset = 2;
00631         }
00632 
00633         // Assume that spaces are part of a single filename.
00634         Common::String filename = argv[1 + paramOffset];
00635         for (int i = 2 + paramOffset; i < argc; i++) {
00636             filename = filename + " " + argv[i];
00637         }
00638         Common::ArchiveMemberList list;
00639         SearchMan.listMatchingMembers(list, filename);
00640         if (list.empty()) {
00641             debugPrintf("File '%s' not found\n", filename.c_str());
00642         } else {
00643             sort(list.begin(), list.end(), ArchiveMemberLess());
00644             for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) {
00645                 Common::SeekableReadStream *stream = (*iter)->createReadStream();
00646                 Common::String md5 = Common::computeStreamMD5AsString(*stream, length);
00647                 debugPrintf("%s  %s  %d\n", md5.c_str(), (*iter)->getDisplayName().c_str(), stream->size());
00648                 delete stream;
00649             }
00650         }
00651     }
00652     return true;
00653 }
00654 
00655 bool Debugger::cmdMd5Mac(int argc, const char **argv) {
00656     if (argc < 2) {
00657         debugPrintf("md5mac [-n length] <base filename>\n");
00658     } else {
00659         uint32 length = 0;
00660         uint paramOffset = 0;
00661 
00662         // If the user supplied an -n parameter, set the bytes to read
00663         if (!strcmp(argv[1], "-n")) {
00664             // Make sure that we have at least two more parameters
00665             if (argc < 4) {
00666                 debugPrintf("md5mac [-n length] <base filename>\n");
00667                 return true;
00668             }
00669             length = atoi(argv[2]);
00670             paramOffset = 2;
00671         }
00672 
00673         // Assume that spaces are part of a single filename.
00674         Common::String filename = argv[1 + paramOffset];
00675         for (int i = 2 + paramOffset; i < argc; i++) {
00676             filename = filename + " " + argv[i];
00677         }
00678         Common::MacResManager macResMan;
00679         // FIXME: There currently isn't any way to tell the Mac resource
00680         // manager to open a specific file. Instead, it takes a "base name"
00681         // and constructs a file name out of that. While usually a desirable
00682         // thing, it's not ideal here.
00683         if (!macResMan.open(filename)) {
00684             debugPrintf("Resource file '%s' not found\n", filename.c_str());
00685         } else {
00686             if (!macResMan.hasResFork() && !macResMan.hasDataFork()) {
00687                 debugPrintf("'%s' has neither data not resource fork\n", macResMan.getBaseFileName().c_str());
00688             } else {
00689                 // The resource fork is probably the most relevant one.
00690                 if (macResMan.hasResFork()) {
00691                     Common::String md5 = macResMan.computeResForkMD5AsString(length);
00692                     debugPrintf("%s  %s (resource)  %d\n", md5.c_str(), macResMan.getBaseFileName().c_str(), macResMan.getResForkDataSize());
00693                 }
00694                 if (macResMan.hasDataFork()) {
00695                     Common::SeekableReadStream *stream = macResMan.getDataFork();
00696                     Common::String md5 = Common::computeStreamMD5AsString(*stream, length);
00697                     debugPrintf("%s  %s (data)  %d\n", md5.c_str(), macResMan.getBaseFileName().c_str(), stream->size());
00698                 }
00699             }
00700             macResMan.close();
00701         }
00702     }
00703     return true;
00704 }
00705 #endif
00706 
00707 bool Debugger::cmdDebugLevel(int argc, const char **argv) {
00708     if (argc == 1) { // print level
00709         debugPrintf("Debugging is currently %s (set at level %d)\n", (gDebugLevel >= 0) ? "enabled" : "disabled", gDebugLevel);
00710         debugPrintf("Usage: %s <n> where n is 0 to 10 or -1 to disable debugging\n", argv[0]);
00711     } else { // set level
00712         gDebugLevel = atoi(argv[1]);
00713         if (gDebugLevel >= 0 && gDebugLevel < 11) {
00714             debugPrintf("Debug level set to level %d\n", gDebugLevel);
00715         } else if (gDebugLevel < 0) {
00716             debugPrintf("Debugging is now disabled\n");
00717         } else {
00718             debugPrintf("Invalid debug level value\n");
00719             debugPrintf("Usage: %s <n> where n is 0 to 10 or -1 to disable debugging\n", argv[0]);
00720         }
00721     }
00722 
00723     return true;
00724 }
00725 
00726 bool Debugger::cmdDebugFlagsList(int argc, const char **argv) {
00727     const Common::DebugManager::DebugChannelList &debugLevels = DebugMan.listDebugChannels();
00728 
00729     debugPrintf("Engine debug levels:\n");
00730     debugPrintf("--------------------\n");
00731     if (debugLevels.empty()) {
00732         debugPrintf("No engine debug levels\n");
00733         return true;
00734     }
00735     for (Common::DebugManager::DebugChannelList::const_iterator i = debugLevels.begin(); i != debugLevels.end(); ++i) {
00736         debugPrintf("%c%s - %s (%s)\n", i->enabled ? '+' : ' ',
00737                 i->name.c_str(), i->description.c_str(),
00738                 i->enabled ? "enabled" : "disabled");
00739     }
00740     debugPrintf("\n");
00741     return true;
00742 }
00743 
00744 bool Debugger::cmdDebugFlagEnable(int argc, const char **argv) {
00745     if (argc < 2) {
00746         debugPrintf("debugflag_enable [<flag> | all]\n");
00747     } else {
00748         if (!scumm_stricmp(argv[1], "all")) {
00749             debugPrintf("Enabled all debug flags\n");
00750             DebugMan.enableAllDebugChannels();
00751         } else if (DebugMan.enableDebugChannel(argv[1])) {
00752             debugPrintf("Enabled debug flag '%s'\n", argv[1]);
00753         } else {
00754             debugPrintf("Failed to enable debug flag '%s'\n", argv[1]);
00755         }
00756     }
00757     return true;
00758 }
00759 
00760 bool Debugger::cmdDebugFlagDisable(int argc, const char **argv) {
00761     if (argc < 2) {
00762         debugPrintf("debugflag_disable [<flag> | all]\n");
00763     } else {
00764         if (!scumm_stricmp(argv[1], "all")) {
00765             debugPrintf("Disabled all debug flags\n");
00766             DebugMan.disableAllDebugChannels();
00767         } else if (DebugMan.disableDebugChannel(argv[1])) {
00768             debugPrintf("Disabled debug flag '%s'\n", argv[1]);
00769         } else {
00770             debugPrintf("Failed to disable debug flag '%s'\n", argv[1]);
00771         }
00772     }
00773     return true;
00774 }
00775 
00776 // Console handler
00777 #ifndef USE_TEXT_CONSOLE_FOR_DEBUGGER
00778 bool Debugger::debuggerInputCallback(GUI::ConsoleDialog *console, const char *input, void *refCon) {
00779     Debugger *debugger = (Debugger *)refCon;
00780 
00781     return debugger->parseCommand(input);
00782 }
00783 
00784 
00785 bool Debugger::debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, Common::String &completion, void *refCon) {
00786     Debugger *debugger = (Debugger *)refCon;
00787 
00788     return debugger->tabComplete(input, completion);
00789 }
00790 
00791 #endif
00792 
00793 } // End of namespace GUI


Generated on Sat Feb 16 2019 05:00:49 for ResidualVM by doxygen 1.7.1
curved edge   curved edge