scummvm/engines/glk/alan3/debug.cpp
Orgad Shaneh 0111a08560 GLK: Use nullptr
Using clang-tidy modernize-use-nullptr
2021-11-14 15:51:59 +02:00

1111 lines
30 KiB
C++

/* 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 "glk/alan3/debug.h"
#include "glk/alan3/alan3.h"
#include "glk/alan3/class.h"
#include "glk/alan3/sysdep.h"
#include "glk/alan3/alan_version.h"
#include "glk/alan3/compatibility.h"
#include "glk/alan3/current.h"
#include "glk/alan3/event.h"
#include "glk/alan3/exe.h"
#include "glk/alan3/glkio.h"
#include "glk/alan3/instance.h"
#include "glk/alan3/inter.h"
#include "glk/alan3/lists.h"
#include "glk/alan3/memory.h"
#include "glk/alan3/options.h"
#include "glk/alan3/output.h"
#include "glk/alan3/sysdep.h"
#include "glk/alan3/utils.h"
#include "glk/streams.h"
namespace Glk {
namespace Alan3 {
#define BREAKPOINTMAX 50
/* PUBLIC: */
int breakpointCount = 0;
Breakpoint breakpoint[BREAKPOINTMAX];
#define debugPrefix "adbg: "
/*----------------------------------------------------------------------*/
static void showAttributes(AttributeEntry *attrib) {
AttributeEntry *at;
int i;
char str[80];
if (attrib == nullptr)
return;
i = 1;
for (at = attrib; !isEndOfArray(at); at++) {
sprintf(str, "$i$t%s[%d] = %d", (char *) pointerTo(at->id), at->code, (int)at->value);
output(str);
i++;
}
}
/*----------------------------------------------------------------------*/
static void showContents(CONTEXT, int cnt) {
uint i;
char str[80];
Abool found = FALSE;
output("$iContains:");
for (i = 1; i <= header->instanceMax; i++) {
if (isIn(i, cnt, DIRECT)) { /* Yes, it's directly in this container */
if (!found)
found = TRUE;
output("$i$t");
say(context, i);
sprintf(str, "[%d] ", i);
output(str);
}
}
if (!found)
output("nothing");
}
/*----------------------------------------------------------------------*/
static char *idOfInstance(CONTEXT, int instance) {
int base = header->instanceTableAddress +
header->instanceMax * sizeof(InstanceEntry) / sizeof(Aword) + 1;
return (char *)&memory[memory[base + instance - 1]];
}
/*----------------------------------------------------------------------*/
static void sayInstanceNumberAndName(CONTEXT, int ins) {
char buf[1000];
sprintf(buf, "[%d] %s (\"$$", ins, idOfInstance(context, ins));
output(buf);
say(context, ins);
output("$$\")");
}
/*----------------------------------------------------------------------*/
static void sayLocationOfInstance(CONTEXT, int ins, const char *prefix) {
if (admin[ins].location == 0)
return;
else {
output(prefix);
if (isALocation(admin[ins].location)) {
output("at");
CALL1(sayInstanceNumberAndName, admin[ins].location)
CALL2(sayLocationOfInstance, admin[ins].location, prefix)
} else if (isAContainer(admin[ins].location)) {
if (isAObject(admin[ins].location))
output("in");
else if (isAActor(admin[ins].location))
output("carried by");
CALL1(sayInstanceNumberAndName, admin[ins].location)
CALL2(sayLocationOfInstance, admin[ins].location, prefix)
} else {
output("Illegal location!");
}
}
}
/*----------------------------------------------------------------------*/
static void listInstance(CONTEXT, int ins) {
output("$i");
CALL1(sayInstanceNumberAndName, ins)
if (instances[ins].container)
output("(container)");
CALL2(sayLocationOfInstance, ins, ", ")
}
/*----------------------------------------------------------------------*/
static void listInstances(CONTEXT, char *pattern) {
uint ins;
bool found = FALSE;
for (ins = 1; ins <= header->instanceMax; ins++) {
if (pattern == nullptr || (pattern != nullptr && match(pattern, idOfInstance(context, ins)))) {
if (!found) {
output("Instances:");
found = TRUE;
}
CALL1(listInstance, ins)
}
}
if (pattern != nullptr && !found)
output("No instances matched the pattern.");
}
/*----------------------------------------------------------------------*/
static void showInstance(CONTEXT, int ins) {
char str[80];
if (ins > (int)header->instanceMax || ins < 1) {
sprintf(str, "Instance index %d is out of range.", ins);
output(str);
return;
}
output("The");
CALL1(sayInstanceNumberAndName, ins)
if (instances[ins].parent) {
sprintf(str, "Isa %s[%d]", idOfClass(instances[ins].parent), instances[ins].parent);
output(str);
}
if (!isA(ins, header->locationClassId) || (isA(ins, header->locationClassId) && admin[ins].location != 0)) {
sprintf(str, "$iLocation:");
output(str);
needSpace = TRUE;
CALL2(sayLocationOfInstance, ins, "")
}
output("$iAttributes:");
showAttributes(admin[ins].attributes);
if (instances[ins].container)
CALL1(showContents, ins)
if (isA(ins, header->actorClassId)) {
if (admin[ins].script == 0)
output("$iIs idle");
else {
sprintf(str, "$iExecuting script: %d, Step: %d", admin[ins].script, admin[ins].step);
output(str);
}
}
}
/*----------------------------------------------------------------------*/
static void listObjects(CONTEXT) {
uint obj;
output("Objects:");
for (obj = 1; obj <= header->instanceMax; obj++)
if (isAObject(obj))
CALL1(listInstance, obj)
}
/*----------------------------------------------------------------------*/
static void showObject(CONTEXT, int obj) {
char str[80];
if (!isAObject(obj)) {
sprintf(str, "Instance %d is not an object", obj);
output(str);
return;
}
CALL1(showInstance, obj)
}
/*----------------------------------------------------------------------*/
static int sourceFileNumber(char *fileName) {
SourceFileEntry *entries = (SourceFileEntry *)pointerTo(header->sourceFileTable);
int n;
for (n = 0; * (Aword *)&entries[n] != EOD; n++) {
char *entryName;
entryName = getStringFromFile(entries[n].fpos, entries[n].len);
if (strcmp(entryName, fileName) == 0) return n;
entryName = baseNameStart(entryName);
if (strcmp(entryName, fileName) == 0) return n;
}
return -1;
}
/*----------------------------------------------------------------------*/
static void printClassName(int c) {
output(idOfClass(c));
}
/*----------------------------------------------------------------------*/
static void showClassInheritance(int c) {
char str[80];
if (classes[c].parent != 0) {
output(", Isa");
printClassName(classes[c].parent);
sprintf(str, "[%d]", classes[c].parent);
output(str);
}
}
/*----------------------------------------------------------------------*/
static void showClass(int cla) {
char str[80];
if (cla < 1) {
sprintf(str, "Class index %d is out of range.", cla);
output(str);
return;
}
output("$t");
printClassName(cla);
sprintf(str, "[%d]", cla);
output(str);
showClassInheritance(cla);
}
/*----------------------------------------------------------------------*/
static void listClass(int c) {
char str[80];
sprintf(str, "%3d: ", c);
output(str);
printClassName(c);
showClassInheritance(c);
}
/*----------------------------------------------------------------------*/
static void showClassHierarchy(int thisItem, int depth) {
int i;
uint child;
output("$i");
for (i = 0; i < depth; i++)
output("$t");
listClass(thisItem);
for (child = 1; child <= header->classMax; child++) {
if (classes[child].parent == thisItem) {
showClassHierarchy(child, depth + 1);
}
}
}
/*----------------------------------------------------------------------*/
static void listLocations(CONTEXT) {
uint loc;
output("Locations:");
for (loc = 1; loc <= header->instanceMax; loc++)
if (isALocation(loc))
listInstance(context, loc);
}
/*----------------------------------------------------------------------*/
static void showLocation(CONTEXT, int loc) {
char str[80];
if (!isALocation(loc)) {
sprintf(str, "Instance %d is not a location.", loc);
output(str);
return;
}
output("The ");
CALL1(say, loc)
sprintf(str, "(%d) Isa location :", loc);
output(str);
output("$iAttributes =");
showAttributes(admin[loc].attributes);
}
/*----------------------------------------------------------------------*/
static void listActors(CONTEXT) {
uint act;
output("Actors:");
for (act = 1; act <= header->instanceMax; act++)
if (isAActor(act))
CALL1(listInstance, act)
}
/*----------------------------------------------------------------------*/
static void showActor(CONTEXT, int act) {
char str[80];
if (!isAActor(act)) {
sprintf(str, "Instance %d is not an actor.", act);
output(str);
return;
}
CALL1(showInstance, act)
}
/*----------------------------------------------------------------------*/
static void showEvents(CONTEXT) {
uint event;
int i;
char str[80];
bool scheduled;
output("Events:");
for (event = 1; event <= header->eventMax; event++) {
sprintf(str, "$i%d [%s]:", event, (char *)pointerTo(events[event].id));
output(str);
scheduled = FALSE;
for (i = 0; i < eventQueueTop; i++)
if ((scheduled = (eventQueue[i].event == (int)event)))
break;
if (scheduled) {
sprintf(str, "Scheduled for +%d, at ", eventQueue[i].after);
output(str);
CALL1(say, eventQueue[i].where)
} else
output("Not scheduled.");
}
}
/*======================================================================*/
char *sourceFileName(int fileNumber) {
SourceFileEntry *entries = (SourceFileEntry *)pointerTo(header->sourceFileTable);
return getStringFromFile(entries[fileNumber].fpos, entries[fileNumber].len);
}
/*======================================================================*/
bool readLine(Common::SeekableReadStream *rs, char *line, int maxLen) {
if (rs->pos() < rs->size()) {
line[maxLen - 1] = '\0';
char c;
do {
c = rs->readByte();
*line++ = c;
} while (--maxLen > 1);
}
return rs->pos() < rs->size();
}
/*======================================================================*/
char *readSourceLine(int file, int line) {
int count;
#define SOURCELINELENGTH 1000
static char buffer[SOURCELINELENGTH];
frefid_t sourceFileRef = g_vm->glk_fileref_create_by_name(fileusage_TextMode, sourceFileName(file), 0);
strid_t sourceFile = g_vm->glk_stream_open_file(sourceFileRef, filemode_Read, 0);
if (sourceFile != nullptr) {
for (count = 0; count < line; count++) {
if (!readLine(*sourceFile, buffer, SOURCELINELENGTH))
return nullptr;
// If not read the whole line, or no newline, try to read again
while (strchr(buffer, '\n') == nullptr) {
if (!readLine(*sourceFile, buffer, SOURCELINELENGTH))
break;
}
}
delete sourceFile;
return buffer;
}
return nullptr;
}
/*======================================================================*/
void showSourceLine(int fileNumber, int line) {
char *buffer = readSourceLine(fileNumber, line);
if (buffer != nullptr) {
if (buffer[strlen(buffer) - 1] == '\n')
buffer[strlen(buffer) - 1] = '\0';
printf("<%05d>: %s", line, buffer);
}
}
/*----------------------------------------------------------------------*/
static void listFiles() {
SourceFileEntry *entry;
int i = 0;
for (entry = (SourceFileEntry *)pointerTo(header->sourceFileTable); * ((Aword *)entry) != EOD; entry++) {
printf(" %2d : %s\n", i, sourceFileName(i));
i++;
}
}
/*----------------------------------------------------------------------*/
static int findSourceLineIndex(SourceLineEntry *entry, int file, int line) {
/* Will return index to the closest line available */
int i = 0;
while (!isEndOfArray(&entry[i]) && entry[i].file != file)
i++;
while (!isEndOfArray(&entry[i]) && entry[i].file == file && entry[i].line < line)
i++;
if (isEndOfArray(entry) || entry[i].file != file)
return i - 1;
else
return i;
}
/*----------------------------------------------------------------------*/
static void listBreakpoints() {
int i;
bool found = FALSE;
for (i = 0; i < BREAKPOINTMAX; i++)
if (breakpoint[i].line != 0) {
if (!found)
printf("Breakpoints set:\n");
found = TRUE;
printf(" %s:%d\n", sourceFileName(breakpoint[i].file), breakpoint[i].line);
}
if (!found)
printf("No breakpoints set\n");
}
/*======================================================================*/
int breakpointIndex(int file, int line) {
int i;
for (i = 0; i < BREAKPOINTMAX; i++)
if (breakpoint[i].line == line && breakpoint[i].file == file)
return i;
return -1;
}
/*----------------------------------------------------------------------*/
static int availableBreakpointSlot() {
int i;
for (i = 0; i < BREAKPOINTMAX; i++)
if (breakpoint[i].line == 0)
return i;
return -1;
}
/*----------------------------------------------------------------------*/
static void setBreakpoint(int file, int line) {
int i = breakpointIndex(file, line);
if (i != -1)
printf("Breakpoint already set at %s:%d\n", sourceFileName(file), line);
else {
i = availableBreakpointSlot();
if (i == -1)
printf("No room for more breakpoints. Delete one first.\n");
else {
int lineIndex = findSourceLineIndex((SourceLineEntry *)pointerTo(header->sourceLineTable), file, line);
SourceLineEntry *entry = (SourceLineEntry *)pointerTo(header->sourceLineTable);
char leadingText[100] = "Breakpoint";
if (entry[lineIndex].file == (Aint)EOD) {
printf("Line %d not available\n", line);
} else {
if (entry[lineIndex].line != line)
sprintf(leadingText, "Line %d not available, breakpoint instead", line);
breakpoint[i].file = entry[lineIndex].file;
breakpoint[i].line = entry[lineIndex].line;
printf("%s set at %s:%d\n", leadingText, sourceFileName(entry[lineIndex].file), entry[lineIndex].line);
showSourceLine(entry[lineIndex].file, entry[lineIndex].line);
printf("\n");
}
}
}
}
/*----------------------------------------------------------------------*/
static void deleteBreakpoint(int line, int file) {
int i = breakpointIndex(file, line);
if (i == -1)
printf("No breakpoint set at %s:%d\n", sourceFileName(file), line);
else {
breakpoint[i].line = 0;
printf("Breakpoint at %s:%d deleted\n", sourceFileName(file), line);
}
}
static bool saved_traceSection, saved_traceInstruction, saved_capitilize, saved_tracePush, saved_traceStack, saved_traceSource;
static int loc;
/*======================================================================*/
void saveInfo(void) {
/* Save some important things */
saved_capitilize = capitalize;
capitalize = FALSE;
saved_traceSection = traceSectionOption;
traceSectionOption = FALSE;
saved_traceSource = traceSourceOption;
traceSourceOption = FALSE;
saved_traceInstruction = traceInstructionOption;
traceInstructionOption = FALSE;
saved_tracePush = tracePushOption;
tracePushOption = FALSE;
saved_traceStack = traceStackOption;
traceStackOption = FALSE;
loc = current.location;
current.location = where(HERO, DIRECT);
}
/*======================================================================*/
void restoreInfo(void) {
/* Restore! */
capitalize = saved_capitilize;
traceSectionOption = saved_traceSection;
traceInstructionOption = saved_traceInstruction;
traceSourceOption = saved_traceSource;
tracePushOption = saved_tracePush;
traceStackOption = saved_traceStack;
current.location = loc;
}
#define HELP_COMMAND 'H'
#define QUIT_COMMAND 'Q'
#define EXIT_COMMAND 'X'
#define GO_COMMAND 'G'
#define FILES_COMMAND 'F'
#define INSTANCES_COMMAND 'I'
#define CLASSES_COMMAND 'C'
#define OBJECTS_COMMAND 'O'
#define ACTORS_COMMAND 'A'
#define LOCATIONS_COMMAND 'L'
#define EVENTS_COMMAND 'E'
#define BREAK_COMMAND 'B'
#define DELETE_COMMAND 'D'
#define TRACE_COMMAND 'R'
#define SECTION_TRACE_COMMAND 'T'
#define INSTRUCTION_TRACE_COMMAND 'S'
#define NEXT_COMMAND 'N'
#define UNKNOWN_COMMAND '?'
#define AMBIGUOUS_COMMAND '-'
#define TRACE_SOURCE_COMMAND 's'
#define TRACE_SECTION_COMMAND 'e'
#define TRACE_INSTRUCTION_COMMAND 'i'
#define TRACE_PUSH_COMMAND 'p'
#define TRACE_STACK_COMMAND 't'
typedef struct DebugParseEntry {
const char *command;
const char *parameter;
char code;
const char *helpText;
} DebugParseEntry;
static const DebugParseEntry commandEntries[] = {
{"help", "", HELP_COMMAND, "this help"},
{"?", "", HELP_COMMAND, "d:o"},
{"break", "[[file:]n]", BREAK_COMMAND, "set breakpoint at source line [n] (optionally in [file])"},
{"delete", "[[file:]n]", DELETE_COMMAND, "delete breakpoint at source line [n] (optionally in [file])"},
{"files", "", FILES_COMMAND, "list source files"},
{"events", "", EVENTS_COMMAND, "list events"},
{"classes", "", CLASSES_COMMAND, "list class hierarchy"},
{"instances", "[n]", INSTANCES_COMMAND, "list instance(s), all, wildcard, number or name"},
{"objects", "[n]", OBJECTS_COMMAND, "list instance(s) that are objects"},
{"actors", "[n]", ACTORS_COMMAND, "list instance(s) that are actors"},
{"locations", "[n]", LOCATIONS_COMMAND, "list instances that are locations"},
{"trace", "('source'|'section'|'instruction'|'push'|'stack')", TRACE_COMMAND, "toggle various traces"},
{"next", "", NEXT_COMMAND, "run game and stop at next source line"},
{"go", "", GO_COMMAND, "go another player turn"},
{"exit", "", EXIT_COMMAND, "exit to game, enter 'debug' to get back"},
{"x", "", EXIT_COMMAND, "d:o"},
{"quit", "", QUIT_COMMAND, "quit game"},
{nullptr, nullptr, '\0', nullptr}
};
static const DebugParseEntry traceSubcommand[] = {
{"source", "", TRACE_SOURCE_COMMAND, ""},
{"section", "", TRACE_SECTION_COMMAND, ""},
{"instructions", "", TRACE_INSTRUCTION_COMMAND, ""},
{"pushs", "", TRACE_PUSH_COMMAND, ""},
{"stacks", "", TRACE_STACK_COMMAND, ""},
{nullptr, nullptr, '\0', nullptr}
};
static char *spaces(int length) {
static char buf[200];
int i;
for (i = 0; i < length; i++)
buf[i] = ' ';
buf[i] = '\0';
return buf;
}
/*----------------------------------------------------------------------*/
static char *padding(const DebugParseEntry *entry, int maxLength) {
return spaces(maxLength - strlen(entry->command) - strlen(entry->parameter));
}
/*----------------------------------------------------------------------*/
static void handleHelpCommand() {
if (!regressionTestOption)
output(alan.longHeader);
const DebugParseEntry *entry = commandEntries;
int maxLength = 0;
for (entry = commandEntries; entry->command != nullptr; entry++) {
if (strlen(entry->command) + strlen(entry->parameter) > (uint)maxLength)
maxLength = strlen(entry->command) + strlen(entry->parameter);
}
output("$nADBG Commands (can be abbreviated):");
for (entry = commandEntries; entry->command != nullptr; entry++) {
char buf[200];
sprintf(buf, "$i%s %s %s$n$t$t-- %s", entry->command, entry->parameter, padding(entry, maxLength), entry->helpText);
output(buf);
}
}
/*----------------------------------------------------------------------*/
static const DebugParseEntry *findEntry(char *command, const DebugParseEntry *entry) {
while (entry->command != nullptr) {
if (scumm_strnicmp(command, entry->command, strlen(command)) == 0)
return entry;
entry++;
}
return nullptr;
}
/*----------------------------------------------------------------------*/
static char parseDebugCommand(char *command) {
const DebugParseEntry *entry = findEntry(command, commandEntries);
if (entry != nullptr) {
if (strlen(command) < strlen(entry->command)) {
/* See if there are any more partial matches */
if (findEntry(command, entry + 1) != nullptr)
/* TODO: we should list the possible matches somehow */
return AMBIGUOUS_COMMAND;
}
return entry->code;
} else
return UNKNOWN_COMMAND;
}
/*----------------------------------------------------------------------*/
static void readCommand(CONTEXT, char buf[], size_t maxLen) {
char c;
bool flag;
capitalize = FALSE;
if (anyOutput) newline();
do {
output("adbg> ");
FUNC2(g_io->readLine, flag, buf, maxLen)
if (!flag) {
newline();
CALL0(quitGame)
}
lin = 1;
c = buf[0];
} while (c == '\0');
}
/*----------------------------------------------------------------------*/
static void displaySourceLocation(int line, int fileNumber) {
const char *cause;
if (anyOutput) newline();
if (breakpointIndex(fileNumber, line) != -1)
cause = "Breakpoint hit at";
else
cause = "Stepping to";
printf("%s %s %s:%d\n", debugPrefix, cause, sourceFileName(fileNumber), line);
showSourceLine(fileNumber, line);
printf("\n");
anyOutput = FALSE;
}
/*----------------------------------------------------------------------*/
static void toggleSectionTrace() {
if ((saved_traceSection = !saved_traceSection))
printf("Section trace on.");
else
printf("Section trace off.");
}
/*----------------------------------------------------------------------*/
static void toggleInstructionTrace() {
if ((saved_traceInstruction = !saved_traceInstruction))
printf("Single instruction trace on.");
else
printf("Single instruction trace off.");
}
/*----------------------------------------------------------------------*/
static void toggleSourceTrace() {
if ((saved_traceSource = !saved_traceSource))
printf("Source code trace on.");
else
printf("Source code trace off.");
}
/*----------------------------------------------------------------------*/
static void togglePushTrace() {
if ((saved_tracePush = !saved_tracePush))
printf("Stack Push trace on.");
else
printf("Stack Push trace off.");
}
/*----------------------------------------------------------------------*/
static void toggleStackTrace() {
if ((saved_traceStack = !saved_traceStack))
printf("Full stack trace on.");
else
printf("Full stack trace off.");
}
/*----------------------------------------------------------------------*/
static int parseTraceCommand() {
char *subcommand = strtok(nullptr, "");
const DebugParseEntry *entry;
if (subcommand == nullptr)
return UNKNOWN_COMMAND;
else {
entry = findEntry(subcommand, traceSubcommand);
if (entry != nullptr) {
if (strlen(subcommand) < strlen(entry->command)) {
if (findEntry(subcommand, entry + 1) != nullptr)
return AMBIGUOUS_COMMAND;
}
return entry->code;
} else
return UNKNOWN_COMMAND;
}
}
/*----------------------------------------------------------------------*/
static const char *printTraceState(bool state) {
if (state)
return "on - Traces";
else
return "off - Doesn't trace";
}
/*----------------------------------------------------------------------*/
static void printTrace(void) {
printf("Trace section : %s entry to every section (check, description, event, actor, ...)\n", printTraceState(saved_traceSection));
printf("Trace source : %s every source line executed\n", printTraceState(saved_traceSource));
printf("Trace instruction : %s every Amachine instruction executed\n", printTraceState(saved_traceInstruction));
printf("Trace push : %s every push onto the Amachine stack\n", printTraceState(saved_tracePush));
printf("Trace stack : %s the complete stack every time\n", printTraceState(saved_traceStack));
}
/*----------------------------------------------------------------------*/
static void handleTraceCommand() {
char subcommand = parseTraceCommand();
switch (subcommand) {
case TRACE_SECTION_COMMAND:
toggleSectionTrace();
break;
case TRACE_SOURCE_COMMAND:
toggleSourceTrace();
break;
case TRACE_INSTRUCTION_COMMAND:
toggleInstructionTrace();
break;
case TRACE_PUSH_COMMAND:
togglePushTrace();
break;
case TRACE_STACK_COMMAND:
toggleStackTrace();
break;
case AMBIGUOUS_COMMAND:
output("Ambiguous Trace subcommand abbreviation. ? for help.");
break;
default:
printTrace();
}
}
/*----------------------------------------------------------------------*/
static void handleBreakCommand(int fileNumber) {
char *parameter = strtok(nullptr, ":");
if (parameter != nullptr && Common::isAlpha((int)parameter[0])) {
fileNumber = sourceFileNumber(parameter);
if (fileNumber == -1) {
printf("No such file: '%s'\n", parameter);
return;
}
parameter = strtok(nullptr, "");
}
if (parameter == nullptr)
listBreakpoints();
else
setBreakpoint(fileNumber, atoi(parameter));
}
/*----------------------------------------------------------------------*/
static void handleDeleteCommand(bool calledFromBreakpoint, int line, int fileNumber) {
char *parameter = strtok(nullptr, "");
if (parameter == nullptr) {
if (calledFromBreakpoint)
deleteBreakpoint(line, fileNumber);
else
printf("No current breakpoint to delete\n");
} else
deleteBreakpoint(atoi(parameter), fileNumber);
}
/*----------------------------------------------------------------------*/
static void handleNextCommand(bool calledFromBreakpoint) {
stopAtNextLine = TRUE;
debugOption = FALSE;
if (!calledFromBreakpoint)
current.sourceLine = 0;
restoreInfo();
}
/*----------------------------------------------------------------------*/
static void handleLocationsCommand(CONTEXT) {
char *parameter = strtok(nullptr, "");
if (parameter == nullptr)
listLocations(context);
else
showLocation(context, atoi(parameter));
}
/*----------------------------------------------------------------------*/
static void handleActorsCommand(CONTEXT) {
char *parameter = strtok(nullptr, "");
if (parameter == nullptr)
listActors(context);
else
showActor(context, atoi(parameter));
}
/*----------------------------------------------------------------------*/
static void handleClassesCommand(CONTEXT) {
char *parameter = strtok(nullptr, "");
if (parameter == nullptr || strchr(parameter, '*') != nullptr) {
output("Classes:");
showClassHierarchy(1, 0);
listInstances(context, parameter);
} else if (Common::isDigit((int)parameter[0]))
showClass(atoi(parameter));
else {
printf("You have to give a class index to display. You can't use names (yet).");
}
}
/*----------------------------------------------------------------------*/
static void handleObjectsCommand(CONTEXT) {
char *parameter = strtok(nullptr, "");
if (parameter == nullptr)
listObjects(context);
else
showObject(context, atoi(parameter));
}
/*----------------------------------------------------------------------*/
static void handleInstancesCommand(CONTEXT) {
char *parameter = strtok(nullptr, "");
uint i;
if (parameter == nullptr || strchr(parameter, '*') != nullptr)
listInstances(context, parameter);
else if (Common::isDigit((int)parameter[0]))
showInstance(context, atoi(parameter));
else {
for (i = 1; i < header->instanceMax; i++)
if (strcmp(parameter, idOfInstance(context, i)) == 0) {
showInstance(context, i);
return;
}
printf("No instance named '%s'.", parameter);
}
}
/*----------------------------------------------------------------------*/
static bool exactSameVersion() {
return header->version[3] == alan.version.version
&& header->version[2] == alan.version.revision
&& header->version[1] == alan.version.correction
&& header->version[0] == alan.version.state[0];
}
/*======================================================================*/
void debug(CONTEXT, bool calledFromBreakpoint, int line, int fileNumber) {
static bool warned = FALSE;
saveInfo();
g_vm->glk_set_style(style_Preformatted);
if (calledFromBreakpoint)
displaySourceLocation(line, fileNumber);
else {
if (!exactSameVersion() && !warned && !regressionTestOption) {
printf("<WARNING: You are debugging a game which has version %s.>\n",
decodedGameVersion(header->version));
printf("<That is not exactly the same as this interpreter (%s).>\n", alan.version.string);
printf("<This might cause a lot of trouble. Cross your fingers...>\n");
warned = TRUE;
}
}
while (TRUE) {
char commandLine[200];
CALL2(readCommand, commandLine, 200)
char *command = strtok(commandLine, " ");
char commandCode = parseDebugCommand(command);
switch (commandCode) {
case AMBIGUOUS_COMMAND:
output("Ambiguous ADBG command abbreviation. ? for help.");
break;
case ACTORS_COMMAND:
handleActorsCommand(context);
break;
case BREAK_COMMAND:
handleBreakCommand(fileNumber);
break;
case CLASSES_COMMAND:
handleClassesCommand(context);
break;
case DELETE_COMMAND:
handleDeleteCommand(calledFromBreakpoint, line, fileNumber);
break;
case EVENTS_COMMAND:
showEvents(context);
break;
case EXIT_COMMAND:
debugOption = FALSE;
restoreInfo();
goto exit_debug;
case FILES_COMMAND:
listFiles();
break;
case GO_COMMAND:
restoreInfo();
goto exit_debug;
case HELP_COMMAND:
handleHelpCommand();
break;
case INSTANCES_COMMAND:
handleInstancesCommand(context);
break;
case TRACE_COMMAND:
handleTraceCommand();
break;
case INSTRUCTION_TRACE_COMMAND:
toggleInstructionTrace();
break;
case LOCATIONS_COMMAND:
handleLocationsCommand(context);
break;
case NEXT_COMMAND:
handleNextCommand(calledFromBreakpoint);
goto exit_debug;
case OBJECTS_COMMAND:
handleObjectsCommand(context);
break;
case QUIT_COMMAND:
CALL1(terminate, 0)
break;
case SECTION_TRACE_COMMAND:
toggleSectionTrace();
break;
default:
output("Unknown ADBG command. ? for help.");
break;
}
}
exit_debug:
g_vm->glk_set_style(style_Normal);
}
/*======================================================================*/
void traceSay(CONTEXT, int item) {
/*
Say something, but make sure we don't disturb anything and that it is
shown to the player. Needed for tracing. During debugging things are
set up to avoid this problem.
*/
saveInfo();
needSpace = FALSE;
col = 1;
if (item == 0) {
printf("$null$");
} else {
CALL1(say, item)
}
needSpace = FALSE;
col = 1;
restoreInfo();
}
} // End of namespace Alan3
} // End of namespace Glk