1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/alan3/debug.h"
24 #include "glk/alan3/alan3.h"
25 #include "glk/alan3/class.h"
26 #include "glk/alan3/sysdep.h"
27 #include "glk/alan3/alan_version.h"
28 #include "glk/alan3/compatibility.h"
29 #include "glk/alan3/current.h"
30 #include "glk/alan3/event.h"
31 #include "glk/alan3/exe.h"
32 #include "glk/alan3/glkio.h"
33 #include "glk/alan3/instance.h"
34 #include "glk/alan3/inter.h"
35 #include "glk/alan3/lists.h"
36 #include "glk/alan3/memory.h"
37 #include "glk/alan3/options.h"
38 #include "glk/alan3/output.h"
39 #include "glk/alan3/sysdep.h"
40 #include "glk/alan3/utils.h"
41 #include "glk/streams.h"
42
43 namespace Glk {
44 namespace Alan3 {
45
46 #define BREAKPOINTMAX 50
47
48
49 /* PUBLIC: */
50 int breakpointCount = 0;
51 Breakpoint breakpoint[BREAKPOINTMAX];
52
53 #define debugPrefix "adbg: "
54
55 /*----------------------------------------------------------------------*/
showAttributes(AttributeEntry * attrib)56 static void showAttributes(AttributeEntry *attrib) {
57 AttributeEntry *at;
58 int i;
59 char str[80];
60
61 if (attrib == 0)
62 return;
63
64 i = 1;
65 for (at = attrib; !isEndOfArray(at); at++) {
66 sprintf(str, "$i$t%s[%d] = %d", (char *) pointerTo(at->id), at->code, (int)at->value);
67
68 output(str);
69 i++;
70 }
71 }
72
73
74 /*----------------------------------------------------------------------*/
showContents(CONTEXT,int cnt)75 static void showContents(CONTEXT, int cnt) {
76 uint i;
77 char str[80];
78 Abool found = FALSE;
79
80 output("$iContains:");
81 for (i = 1; i <= header->instanceMax; i++) {
82 if (isIn(i, cnt, DIRECT)) { /* Yes, it's directly in this container */
83 if (!found)
84 found = TRUE;
85 output("$i$t");
86 say(context, i);
87 sprintf(str, "[%d] ", i);
88 output(str);
89 }
90 }
91 if (!found)
92 output("nothing");
93 }
94
95
96 /*----------------------------------------------------------------------*/
idOfInstance(CONTEXT,int instance)97 static char *idOfInstance(CONTEXT, int instance) {
98 int base = header->instanceTableAddress +
99 header->instanceMax * sizeof(InstanceEntry) / sizeof(Aword) + 1;
100 return (char *)&memory[memory[base + instance - 1]];
101 }
102
103
104 /*----------------------------------------------------------------------*/
sayInstanceNumberAndName(CONTEXT,int ins)105 static void sayInstanceNumberAndName(CONTEXT, int ins) {
106 char buf[1000];
107
108 sprintf(buf, "[%d] %s (\"$$", ins, idOfInstance(context, ins));
109 output(buf);
110 say(context, ins);
111 output("$$\")");
112 }
113
114
115 /*----------------------------------------------------------------------*/
sayLocationOfInstance(CONTEXT,int ins,const char * prefix)116 static void sayLocationOfInstance(CONTEXT, int ins, const char *prefix) {
117 if (admin[ins].location == 0)
118 return;
119 else {
120 output(prefix);
121 if (isALocation(admin[ins].location)) {
122 output("at");
123 CALL1(sayInstanceNumberAndName, admin[ins].location)
124 CALL2(sayLocationOfInstance, admin[ins].location, prefix)
125 } else if (isAContainer(admin[ins].location)) {
126 if (isAObject(admin[ins].location))
127 output("in");
128 else if (isAActor(admin[ins].location))
129 output("carried by");
130 CALL1(sayInstanceNumberAndName, admin[ins].location)
131 CALL2(sayLocationOfInstance, admin[ins].location, prefix)
132 } else {
133 output("Illegal location!");
134 }
135 }
136 }
137
138 /*----------------------------------------------------------------------*/
listInstance(CONTEXT,int ins)139 static void listInstance(CONTEXT, int ins) {
140 output("$i");
141 CALL1(sayInstanceNumberAndName, ins)
142 if (instances[ins].container)
143 output("(container)");
144 CALL2(sayLocationOfInstance, ins, ", ")
145 }
146
147
148 /*----------------------------------------------------------------------*/
listInstances(CONTEXT,char * pattern)149 static void listInstances(CONTEXT, char *pattern) {
150 uint ins;
151 bool found = FALSE;
152
153 for (ins = 1; ins <= header->instanceMax; ins++) {
154 if (pattern == NULL || (pattern != NULL && match(pattern, idOfInstance(context, ins)))) {
155 if (!found) {
156 output("Instances:");
157 found = TRUE;
158 }
159 CALL1(listInstance, ins)
160 }
161 }
162 if (pattern != NULL && !found)
163 output("No instances matched the pattern.");
164 }
165
166 /*----------------------------------------------------------------------*/
showInstance(CONTEXT,int ins)167 static void showInstance(CONTEXT, int ins) {
168 char str[80];
169
170 if (ins > (int)header->instanceMax || ins < 1) {
171 sprintf(str, "Instance index %d is out of range.", ins);
172 output(str);
173 return;
174 }
175
176 output("The");
177 CALL1(sayInstanceNumberAndName, ins)
178 if (instances[ins].parent) {
179 sprintf(str, "Isa %s[%d]", idOfClass(instances[ins].parent), instances[ins].parent);
180 output(str);
181 }
182
183 if (!isA(ins, header->locationClassId) || (isA(ins, header->locationClassId) && admin[ins].location != 0)) {
184 sprintf(str, "$iLocation:");
185 output(str);
186 needSpace = TRUE;
187 CALL2(sayLocationOfInstance, ins, "")
188 }
189
190 output("$iAttributes:");
191 showAttributes(admin[ins].attributes);
192
193 if (instances[ins].container)
194 CALL1(showContents, ins)
195
196 if (isA(ins, header->actorClassId)) {
197 if (admin[ins].script == 0)
198 output("$iIs idle");
199 else {
200 sprintf(str, "$iExecuting script: %d, Step: %d", admin[ins].script, admin[ins].step);
201 output(str);
202 }
203 }
204 }
205
206
207 /*----------------------------------------------------------------------*/
listObjects(CONTEXT)208 static void listObjects(CONTEXT) {
209 uint obj;
210
211 output("Objects:");
212 for (obj = 1; obj <= header->instanceMax; obj++)
213 if (isAObject(obj))
214 CALL1(listInstance, obj)
215 }
216
217
218 /*----------------------------------------------------------------------*/
showObject(CONTEXT,int obj)219 static void showObject(CONTEXT, int obj) {
220 char str[80];
221
222
223 if (!isAObject(obj)) {
224 sprintf(str, "Instance %d is not an object", obj);
225 output(str);
226 return;
227 }
228
229 CALL1(showInstance, obj)
230 }
231
232 /*----------------------------------------------------------------------*/
sourceFileNumber(char * fileName)233 static int sourceFileNumber(char *fileName) {
234 SourceFileEntry *entries = (SourceFileEntry *)pointerTo(header->sourceFileTable);
235 int n;
236
237 for (n = 0; * (Aword *)&entries[n] != EOD; n++) {
238 char *entryName;
239 entryName = getStringFromFile(entries[n].fpos, entries[n].len);
240 if (strcmp(entryName, fileName) == 0) return n;
241 entryName = baseNameStart(entryName);
242 if (strcmp(entryName, fileName) == 0) return n;
243 }
244 return -1;
245 }
246
247
248
249 /*----------------------------------------------------------------------*/
printClassName(int c)250 static void printClassName(int c) {
251 output(idOfClass(c));
252 }
253
254
255 /*----------------------------------------------------------------------*/
showClassInheritance(int c)256 static void showClassInheritance(int c) {
257 char str[80];
258
259 if (classes[c].parent != 0) {
260 output(", Isa");
261 printClassName(classes[c].parent);
262 sprintf(str, "[%d]", classes[c].parent);
263 output(str);
264 }
265 }
266
267
268 /*----------------------------------------------------------------------*/
showClass(int cla)269 static void showClass(int cla) {
270 char str[80];
271
272 if (cla < 1) {
273 sprintf(str, "Class index %d is out of range.", cla);
274 output(str);
275 return;
276 }
277
278 output("$t");
279 printClassName(cla);
280 sprintf(str, "[%d]", cla);
281 output(str);
282 showClassInheritance(cla);
283 }
284
285
286 /*----------------------------------------------------------------------*/
listClass(int c)287 static void listClass(int c) {
288 char str[80];
289
290 sprintf(str, "%3d: ", c);
291 output(str);
292 printClassName(c);
293 showClassInheritance(c);
294 }
295
296
297 /*----------------------------------------------------------------------*/
showClassHierarchy(int thisItem,int depth)298 static void showClassHierarchy(int thisItem, int depth) {
299 int i;
300 uint child;
301
302 output("$i");
303 for (i = 0; i < depth; i++)
304 output("$t");
305
306 listClass(thisItem);
307 for (child = 1; child <= header->classMax; child++) {
308 if (classes[child].parent == thisItem) {
309 showClassHierarchy(child, depth + 1);
310 }
311 }
312 }
313
314
315 /*----------------------------------------------------------------------*/
listLocations(CONTEXT)316 static void listLocations(CONTEXT) {
317 uint loc;
318
319 output("Locations:");
320 for (loc = 1; loc <= header->instanceMax; loc++)
321 if (isALocation(loc))
322 listInstance(context, loc);
323 }
324
325
326 /*----------------------------------------------------------------------*/
showLocation(CONTEXT,int loc)327 static void showLocation(CONTEXT, int loc) {
328 char str[80];
329
330
331 if (!isALocation(loc)) {
332 sprintf(str, "Instance %d is not a location.", loc);
333 output(str);
334 return;
335 }
336
337 output("The ");
338 CALL1(say, loc)
339 sprintf(str, "(%d) Isa location :", loc);
340 output(str);
341
342 output("$iAttributes =");
343 showAttributes(admin[loc].attributes);
344 }
345
346
347 /*----------------------------------------------------------------------*/
listActors(CONTEXT)348 static void listActors(CONTEXT) {
349 uint act;
350
351 output("Actors:");
352 for (act = 1; act <= header->instanceMax; act++)
353 if (isAActor(act))
354 CALL1(listInstance, act)
355 }
356
357
358 /*----------------------------------------------------------------------*/
showActor(CONTEXT,int act)359 static void showActor(CONTEXT, int act) {
360 char str[80];
361
362 if (!isAActor(act)) {
363 sprintf(str, "Instance %d is not an actor.", act);
364 output(str);
365 return;
366 }
367
368 CALL1(showInstance, act)
369 }
370
371
372 /*----------------------------------------------------------------------*/
showEvents(CONTEXT)373 static void showEvents(CONTEXT) {
374 uint event;
375 int i;
376 char str[80];
377 bool scheduled;
378
379 output("Events:");
380 for (event = 1; event <= header->eventMax; event++) {
381 sprintf(str, "$i%d [%s]:", event, (char *)pointerTo(events[event].id));
382
383 output(str);
384 scheduled = FALSE;
385 for (i = 0; i < eventQueueTop; i++)
386 if ((scheduled = (eventQueue[i].event == (int)event)))
387 break;
388 if (scheduled) {
389 sprintf(str, "Scheduled for +%d, at ", eventQueue[i].after);
390 output(str);
391 CALL1(say, eventQueue[i].where)
392 } else
393 output("Not scheduled.");
394 }
395 }
396
397
398 /*======================================================================*/
sourceFileName(int fileNumber)399 char *sourceFileName(int fileNumber) {
400 SourceFileEntry *entries = (SourceFileEntry *)pointerTo(header->sourceFileTable);
401
402 return getStringFromFile(entries[fileNumber].fpos, entries[fileNumber].len);
403 }
404
405
406 /*======================================================================*/
readLine(Common::SeekableReadStream * rs,char * line,int maxLen)407 bool readLine(Common::SeekableReadStream *rs, char *line, int maxLen) {
408 if (rs->pos() < rs->size()) {
409 line[maxLen - 1] = '\0';
410
411 char c;
412 do {
413 c = rs->readByte();
414 *line++ = c;
415 } while (--maxLen > 1);
416 }
417
418 return rs->pos() < rs->size();
419 }
420
421
422 /*======================================================================*/
readSourceLine(int file,int line)423 char *readSourceLine(int file, int line) {
424 int count;
425 #define SOURCELINELENGTH 1000
426 static char buffer[SOURCELINELENGTH];
427
428 frefid_t sourceFileRef = g_vm->glk_fileref_create_by_name(fileusage_TextMode, sourceFileName(file), 0);
429 strid_t sourceFile = g_vm->glk_stream_open_file(sourceFileRef, filemode_Read, 0);
430
431 if (sourceFile != NULL) {
432 for (count = 0; count < line; count++) {
433 if (!readLine(*sourceFile, buffer, SOURCELINELENGTH))
434 return NULL;
435
436 // If not read the whole line, or no newline, try to read again
437 while (strchr(buffer, '\n') == NULL) {
438 if (!readLine(*sourceFile, buffer, SOURCELINELENGTH))
439 break;
440 }
441 }
442
443 delete sourceFile;
444 return buffer;
445 }
446
447 return NULL;
448 }
449
450 /*======================================================================*/
showSourceLine(int fileNumber,int line)451 void showSourceLine(int fileNumber, int line) {
452 char *buffer = readSourceLine(fileNumber, line);
453 if (buffer != NULL) {
454 if (buffer[strlen(buffer) - 1] == '\n')
455 buffer[strlen(buffer) - 1] = '\0';
456 printf("<%05d>: %s", line, buffer);
457 }
458 }
459
460
461 /*----------------------------------------------------------------------*/
listFiles()462 static void listFiles() {
463 SourceFileEntry *entry;
464 int i = 0;
465 for (entry = (SourceFileEntry *)pointerTo(header->sourceFileTable); * ((Aword *)entry) != EOD; entry++) {
466 printf(" %2d : %s\n", i, sourceFileName(i));
467 i++;
468 }
469 }
470
471
472 /*----------------------------------------------------------------------*/
findSourceLineIndex(SourceLineEntry * entry,int file,int line)473 static int findSourceLineIndex(SourceLineEntry *entry, int file, int line) {
474 /* Will return index to the closest line available */
475 int i = 0;
476
477 while (!isEndOfArray(&entry[i]) && entry[i].file != file)
478 i++;
479 while (!isEndOfArray(&entry[i]) && entry[i].file == file && entry[i].line < line)
480 i++;
481 if (isEndOfArray(entry) || entry[i].file != file)
482 return i - 1;
483 else
484 return i;
485 }
486
487
488 /*----------------------------------------------------------------------*/
listBreakpoints()489 static void listBreakpoints() {
490 int i;
491 bool found = FALSE;
492
493 for (i = 0; i < BREAKPOINTMAX; i++)
494 if (breakpoint[i].line != 0) {
495 if (!found)
496 printf("Breakpoints set:\n");
497 found = TRUE;
498 printf(" %s:%d\n", sourceFileName(breakpoint[i].file), breakpoint[i].line);
499 }
500 if (!found)
501 printf("No breakpoints set\n");
502 }
503
504
505 /*======================================================================*/
breakpointIndex(int file,int line)506 int breakpointIndex(int file, int line) {
507 int i;
508
509 for (i = 0; i < BREAKPOINTMAX; i++)
510 if (breakpoint[i].line == line && breakpoint[i].file == file)
511 return i;
512 return -1;
513 }
514
515
516 /*----------------------------------------------------------------------*/
availableBreakpointSlot()517 static int availableBreakpointSlot() {
518 int i;
519
520 for (i = 0; i < BREAKPOINTMAX; i++)
521 if (breakpoint[i].line == 0)
522 return i;
523 return -1;
524 }
525
526
527 /*----------------------------------------------------------------------*/
setBreakpoint(int file,int line)528 static void setBreakpoint(int file, int line) {
529 int i = breakpointIndex(file, line);
530
531 if (i != -1)
532 printf("Breakpoint already set at %s:%d\n", sourceFileName(file), line);
533 else {
534 i = availableBreakpointSlot();
535 if (i == -1)
536 printf("No room for more breakpoints. Delete one first.\n");
537 else {
538 int lineIndex = findSourceLineIndex((SourceLineEntry *)pointerTo(header->sourceLineTable), file, line);
539 SourceLineEntry *entry = (SourceLineEntry *)pointerTo(header->sourceLineTable);
540 char leadingText[100] = "Breakpoint";
541 if (entry[lineIndex].file == (Aint)EOD) {
542 printf("Line %d not available\n", line);
543 } else {
544 if (entry[lineIndex].line != line)
545 sprintf(leadingText, "Line %d not available, breakpoint instead", line);
546 breakpoint[i].file = entry[lineIndex].file;
547 breakpoint[i].line = entry[lineIndex].line;
548 printf("%s set at %s:%d\n", leadingText, sourceFileName(entry[lineIndex].file), entry[lineIndex].line);
549 showSourceLine(entry[lineIndex].file, entry[lineIndex].line);
550 printf("\n");
551 }
552 }
553 }
554 }
555
556
557 /*----------------------------------------------------------------------*/
deleteBreakpoint(int line,int file)558 static void deleteBreakpoint(int line, int file) {
559 int i = breakpointIndex(file, line);
560
561 if (i == -1)
562 printf("No breakpoint set at %s:%d\n", sourceFileName(file), line);
563 else {
564 breakpoint[i].line = 0;
565 printf("Breakpoint at %s:%d deleted\n", sourceFileName(file), line);
566 }
567 }
568
569
570
571 static bool saved_traceSection, saved_traceInstruction, saved_capitilize, saved_tracePush, saved_traceStack, saved_traceSource;
572 static int loc;
573
574 /*======================================================================*/
saveInfo(void)575 void saveInfo(void) {
576 /* Save some important things */
577 saved_capitilize = capitalize;
578 capitalize = FALSE;
579 saved_traceSection = traceSectionOption;
580 traceSectionOption = FALSE;
581 saved_traceSource = traceSourceOption;
582 traceSourceOption = FALSE;
583 saved_traceInstruction = traceInstructionOption;
584 traceInstructionOption = FALSE;
585 saved_tracePush = tracePushOption;
586 tracePushOption = FALSE;
587 saved_traceStack = traceStackOption;
588 traceStackOption = FALSE;
589 loc = current.location;
590 current.location = where(HERO, DIRECT);
591 }
592
593
594 /*======================================================================*/
restoreInfo(void)595 void restoreInfo(void) {
596 /* Restore! */
597 capitalize = saved_capitilize;
598 traceSectionOption = saved_traceSection;
599 traceInstructionOption = saved_traceInstruction;
600 traceSourceOption = saved_traceSource;
601 tracePushOption = saved_tracePush;
602 traceStackOption = saved_traceStack;
603 current.location = loc;
604 }
605
606 #define HELP_COMMAND 'H'
607 #define QUIT_COMMAND 'Q'
608 #define EXIT_COMMAND 'X'
609 #define GO_COMMAND 'G'
610 #define FILES_COMMAND 'F'
611 #define INSTANCES_COMMAND 'I'
612 #define CLASSES_COMMAND 'C'
613 #define OBJECTS_COMMAND 'O'
614 #define ACTORS_COMMAND 'A'
615 #define LOCATIONS_COMMAND 'L'
616 #define EVENTS_COMMAND 'E'
617 #define BREAK_COMMAND 'B'
618 #define DELETE_COMMAND 'D'
619 #define TRACE_COMMAND 'R'
620 #define SECTION_TRACE_COMMAND 'T'
621 #define INSTRUCTION_TRACE_COMMAND 'S'
622 #define NEXT_COMMAND 'N'
623 #define UNKNOWN_COMMAND '?'
624 #define AMBIGUOUS_COMMAND '-'
625 #define TRACE_SOURCE_COMMAND 's'
626 #define TRACE_SECTION_COMMAND 'e'
627 #define TRACE_INSTRUCTION_COMMAND 'i'
628 #define TRACE_PUSH_COMMAND 'p'
629 #define TRACE_STACK_COMMAND 't'
630
631 typedef struct DebugParseEntry {
632 const char *command;
633 const char *parameter;
634 char code;
635 const char *helpText;
636 } DebugParseEntry;
637
638 static const DebugParseEntry commandEntries[] = {
639 {"help", "", HELP_COMMAND, "this help"},
640 {"?", "", HELP_COMMAND, "d:o"},
641 {"break", "[[file:]n]", BREAK_COMMAND, "set breakpoint at source line [n] (optionally in [file])"},
642 {"delete", "[[file:]n]", DELETE_COMMAND, "delete breakpoint at source line [n] (optionally in [file])"},
643 {"files", "", FILES_COMMAND, "list source files"},
644 {"events", "", EVENTS_COMMAND, "list events"},
645 {"classes", "", CLASSES_COMMAND, "list class hierarchy"},
646 {"instances", "[n]", INSTANCES_COMMAND, "list instance(s), all, wildcard, number or name"},
647 {"objects", "[n]", OBJECTS_COMMAND, "list instance(s) that are objects"},
648 {"actors", "[n]", ACTORS_COMMAND, "list instance(s) that are actors"},
649 {"locations", "[n]", LOCATIONS_COMMAND, "list instances that are locations"},
650 {"trace", "('source'|'section'|'instruction'|'push'|'stack')", TRACE_COMMAND, "toggle various traces"},
651 {"next", "", NEXT_COMMAND, "run game and stop at next source line"},
652 {"go", "", GO_COMMAND, "go another player turn"},
653 {"exit", "", EXIT_COMMAND, "exit to game, enter 'debug' to get back"},
654 {"x", "", EXIT_COMMAND, "d:o"},
655 {"quit", "", QUIT_COMMAND, "quit game"},
656 {NULL, NULL, '\0', NULL}
657 };
658
659 static const DebugParseEntry traceSubcommand[] = {
660 {"source", "", TRACE_SOURCE_COMMAND, ""},
661 {"section", "", TRACE_SECTION_COMMAND, ""},
662 {"instructions", "", TRACE_INSTRUCTION_COMMAND, ""},
663 {"pushs", "", TRACE_PUSH_COMMAND, ""},
664 {"stacks", "", TRACE_STACK_COMMAND, ""},
665 {NULL, NULL, '\0', NULL}
666 };
667
668
spaces(int length)669 static char *spaces(int length) {
670 static char buf[200];
671 int i;
672
673 for (i = 0; i < length; i++)
674 buf[i] = ' ';
675 buf[i] = '\0';
676 return buf;
677 }
678
679
680 /*----------------------------------------------------------------------*/
padding(const DebugParseEntry * entry,int maxLength)681 static char *padding(const DebugParseEntry *entry, int maxLength) {
682 return spaces(maxLength - strlen(entry->command) - strlen(entry->parameter));
683 }
684
685
686 /*----------------------------------------------------------------------*/
handleHelpCommand()687 static void handleHelpCommand() {
688 if (!regressionTestOption)
689 output(alan.longHeader);
690
691 const DebugParseEntry *entry = commandEntries;
692
693 int maxLength = 0;
694 for (entry = commandEntries; entry->command != NULL; entry++) {
695 if (strlen(entry->command) + strlen(entry->parameter) > (uint)maxLength)
696 maxLength = strlen(entry->command) + strlen(entry->parameter);
697 }
698
699 output("$nADBG Commands (can be abbreviated):");
700 for (entry = commandEntries; entry->command != NULL; entry++) {
701 char buf[200];
702 sprintf(buf, "$i%s %s %s$n$t$t-- %s", entry->command, entry->parameter, padding(entry, maxLength), entry->helpText);
703 output(buf);
704 }
705 }
706
707
708 /*----------------------------------------------------------------------*/
findEntry(char * command,const DebugParseEntry * entry)709 static const DebugParseEntry *findEntry(char *command, const DebugParseEntry *entry) {
710 while (entry->command != NULL) {
711 if (scumm_strnicmp(command, entry->command, strlen(command)) == 0)
712 return entry;
713 entry++;
714 }
715 return NULL;
716 }
717
718
719 /*----------------------------------------------------------------------*/
parseDebugCommand(char * command)720 static char parseDebugCommand(char *command) {
721 const DebugParseEntry *entry = findEntry(command, commandEntries);
722 if (entry != NULL) {
723 if (strlen(command) < strlen(entry->command)) {
724 /* See if there are any more partial matches */
725 if (findEntry(command, entry + 1) != NULL)
726 /* TODO: we should list the possible matches somehow */
727 return AMBIGUOUS_COMMAND;
728 }
729 return entry->code;
730 } else
731 return UNKNOWN_COMMAND;
732 }
733
734
735 /*----------------------------------------------------------------------*/
readCommand(CONTEXT,char buf[],size_t maxLen)736 static void readCommand(CONTEXT, char buf[], size_t maxLen) {
737 char c;
738 bool flag;
739
740 capitalize = FALSE;
741 if (anyOutput) newline();
742 do {
743 output("adbg> ");
744
745 FUNC2(g_io->readLine, flag, buf, maxLen)
746 if (!flag) {
747 newline();
748 CALL0(quitGame)
749 }
750 lin = 1;
751 c = buf[0];
752 } while (c == '\0');
753 }
754
755
756 /*----------------------------------------------------------------------*/
displaySourceLocation(int line,int fileNumber)757 static void displaySourceLocation(int line, int fileNumber) {
758 const char *cause;
759 if (anyOutput) newline();
760 if (breakpointIndex(fileNumber, line) != -1)
761 cause = "Breakpoint hit at";
762 else
763 cause = "Stepping to";
764 printf("%s %s %s:%d\n", debugPrefix, cause, sourceFileName(fileNumber), line);
765 showSourceLine(fileNumber, line);
766 printf("\n");
767 anyOutput = FALSE;
768 }
769
770
771 /*----------------------------------------------------------------------*/
toggleSectionTrace()772 static void toggleSectionTrace() {
773 if ((saved_traceSection = !saved_traceSection))
774 printf("Section trace on.");
775 else
776 printf("Section trace off.");
777 }
778
779 /*----------------------------------------------------------------------*/
toggleInstructionTrace()780 static void toggleInstructionTrace() {
781 if ((saved_traceInstruction = !saved_traceInstruction))
782 printf("Single instruction trace on.");
783 else
784 printf("Single instruction trace off.");
785 }
786
787 /*----------------------------------------------------------------------*/
toggleSourceTrace()788 static void toggleSourceTrace() {
789 if ((saved_traceSource = !saved_traceSource))
790 printf("Source code trace on.");
791 else
792 printf("Source code trace off.");
793 }
794
795
796 /*----------------------------------------------------------------------*/
togglePushTrace()797 static void togglePushTrace() {
798 if ((saved_tracePush = !saved_tracePush))
799 printf("Stack Push trace on.");
800 else
801 printf("Stack Push trace off.");
802 }
803
804
805 /*----------------------------------------------------------------------*/
toggleStackTrace()806 static void toggleStackTrace() {
807 if ((saved_traceStack = !saved_traceStack))
808 printf("Full stack trace on.");
809 else
810 printf("Full stack trace off.");
811 }
812
813
814 /*----------------------------------------------------------------------*/
parseTraceCommand()815 static int parseTraceCommand() {
816 char *subcommand = strtok(NULL, "");
817 const DebugParseEntry *entry;
818 if (subcommand == 0)
819 return UNKNOWN_COMMAND;
820 else {
821 entry = findEntry(subcommand, traceSubcommand);
822 if (entry != NULL) {
823 if (strlen(subcommand) < strlen(entry->command)) {
824 if (findEntry(subcommand, entry + 1) != NULL)
825 return AMBIGUOUS_COMMAND;
826 }
827 return entry->code;
828 } else
829 return UNKNOWN_COMMAND;
830 }
831 }
832
833
834 /*----------------------------------------------------------------------*/
printTraceState(bool state)835 static const char *printTraceState(bool state) {
836 if (state)
837 return "on - Traces";
838 else
839 return "off - Doesn't trace";
840 }
841
842 /*----------------------------------------------------------------------*/
printTrace(void)843 static void printTrace(void) {
844 printf("Trace section : %s entry to every section (check, description, event, actor, ...)\n", printTraceState(saved_traceSection));
845 printf("Trace source : %s every source line executed\n", printTraceState(saved_traceSource));
846 printf("Trace instruction : %s every Amachine instruction executed\n", printTraceState(saved_traceInstruction));
847 printf("Trace push : %s every push onto the Amachine stack\n", printTraceState(saved_tracePush));
848 printf("Trace stack : %s the complete stack every time\n", printTraceState(saved_traceStack));
849 }
850
851
852 /*----------------------------------------------------------------------*/
handleTraceCommand()853 static void handleTraceCommand() {
854 char subcommand = parseTraceCommand();
855
856 switch (subcommand) {
857 case TRACE_SECTION_COMMAND:
858 toggleSectionTrace();
859 break;
860 case TRACE_SOURCE_COMMAND:
861 toggleSourceTrace();
862 break;
863 case TRACE_INSTRUCTION_COMMAND:
864 toggleInstructionTrace();
865 break;
866 case TRACE_PUSH_COMMAND:
867 togglePushTrace();
868 break;
869 case TRACE_STACK_COMMAND:
870 toggleStackTrace();
871 break;
872 case AMBIGUOUS_COMMAND:
873 output("Ambiguous Trace subcommand abbreviation. ? for help.");
874 break;
875 default:
876 printTrace();
877 }
878 }
879
880
881 /*----------------------------------------------------------------------*/
handleBreakCommand(int fileNumber)882 static void handleBreakCommand(int fileNumber) {
883 char *parameter = strtok(NULL, ":");
884 if (parameter != NULL && Common::isAlpha((int)parameter[0])) {
885 fileNumber = sourceFileNumber(parameter);
886 if (fileNumber == -1) {
887 printf("No such file: '%s'\n", parameter);
888 return;
889 }
890 parameter = strtok(NULL, "");
891 }
892 if (parameter == NULL)
893 listBreakpoints();
894 else
895 setBreakpoint(fileNumber, atoi(parameter));
896 }
897
898
899 /*----------------------------------------------------------------------*/
handleDeleteCommand(bool calledFromBreakpoint,int line,int fileNumber)900 static void handleDeleteCommand(bool calledFromBreakpoint, int line, int fileNumber) {
901 char *parameter = strtok(NULL, "");
902 if (parameter == NULL) {
903 if (calledFromBreakpoint)
904 deleteBreakpoint(line, fileNumber);
905 else
906 printf("No current breakpoint to delete\n");
907 } else
908 deleteBreakpoint(atoi(parameter), fileNumber);
909 }
910
911
912 /*----------------------------------------------------------------------*/
handleNextCommand(bool calledFromBreakpoint)913 static void handleNextCommand(bool calledFromBreakpoint) {
914 stopAtNextLine = TRUE;
915 debugOption = FALSE;
916 if (!calledFromBreakpoint)
917 current.sourceLine = 0;
918 restoreInfo();
919 }
920
921
922 /*----------------------------------------------------------------------*/
handleLocationsCommand(CONTEXT)923 static void handleLocationsCommand(CONTEXT) {
924 char *parameter = strtok(NULL, "");
925 if (parameter == 0)
926 listLocations(context);
927 else
928 showLocation(context, atoi(parameter));
929 }
930
931
932 /*----------------------------------------------------------------------*/
handleActorsCommand(CONTEXT)933 static void handleActorsCommand(CONTEXT) {
934 char *parameter = strtok(NULL, "");
935 if (parameter == NULL)
936 listActors(context);
937 else
938 showActor(context, atoi(parameter));
939 }
940
941
942 /*----------------------------------------------------------------------*/
handleClassesCommand(CONTEXT)943 static void handleClassesCommand(CONTEXT) {
944 char *parameter = strtok(NULL, "");
945 if (parameter == NULL || strchr(parameter, '*') != 0) {
946 output("Classes:");
947 showClassHierarchy(1, 0);
948 listInstances(context, parameter);
949 } else if (Common::isDigit((int)parameter[0]))
950 showClass(atoi(parameter));
951 else {
952 printf("You have to give a class index to display. You can't use names (yet).");
953 }
954 }
955
956
957 /*----------------------------------------------------------------------*/
handleObjectsCommand(CONTEXT)958 static void handleObjectsCommand(CONTEXT) {
959 char *parameter = strtok(NULL, "");
960 if (parameter == NULL)
961 listObjects(context);
962 else
963 showObject(context, atoi(parameter));
964 }
965
966
967 /*----------------------------------------------------------------------*/
handleInstancesCommand(CONTEXT)968 static void handleInstancesCommand(CONTEXT) {
969 char *parameter = strtok(NULL, "");
970 uint i;
971
972 if (parameter == NULL || strchr(parameter, '*') != 0)
973 listInstances(context, parameter);
974 else if (Common::isDigit((int)parameter[0]))
975 showInstance(context, atoi(parameter));
976 else {
977 for (i = 1; i < header->instanceMax; i++)
978 if (strcmp(parameter, idOfInstance(context, i)) == 0) {
979 showInstance(context, i);
980 return;
981 }
982 printf("No instance named '%s'.", parameter);
983 }
984 }
985
986 /*----------------------------------------------------------------------*/
exactSameVersion()987 static bool exactSameVersion() {
988 return header->version[3] == alan.version.version
989 && header->version[2] == alan.version.revision
990 && header->version[1] == alan.version.correction
991 && header->version[0] == alan.version.state[0];
992 }
993
994
995 /*======================================================================*/
debug(CONTEXT,bool calledFromBreakpoint,int line,int fileNumber)996 void debug(CONTEXT, bool calledFromBreakpoint, int line, int fileNumber) {
997 static bool warned = FALSE;
998
999 saveInfo();
1000 g_vm->glk_set_style(style_Preformatted);
1001
1002 if (calledFromBreakpoint)
1003 displaySourceLocation(line, fileNumber);
1004 else {
1005 if (!exactSameVersion() && !warned && !regressionTestOption) {
1006 printf("<WARNING: You are debugging a game which has version %s.>\n",
1007 decodedGameVersion(header->version));
1008 printf("<That is not exactly the same as this interpreter (%s).>\n", alan.version.string);
1009 printf("<This might cause a lot of trouble. Cross your fingers...>\n");
1010 warned = TRUE;
1011 }
1012 }
1013
1014 while (TRUE) {
1015 char commandLine[200];
1016 CALL2(readCommand, commandLine, 200)
1017
1018 char *command = strtok(commandLine, " ");
1019 char commandCode = parseDebugCommand(command);
1020
1021 switch (commandCode) {
1022 case AMBIGUOUS_COMMAND:
1023 output("Ambiguous ADBG command abbreviation. ? for help.");
1024 break;
1025 case ACTORS_COMMAND:
1026 handleActorsCommand(context);
1027 break;
1028 case BREAK_COMMAND:
1029 handleBreakCommand(fileNumber);
1030 break;
1031 case CLASSES_COMMAND:
1032 handleClassesCommand(context);
1033 break;
1034 case DELETE_COMMAND:
1035 handleDeleteCommand(calledFromBreakpoint, line, fileNumber);
1036 break;
1037 case EVENTS_COMMAND:
1038 showEvents(context);
1039 break;
1040 case EXIT_COMMAND:
1041 debugOption = FALSE;
1042 restoreInfo();
1043 goto exit_debug;
1044 case FILES_COMMAND:
1045 listFiles();
1046 break;
1047 case GO_COMMAND:
1048 restoreInfo();
1049 goto exit_debug;
1050 case HELP_COMMAND:
1051 handleHelpCommand();
1052 break;
1053 case INSTANCES_COMMAND:
1054 handleInstancesCommand(context);
1055 break;
1056 case TRACE_COMMAND:
1057 handleTraceCommand();
1058 break;
1059 case INSTRUCTION_TRACE_COMMAND:
1060 toggleInstructionTrace();
1061 break;
1062 case LOCATIONS_COMMAND:
1063 handleLocationsCommand(context);
1064 break;
1065 case NEXT_COMMAND:
1066 handleNextCommand(calledFromBreakpoint);
1067 goto exit_debug;
1068 case OBJECTS_COMMAND:
1069 handleObjectsCommand(context);
1070 break;
1071 case QUIT_COMMAND:
1072 CALL1(terminate, 0)
1073 break;
1074 case SECTION_TRACE_COMMAND:
1075 toggleSectionTrace();
1076 break;
1077 default:
1078 output("Unknown ADBG command. ? for help.");
1079 break;
1080 }
1081 }
1082
1083 exit_debug:
1084 g_vm->glk_set_style(style_Normal);
1085 }
1086
1087
1088 /*======================================================================*/
traceSay(CONTEXT,int item)1089 void traceSay(CONTEXT, int item) {
1090 /*
1091 Say something, but make sure we don't disturb anything and that it is
1092 shown to the player. Needed for tracing. During debugging things are
1093 set up to avoid this problem.
1094 */
1095
1096 saveInfo();
1097 needSpace = FALSE;
1098 col = 1;
1099 if (item == 0) {
1100 printf("$null$");
1101 } else {
1102 CALL1(say, item)
1103 }
1104
1105 needSpace = FALSE;
1106 col = 1;
1107 restoreInfo();
1108 }
1109
1110 } // End of namespace Alan3
1111 } // End of namespace Glk
1112