Logo Search packages:      
Sourcecode: scummvm version File versions

debugger.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2006 The ScummVM project
 *
 * 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.
 *
 * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-9-1/common/debugger.cpp $
 * $Id: debugger.cpp 20695 2006-02-14 23:31:25Z lordhoto $
 *
 */

#include "common/stdafx.h"

#include "common/debugger.h"
#include "common/system.h"

#if USE_CONSOLE
      #include "gui/console.h"
#endif

namespace Common {

template <class T>
Debugger<T>::Debugger() {
      _frame_countdown = 0;
      _dvar_count = 0;
      _dcmd_count = 0;
      _detach_now = false;
      _isAttached = false;
      _errStr = NULL;
      _firstTime = true;
      _debuggerDialog = new GUI::ConsoleDialog(1.0, 0.67F);
      _debuggerDialog->setInputCallback(debuggerInputCallback, this);
      _debuggerDialog->setCompletionCallback(debuggerCompletionCallback, this);

      DCmd_Register("debugflag_list",                 &Debugger<T>::Cmd_DebugFlagsList);
      DCmd_Register("debugflag_enable",         &Debugger<T>::Cmd_DebugFlagEnable);
      DCmd_Register("debugflag_disable",        &Debugger<T>::Cmd_DebugFlagDisable);
}

template <class T>
Debugger<T>::~Debugger() {
      delete _debuggerDialog;
}


// Initialisation Functions
template <class T>
int Debugger<T>::DebugPrintf(const char *format, ...) {
      va_list     argptr;

      va_start(argptr, format);
      int count;
#if USE_CONSOLE
      count = _debuggerDialog->vprintf(format, argptr);
#else
      count = ::vprintf(format, argptr);
#endif
      va_end (argptr);
      return count;
}

#ifndef __SYMBIAN32__ // gcc/UIQ doesn't like the debugger code for some reason? Actually get a cc1plus core dump here :)
template <class T>
void Debugger<T>::attach(const char *entry) {

      g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);

      if (entry) {
            _errStr = strdup(entry);
      }

      _frame_countdown = 1;
      _detach_now = false;
      _isAttached = true;
}

template <class T>
void Debugger<T>::detach() {
      g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);

      _detach_now = false;
      _isAttached = false;
}

// Temporary execution handler
template <class T>
void Debugger<T>::onFrame() {
      if (_frame_countdown == 0)
            return;
      --_frame_countdown;

      if (!_frame_countdown) {

            preEnter();
            enter();
            postEnter();

            // Detach if we're finished with the debugger
            if (_detach_now)
                  detach();
      }
}
#endif // of ifndef __SYMBIAN32__ // gcc/UIQ doesn't like the debugger code for some reason? Actually get a cc1plus core dump here :)

// Main Debugger Loop
template <class T>
void Debugger<T>::enter() {
#if USE_CONSOLE
      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");
            _firstTime = false;
      }

      if (_errStr) {
            DebugPrintf("ERROR: %s\n\n", _errStr);
            free(_errStr);
            _errStr = NULL;
      }

      _debuggerDialog->runModal();
#else
      // TODO: compared to the console input, this here is very bare bone.
      // For example, no support for tab completion and no history. At least
      // we should re-add (optional) support for the readline library.
      // Or maybe instead of choosing between a console dialog and stdio,
      // we should move that choice into the ConsoleDialog class - that is,
      // the console dialog code could be #ifdef'ed to not print to the dialog
      // but rather to stdio. This way, we could also reuse the command history
      // and tab completion of the console. It would still require a lot of
      // work, but at least no dependency on a 3rd party library...

      printf("Debugger entered, please switch to this console for input.\n");

      int i;
      char buf[256];

      do {
            printf("debug> ");
            if (!fgets(buf, sizeof(buf), stdin))
                  return;

            i = strlen(buf);
            while (i > 0 && buf[i - 1] == '\n')
                  buf[--i] = 0;

            if (i == 0)
                  continue;
      } while (RunCommand(buf));

#endif
}

// Command execution loop
template <class T>
bool Debugger<T>::RunCommand(const char *inputOrig) {
      int i = 0, num_params = 0;
      const char *param[256];
      char *input = strdup(inputOrig);    // One of the rare occasions using strdup is OK (although avoiding strtok might be more elegant here).

      // Parse out any params
      char *tok = strtok(input, " ");
      if (tok) {
            do {
                  param[num_params++] = tok;
            } while ((tok = strtok(NULL, " ")) != NULL);
      } else {
            param[num_params++] = input;
      }

      for (i=0; i < _dcmd_count; i++) {
            if (!strcmp(_dcmds[i].name, param[0])) {
                  bool result = (((T *)this)->*_dcmds[i].function)(num_params, param);
                  free(input);
                  return result;
            }
      }

      // It's not a command, so things get a little tricky for variables. Do fuzzy matching to ignore things like subscripts.
      for (i = 0; i < _dvar_count; i++) {
            if (!strncmp(_dvars[i].name, param[0], strlen(_dvars[i].name))) {
                  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) {
                        // Integer
                        case DVAR_BYTE:
                              *(byte *)_dvars[i].variable = atoi(param[1]);
                              DebugPrintf("byte%s = %d\n", param[0], *(byte *)_dvars[i].variable);
                              break;
                        case DVAR_INT:
                              *(int32 *)_dvars[i].variable = atoi(param[1]);
                              DebugPrintf("(int)%s = %d\n", param[0], *(int32 *)_dvars[i].variable);
                              break;
                        // Integer Array
                        case DVAR_INTARRAY: {
                              char *chr = (char *)strchr(param[0], '[');
                              if (!chr) {
                                    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].optional) {
                                          DebugPrintf("%s is out of range (array is %d elements big)\n", param[0], _dvars[i].optional);
                                    } else {
                                          var[element] = atoi(param[1]);
                                          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, param[1]);
                              break;
                        }
                  } else {
                        // And again, type-dependent prints/defrefs. The array one is still ugly.
                        switch(_dvars[i].type) {
                        // Integer
                        case DVAR_BYTE:
                              DebugPrintf("(byte)%s = %d\n", param[0], *(const byte *)_dvars[i].variable);
                              break;
                        case DVAR_INT:
                              DebugPrintf("(int)%s = %d\n", param[0], *(const int32 *)_dvars[i].variable);
                              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]);
                              } else {
                                    int element = atoi(chr+1);
                                    const int32 *var = *(const int32 **)_dvars[i].variable;
                                    if (element >= _dvars[i].optional) {
                                          DebugPrintf("%s is out of range (array is %d elements big)\n", param[0], _dvars[i].optional);
                                    } else {
                                          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());
                              break;
                        default:
                              DebugPrintf("%s = (unknown type)\n", param[0]);
                              break;
                        }
                  }

                  free(input);
                  return true;
            }
      }

      DebugPrintf("Unknown command or variable\n");
      free(input);
      return true;
}

// returns true if something has been completed
// completion has to be delete[]-ed then
template <class T>
bool Debugger<T>::TabComplete(const char *input, char*& completion) {
      // very basic tab completion
      // for now it just supports command completions

      // adding completions of command parameters would be nice (but hard) :-)
      // maybe also give a list of possible command completions?
      //   (but this will require changes to console)

      if (strchr(input, ' '))
            return false; // already finished the first word

      unsigned int inputlen = strlen(input);

      unsigned int matchlen = 0;
      char match[30]; // the max. command name is 30 chars

      for (int i = 0; i < _dcmd_count; i++) {
            if (!strncmp(_dcmds[i].name, input, inputlen)) {
                  unsigned int commandlen = strlen(_dcmds[i].name);
                  if (commandlen == inputlen) { // perfect match
                        return false;
                  }
                  if (commandlen > inputlen) { // possible match
                        // no previous match
                        if (matchlen == 0) {
                              strcpy(match, _dcmds[i].name + inputlen);
                              matchlen = commandlen - inputlen;
                        } else {
                              // take common prefix of previous match and this command
                              unsigned int j;
                              for (j = 0; j < matchlen; j++) {
                                    if (match[j] != _dcmds[i].name[inputlen + j]) break;
                              }
                              matchlen = j;
                        }
                        if (matchlen == 0)
                              return false;
                  }
            }
      }
      if (matchlen == 0)
            return false;

      completion = new char[matchlen + 1];
      memcpy(completion, match, matchlen);
      completion[matchlen] = 0;
      return true;
}

// Variable registration function
template <class T>
void Debugger<T>::DVar_Register(const char *varname, void *pointer, int type, int optional) {
      assert(_dvar_count < ARRAYSIZE(_dvars));
      strcpy(_dvars[_dvar_count].name, varname);
      _dvars[_dvar_count].type = type;
      _dvars[_dvar_count].variable = pointer;
      _dvars[_dvar_count].optional = optional;

      _dvar_count++;
}

// Command registration function
template <class T>
void Debugger<T>::DCmd_Register(const char *cmdname, DebugProc pointer) {
      assert(_dcmd_count < ARRAYSIZE(_dcmds));
      strcpy(_dcmds[_dcmd_count].name, cmdname);
      _dcmds[_dcmd_count].function = pointer;

      _dcmd_count++;
}

template <class T>
bool Debugger<T>::Cmd_DebugFlagsList(int argc, const char **argv) {
      const Common::Array<Common::EngineDebugLevel> &debugLevels = Common::listSpecialDebugLevels();

      DebugPrintf("Engine debug levels:\n");
      DebugPrintf("--------------------\n");
      if (!debugLevels.size()) {
            DebugPrintf("No engine debug levels\n");
            return true;
      }
      for (uint i = 0; i < debugLevels.size(); ++i) {
            DebugPrintf("'%s' - Description: %s\n", debugLevels[i].option.c_str(), debugLevels[i].description.c_str());
      }
      DebugPrintf("\n");
      return true;
}

template <class T>
bool Debugger<T>::Cmd_DebugFlagEnable(int argc, const char **argv) {
      if (argc < 2) {
            DebugPrintf("debugflag_enable <flag>\n");
      } else {
            if (Common::enableSpecialDebugLevel(argv[1])) {
                  DebugPrintf("Enabled debug flag '%s'\n", argv[1]);
            } else {
                  DebugPrintf("Failed to enable debug flag '%s'\n", argv[1]);
            }
      }
      return true;
}

template <class T>
bool Debugger<T>::Cmd_DebugFlagDisable(int argc, const char **argv) {
      if (argc < 2) {
            DebugPrintf("debugflag_disable <flag>\n");
      } else {
            if (Common::disableSpecialDebugLevel(argv[1])) {
                  DebugPrintf("Disabled debug flag '%s'\n", argv[1]);
            } else {
                  DebugPrintf("Failed to disable debug flag '%s'\n", argv[1]);
            }
      }
      return true;
}

// Console handler
#if USE_CONSOLE
template <class T>
bool Debugger<T>::debuggerInputCallback(GUI::ConsoleDialog *console, const char *input, void *refCon) {
      Debugger *debugger = (Debugger *)refCon;

      return debugger->RunCommand(input);
}


template <class T>
bool Debugger<T>::debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, char*& completion, void *refCon) {
      Debugger *debugger = (Debugger *)refCon;

      return debugger->TabComplete(input, completion);
}

#endif

}     // End of namespace Common

Generated by  Doxygen 1.6.0   Back to index