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/main.h"
24 #include "glk/alan3/alan_version.h"
25 #include "glk/alan3/class.h"
26 #include "glk/alan3/compatibility.h"
27 #include "glk/alan3/container.h"
28 #include "glk/alan3/current.h"
29 #include "glk/alan3/debug.h"
30 #include "glk/alan3/decode.h"
31 #include "glk/alan3/dictionary.h"
32 #include "glk/alan3/event.h"
33 #include "glk/alan3/exe.h"
34 #include "glk/alan3/glkio.h"
35 #include "glk/alan3/instance.h"
36 #include "glk/alan3/inter.h"
37 #include "glk/alan3/jumps.h"
38 #include "glk/alan3/lists.h"
39 #include "glk/alan3/literal.h"
40 #include "glk/alan3/location.h"
41 #include "glk/alan3/memory.h"
42 #include "glk/alan3/msg.h"
43 #include "glk/alan3/options.h"
44 #include "glk/alan3/output.h"
45 #include "glk/alan3/parse.h"
46 #include "glk/alan3/reverse.h"
47 #include "glk/alan3/rules.h"
48 #include "glk/alan3/scan.h"
49 #include "glk/alan3/score.h"
50 #include "glk/alan3/state.h"
51 #include "glk/alan3/syserr.h"
52 #include "glk/alan3/syntax.h"
53 #include "glk/alan3/utils.h"
54
55 namespace Glk {
56 namespace Alan3 {
57
58 /* PUBLIC DATA */
59
60 /* Amachine structures - Static */
61 VerbEntry *vrbs; /* Verb table pointer */
62
63
64 /* PRIVATE DATA */
65 #define STACKSIZE 100
66
67 /*----------------------------------------------------------------------*
68 *
69 * Event Handling
70 *
71 *----------------------------------------------------------------------*/
72
73 /*----------------------------------------------------------------------*/
eventName(int event)74 static char *eventName(int event) {
75 return stringAt(events[event].id);
76 }
77
78
79 /*----------------------------------------------------------------------*/
runPendingEvents(CONTEXT)80 static void runPendingEvents(CONTEXT) {
81 int i;
82
83 resetRules();
84 while (eventQueueTop != 0 && eventQueue[eventQueueTop - 1].after == 0) {
85 eventQueueTop--;
86 if (isALocation(eventQueue[eventQueueTop].where))
87 current.location = eventQueue[eventQueueTop].where;
88 else
89 current.location = where(eventQueue[eventQueueTop].where, TRANSITIVE);
90 if (traceSectionOption) {
91 printf("\n<EVENT %s[%d] (at ", eventName(eventQueue[eventQueueTop].event),
92 eventQueue[eventQueueTop].event);
93 CALL1(traceSay, current.location)
94 printf(" [%d]):>\n", current.location);
95 }
96 CALL1(interpret, events[eventQueue[eventQueueTop].event].code)
97 CALL1(evaluateRules, rules)
98 }
99
100 for (i = 0; i < eventQueueTop; i++)
101 eventQueue[i].after--;
102 }
103
104
105 /*----------------------------------------------------------------------*\
106
107 Main program and initialisation
108
109 \*----------------------------------------------------------------------*/
110
111 Common::SeekableReadStream *codfil;
112
113 /*----------------------------------------------------------------------
114 Calculate where to start calculating the CRC. Is different for
115 different versions. CRC is calculated from pre-beta2 memory start to
116 be compatible. If header size changes this should return beta2
117 header size for later versions.
118 */
crcStart(const byte version[4])119 static int crcStart(const byte version[4]) {
120 /* Some earlier versions had a shorter header */
121 if (isPreAlpha5(version))
122 return sizeof(Pre3_0alpha5Header) / sizeof(Aword);
123 else if (isPreBeta2(version))
124 return sizeof(Pre3_0beta2Header) / sizeof(Aword);
125 else
126 return sizeof(ACodeHeader) / sizeof(Aword);
127 }
128
129
130 /*----------------------------------------------------------------------*/
readTemporaryHeader(CONTEXT,ACodeHeader * tmphdr)131 static void readTemporaryHeader(CONTEXT, ACodeHeader *tmphdr) {
132 codfil->seek(0);
133 if (codfil->read(&tmphdr->tag[0], sizeof(ACodeHeader)) != sizeof(ACodeHeader) ||
134 strncmp((char *)tmphdr, "ALAN", 4) != 0)
135 playererr(context, "Not an Alan game file, does not start with \"ALAN\"");
136 }
137
138
139 /*----------------------------------------------------------------------*/
140 #ifdef SCUMM_LITTLE_ENDIAN
reverseMemory()141 static void reverseMemory() {
142 if (debugOption || traceSectionOption || traceInstructionOption)
143 output("<Hmm, this is a little-endian machine, fixing byte ordering....");
144 reverseACD(); /* Reverse content of the ACD file */
145 if (debugOption || traceSectionOption || traceInstructionOption)
146 output("OK.>$n");
147 }
148 #endif
149
150 /*----------------------------------------------------------------------*/
setupHeader(ACodeHeader tmphdr)151 static void setupHeader(ACodeHeader tmphdr) {
152 if (isPreBeta2(tmphdr.version)) {
153 header = (ACodeHeader *)duplicate(&memory[0], sizeof(ACodeHeader));
154 if (isPreAlpha5(tmphdr.version)) {
155 header->ifids = 0;
156 }
157 header->prompt = 0;
158 } else if (isPreBeta3(tmphdr.version)) {
159 header = (ACodeHeader *)pointerTo(0);
160 } else {
161 header = (ACodeHeader *)pointerTo(0);
162 }
163 }
164
165
166 /*----------------------------------------------------------------------*/
loadAndCheckMemory(ACodeHeader tmphdr,Aword crc,char err[])167 static void loadAndCheckMemory(ACodeHeader tmphdr, Aword crc, char err[]) {
168 int i;
169 /* No memory allocated yet? */
170 if (memory == NULL) {
171 memory = (Aword *)allocate(tmphdr.size * sizeof(Aword));
172 }
173
174 memTop = tmphdr.size;
175 codfil->seek(0);
176 if (codfil->read(memory, sizeof(Aword) * memTop) != (sizeof(Aword) * memTop))
177 syserr("Could not read all ACD code.");
178
179 /* Calculate checksum */
180 for (i = crcStart(tmphdr.version); i < memTop; i++) {
181 crc += memory[i] & 0xff;
182 crc += (memory[i] >> 8) & 0xff;
183 crc += (memory[i] >> 16) & 0xff;
184 crc += (memory[i] >> 24) & 0xff;
185 }
186 if (crc != tmphdr.acdcrc) {
187 sprintf(err, "Checksum error in Acode (.a3c) file (0x%lx instead of 0x%lx).",
188 (unsigned long) crc, (unsigned long) tmphdr.acdcrc);
189 if (!ignoreErrorOption)
190 syserr(err);
191 else {
192 output("<WARNING! $$");
193 output(err);
194 output("$$ Ignored, proceed at your own risk.>$n");
195 }
196 }
197 }
198
199
200 /*----------------------------------------------------------------------*/
decodeState(int c)201 static const char *decodeState(int c) {
202 static char state[3] = "\0\0";
203 switch (c) {
204 case 0:
205 return ".";
206 case 'd':
207 return "dev";
208 case 'a':
209 return "alpha";
210 case 'b':
211 return "beta";
212 default:
213 state[0] = header->version[3];
214 return state;
215 }
216 }
217
218 /*======================================================================*/
decodedGameVersion(const byte version[])219 char *decodedGameVersion(const byte version[]) {
220 static char str[100];
221 sprintf(str, "%d.%d%s%d",
222 (int)version[3],
223 (int)version[2],
224 decodeState(version[0]),
225 (int)version[1]);
226 return str;
227 }
228
229 /*----------------------------------------------------------------------*/
incompatibleDevelopmentVersion(ACodeHeader * hdr)230 static void incompatibleDevelopmentVersion(ACodeHeader *hdr) {
231 Common::String msg = Common::String::format("Incompatible version of ACODE program. "
232 "Development versions always require exact match. Game is %ld.%ld%s%ld, interpreter %ld.%ld%s%ld!",
233 (long)(hdr->version[0]),
234 (long)(hdr->version[1]),
235 decodeState(hdr->version[3]),
236 (long)(hdr->version[2]),
237 (long)alan.version.version,
238 (long)alan.version.revision,
239 alan.version.state,
240 (long)alan.version.correction);
241 apperr(msg.c_str());
242 }
243
244
245 /*----------------------------------------------------------------------*/
incompatibleVersion(ACodeHeader * hdr)246 static void incompatibleVersion(ACodeHeader *hdr) {
247 Common::String msg = Common::String::format("Incompatible version of ACODE program. Game is %ld.%ld, interpreter %ld.%ld.",
248 (long)(hdr->version[0]),
249 (long)(hdr->version[1]),
250 (long)alan.version.version,
251 (long)alan.version.revision);
252 apperr(msg.c_str());
253 }
254
255
256 /*----------------------------------------------------------------------*/
alphaRunningLaterGame(char gameState)257 static void alphaRunningLaterGame(char gameState) {
258 output("<WARNING! You are running an alpha interpreter, but the game is generated by a");
259 if (gameState == 'b')
260 output("beta");
261 else
262 output("release");
263 output("state compiler which was released later. This might cause the game to not work fully as intended. Look for an upgraded game file.>\n");
264 }
265
266 /*----------------------------------------------------------------------*/
nonDevelopmentRunningDevelopmentStateGame(const byte version[])267 static void nonDevelopmentRunningDevelopmentStateGame(const byte version[]) {
268 char errorMessage[200];
269 char versionString[100];
270
271 strcpy(errorMessage, "Games generated by a development state compiler");
272 sprintf(versionString, "(this game is v%d.%d.%d%s)", version[0], version[1],
273 version[2], decodeState(version[3]));
274 strcat(errorMessage, versionString);
275 strcat(errorMessage, "can only be run with a matching interpreter. Look for a game file generated with an alpha, beta or release state compiler.>\n");
276 apperr(errorMessage);
277 }
278
279
280 /*======================================================================*/
checkVersion(ACodeHeader * hdr)281 void checkVersion(ACodeHeader *hdr) {
282 /* Strategy for version matching is:
283 1) Development interpreters/games require exact match
284 2) Alpha, Beta and Release interpreters will not run development games
285 3) Alpha interpreters must warn if they run beta or release games
286 4) Beta interpreters may introduce changes which are not alpha compatible,
287 if the change is a strict addition (i.e. if not used will not affect
288 alpha interpreters, example is introduction of a new opcode if it is
289 done at the end of the list)
290 5) Release interpreters should run alpha and beta games without problems
291
292 NOTE that we are working with a non-reversed version string/word here.
293 */
294
295 char interpreterVersion[4];
296 bool developmentVersion;
297 bool alphaVersion;
298 int compareLength;
299 char gameState = hdr->version[3];
300
301 /* Construct our own version */
302 interpreterVersion[0] = alan.version.version;
303 interpreterVersion[1] = alan.version.revision;
304 interpreterVersion[2] = alan.version.correction;
305 interpreterVersion[3] = alan.version.state[0];
306
307 /* Check version of .ACD file */
308 if (debugOption && !regressionTestOption) {
309 printf("<Version of '%s' is %d.%d%s%d!>\n",
310 g_vm->getFilename().c_str(),
311 (int)hdr->version[0],
312 (int)hdr->version[1],
313 decodeState(hdr->version[3]),
314 (int)hdr->version[2]);
315 newline();
316 }
317
318 /* Development version require exact match, else only 2 digit match */
319 developmentVersion = (strcmp(alan.version.state, "dev") == 0);
320 alphaVersion = (strcmp(alan.version.state, "alpha") == 0);
321 compareLength = (developmentVersion ? 3 : 2);
322
323 if (gameState == 'd' && !developmentVersion)
324 /* Development state game requires development state interpreter... */
325 nonDevelopmentRunningDevelopmentStateGame(hdr->version);
326 else {
327 /* Compatible if version, revision (and correction if dev state) match... */
328 if (memcmp(hdr->version, interpreterVersion, compareLength) != 0) {
329 /* Mismatch! */
330 if (!ignoreErrorOption) {
331 if (developmentVersion)
332 incompatibleDevelopmentVersion(hdr);
333 else
334 incompatibleVersion(hdr);
335 } else
336 output("<WARNING! Incompatible version of ACODE program.>\n");
337 } else if (developmentVersion && gameState != 'd')
338 /* ... unless interpreter is development and game not */
339 incompatibleDevelopmentVersion(hdr);
340 else if (alphaVersion && gameState != 'a') {
341 /* If interpreter is alpha version and the game is later, warn! */
342 alphaRunningLaterGame(gameState);
343 }
344 }
345 }
346
347 /*----------------------------------------------------------------------*/
load(CONTEXT)348 static void load(CONTEXT) {
349 ACodeHeader tmphdr;
350 Aword crc = 0;
351 char err[100];
352
353 CALL1(readTemporaryHeader, &tmphdr)
354 checkVersion(&tmphdr);
355
356 /* Allocate and load memory */
357 #ifdef SCUMM_LITTLE_ENDIAN
358 reverseHdr(&tmphdr);
359 #endif
360
361 if (tmphdr.size <= sizeof(ACodeHeader) / sizeof(Aword))
362 syserr("Malformed game file. Too small.");
363
364 loadAndCheckMemory(tmphdr, crc, err);
365
366 #ifdef SCUMM_LITTLE_ENDIAN
367 reverseMemory();
368 #endif
369 setupHeader(tmphdr);
370 }
371
372
373 /*----------------------------------------------------------------------*/
checkDebug(CONTEXT)374 static void checkDebug(CONTEXT) {
375 /* Make sure he can't debug if not allowed! */
376 if (!header->debug) {
377 if (debugOption | traceSectionOption | traceInstructionOption) {
378 printf("<Sorry, '%s' is not compiled for debug! Exiting.>\n", g_vm->getFilename().c_str());
379 CALL1(terminate, 0)
380 }
381 para();
382 debugOption = FALSE;
383 traceSectionOption = FALSE;
384 traceInstructionOption = FALSE;
385 tracePushOption = FALSE;
386 }
387
388 // If debugging, use no randomization
389 if (debugOption || regressionTestOption)
390 g_vm->setRandomNumberSeed(1);
391 }
392
393
394 /*----------------------------------------------------------------------*/
initStaticData(void)395 static void initStaticData(void) {
396 /* Dictionary */
397 dictionary = (DictionaryEntry *) pointerTo(header->dictionary);
398 /* Find out number of entries in dictionary */
399 for (dictionarySize = 0; !isEndOfArray(&dictionary[dictionarySize]); dictionarySize++);
400
401 /* Scores */
402
403
404 /* All addresses to tables indexed by ids are converted to pointers,
405 then adjusted to point to the (imaginary) element before the
406 actual table so that [0] does not exist. Instead indices goes
407 from 1 and we can use [1]. */
408
409 if (header->instanceTableAddress == 0)
410 syserr("Instance table pointer == 0");
411 instances = (InstanceEntry *) pointerTo(header->instanceTableAddress);
412 instances--; /* Back up one so that first is no. 1 */
413
414
415 if (header->classTableAddress == 0)
416 syserr("Class table pointer == 0");
417 classes = (ClassEntry *) pointerTo(header->classTableAddress);
418 classes--; /* Back up one so that first is no. 1 */
419
420 if (header->containerTableAddress != 0) {
421 containers = (ContainerEntry *) pointerTo(header->containerTableAddress);
422 containers--;
423 }
424
425 if (header->eventTableAddress != 0) {
426 events = (EventEntry *) pointerTo(header->eventTableAddress);
427 events--;
428 }
429
430 /* Scores, if already allocated, copy initial data */
431 if (scores == NULL)
432 scores = (Aword *)duplicate((Aword *) pointerTo(header->scores), header->scoreCount * sizeof(Aword));
433 else
434 memcpy(scores, pointerTo(header->scores), header->scoreCount * sizeof(Aword));
435
436 if (literals == NULL)
437 literals = (LiteralEntry *)allocate(sizeof(Aword) * (MAXPARAMS + 1));
438
439 stxs = (SyntaxEntry *) pointerTo(header->syntaxTableAddress);
440 vrbs = (VerbEntry *) pointerTo(header->verbTableAddress);
441 msgs = (MessageEntry *) pointerTo(header->messageTableAddress);
442 initRules(header->ruleTableAddress);
443
444 if (header->pack)
445 freq = (Aword *) pointerTo(header->freq);
446 }
447
448
449 /*----------------------------------------------------------------------*/
initStrings(void)450 static void initStrings(void) {
451 StringInitEntry *init;
452
453 for (init = (StringInitEntry *) pointerTo(header->stringInitTable); !isEndOfArray(init); init++)
454 setInstanceAttribute(init->instanceCode, init->attributeCode, toAptr(getStringFromFile(init->fpos, init->len)));
455 }
456
457 /*----------------------------------------------------------------------*/
sizeOfAttributeData(void)458 static Aint sizeOfAttributeData(void) {
459 uint i;
460 int size = 0;
461
462 for (i = 1; i <= header->instanceMax; i++) {
463 AttributeEntry *attribute = (AttributeEntry *)pointerTo(instances[i].initialAttributes);
464 while (!isEndOfArray(attribute)) {
465 size += AwordSizeOf(AttributeEntry);
466 attribute++;
467 }
468 size += 1; /* For EOD */
469 }
470
471 if (size != header->attributesAreaSize
472 && (sizeof(AttributeHeaderEntry) == sizeof(AttributeEntry)))
473 syserr("Attribute area size calculated wrong.");
474 return size;
475 }
476
477
478 /*----------------------------------------------------------------------*/
initializeAttributes(int awordSize)479 static AttributeEntry *initializeAttributes(int awordSize) {
480 Aword *attributeArea = (Aword *)allocate(awordSize * sizeof(Aword));
481 Aword *currentAttributeArea = attributeArea;
482 uint i;
483
484 for (i = 1; i <= header->instanceMax; i++) {
485 AttributeHeaderEntry *originalAttribute = (AttributeHeaderEntry *)pointerTo(instances[i].initialAttributes);
486 admin[i].attributes = (AttributeEntry *)currentAttributeArea;
487 while (!isEndOfArray(originalAttribute)) {
488 ((AttributeEntry *)currentAttributeArea)->code = originalAttribute->code;
489 ((AttributeEntry *)currentAttributeArea)->value = originalAttribute->value;
490 ((AttributeEntry *)currentAttributeArea)->id = originalAttribute->id;
491 currentAttributeArea += AwordSizeOf(AttributeEntry);
492 originalAttribute++;
493 }
494 *((Aword *)currentAttributeArea) = EOD;
495 currentAttributeArea += 1;
496 }
497
498 return (AttributeEntry *)attributeArea;
499 }
500
501
502
503
504 /*----------------------------------------------------------------------*/
initDynamicData(void)505 static void initDynamicData(void) {
506 uint instanceId;
507
508 /* Allocate for administrative table */
509 admin = (AdminEntry *)allocate((header->instanceMax + 1) * sizeof(AdminEntry));
510
511 /* Create game state copy of attributes */
512 attributes = initializeAttributes(sizeOfAttributeData());
513
514 /* Initialise string & set attributes */
515 initStrings();
516 initSets((SetInitEntry *)pointerTo(header->setInitTable));
517
518 /* Set initial locations */
519 for (instanceId = 1; instanceId <= header->instanceMax; instanceId++)
520 admin[instanceId].location = instances[instanceId].initialLocation;
521 }
522
523
524 /*----------------------------------------------------------------------*/
runInheritedInitialize(CONTEXT,Aint theClass)525 static void runInheritedInitialize(CONTEXT, Aint theClass) {
526 if (theClass == 0) return;
527 CALL1(runInheritedInitialize, classes[theClass].parent)
528
529 if (classes[theClass].initialize)
530 interpret(context, classes[theClass].initialize);
531 }
532
533
534 /*----------------------------------------------------------------------*/
runInitialize(CONTEXT,Aint theInstance)535 static void runInitialize(CONTEXT, Aint theInstance) {
536 CALL1(runInheritedInitialize, instances[theInstance].parent)
537
538 if (instances[theInstance].initialize != 0)
539 interpret(context, instances[theInstance].initialize);
540 }
541
542
543 /*----------------------------------------------------------------------*/
initializeInstances(CONTEXT)544 static void initializeInstances(CONTEXT) {
545 uint instanceId;
546
547 /* Set initial locations */
548 for (instanceId = 1; instanceId <= header->instanceMax; instanceId++) {
549 current.instance = instanceId;
550 CALL1(runInitialize, instanceId)
551 }
552 }
553
554
555 /*----------------------------------------------------------------------*/
start(CONTEXT)556 static void start(CONTEXT) {
557 int startloc;
558
559 current.tick = 0;
560 current.location = startloc = where(HERO, TRANSITIVE);
561 current.actor = HERO;
562 current.score = 0;
563
564 CALL0(initializeInstances)
565
566 if (traceSectionOption)
567 printf("\n<START:>\n");
568 CALL1(interpret, header->start)
569 para();
570
571 if (where(HERO, TRANSITIVE) == startloc) {
572 if (traceSectionOption)
573 printf("<CURRENT LOCATION:>");
574 CALL0(look)
575 }
576
577 resetAndEvaluateRules(context, rules, header->version);
578 }
579
580
581 /*----------------------------------------------------------------------*/
openFiles(void)582 static void openFiles(void) {
583 /* If logging open log file */
584 if (transcriptOption || logOption) {
585 startTranscript();
586 }
587 }
588
589
590 /*----------------------------------------------------------------------*/
init(CONTEXT)591 static void init(CONTEXT) {
592 int i;
593
594 /* Initialise some status */
595 eventQueueTop = 0; /* No pending events */
596 initStaticData();
597 initDynamicData();
598 initParsing();
599 CALL0(checkDebug)
600
601 getPageSize();
602
603 /* Find first conjunction and use that for ',' handling */
604 for (i = 0; i < dictionarySize; i++)
605 if (isConjunction(i)) {
606 conjWord = i;
607 break;
608 }
609
610 /* Start the adventure */
611 if (debugOption) {
612 CALL3(debug, FALSE, 0, 0)
613 } else {
614 clear();
615 }
616
617 start(context);
618 }
619
620
621
622 /*----------------------------------------------------------------------*/
traceActor(CONTEXT,int theActor)623 static bool traceActor(CONTEXT, int theActor) {
624 if (traceSectionOption) {
625 printf("\n<ACTOR ");
626 R0CALL1(traceSay, theActor)
627 printf("[%d]", theActor);
628 if (current.location != 0) {
629 printf(" (at ");
630 R0CALL1(traceSay, current.location)
631 } else
632 printf(" (nowhere");
633 printf("[%d])", current.location);
634 }
635 return traceSectionOption;
636 }
637
638
639 /*----------------------------------------------------------------------*/
scriptName(int theActor,int theScript)640 static char *scriptName(int theActor, int theScript) {
641 ScriptEntry *scriptEntry = (ScriptEntry *)pointerTo(header->scriptTableAddress);
642
643 while (theScript > 1) {
644 scriptEntry++;
645 theScript--;
646 }
647 return (char *)pointerTo(scriptEntry->id);
648 }
649
650
651 /*----------------------------------------------------------------------*/
moveActor(CONTEXT,int theActor)652 static void moveActor(CONTEXT, int theActor) {
653 ScriptEntry *scr;
654 StepEntry *step;
655 Aint previousInstance = current.instance;
656 bool flag;
657
658 if (context._break) {
659 // forfeit setjmp replacement destination
660 assert(context._label == "forfeit");
661 context.clear();
662 current.instance = previousInstance;
663 return;
664 }
665
666 current.actor = theActor;
667 current.instance = theActor;
668 current.location = where(theActor, TRANSITIVE);
669
670 if (theActor == (int)HERO) {
671 // Ask him!
672 CALL0(parse)
673 capitalize = TRUE;
674 fail = FALSE; // fail only aborts one actor
675
676 } else if (admin[theActor].script != 0) {
677 for (scr = (ScriptEntry *) pointerTo(header->scriptTableAddress); !isEndOfArray(scr); scr++) {
678 if (scr->code == admin[theActor].script) {
679 /* Find correct step in the list by indexing */
680 step = (StepEntry *) pointerTo(scr->steps);
681 step = (StepEntry *) &step[admin[theActor].step];
682 /* Now execute it, maybe. First check wait count */
683 if (admin[theActor].waitCount > 0) { /* Wait some more ? */
684 FUNC1(traceActor, flag, theActor)
685 if (flag)
686 printf(", SCRIPT %s[%ld], STEP %ld, Waiting another %ld turns>\n",
687 scriptName(theActor, admin[theActor].script),
688 (long)admin[theActor].script, (long)admin[theActor].step + 1,
689 (long)admin[theActor].waitCount);
690
691 admin[theActor].waitCount--;
692 break;
693 }
694 /* Then check possible expression to wait for */
695 if (step->exp != 0) {
696 FUNC1(traceActor, flag, theActor)
697 if (flag)
698 printf(", SCRIPT %s[%ld], STEP %ld, Evaluating:>\n",
699 scriptName(theActor, admin[theActor].script),
700 (long)admin[theActor].script, (long)admin[theActor].step + 1);
701 FUNC1(evaluate, flag, step->exp)
702 if (!flag)
703 break; /* Break loop, don't execute step*/
704 }
705 /* OK, so finally let him do his thing */
706 admin[theActor].step++; /* Increment step number before executing... */
707 if (!isEndOfArray(step + 1) && (step + 1)->after != 0) {
708 FUNC1(evaluate, admin[theActor].waitCount, (step + 1)->after)
709 }
710
711 FUNC1(traceActor, flag, theActor)
712 if (flag)
713 printf(", SCRIPT %s[%ld], STEP %ld, Executing:>\n",
714 scriptName(theActor, admin[theActor].script),
715 (long)admin[theActor].script,
716 (long)admin[theActor].step);
717 CALL1(interpret, step->stms)
718 step++;
719 /* ... so that we can see if he failed or is USEing another script now */
720 if (fail || (admin[theActor].step != 0 && isEndOfArray(step)))
721 /* No more steps in this script, so stop him */
722 admin[theActor].script = 0;
723 fail = FALSE; /* fail only aborts one actor */
724 break; /* We have executed a script so leave loop */
725 }
726 }
727 if (isEndOfArray(scr))
728 syserr("Unknown actor script.");
729 } else {
730 FUNC1(traceActor, flag, theActor)
731 if (flag) {
732 printf(", Idle>\n");
733 }
734 }
735
736 current.instance = previousInstance;
737 }
738
739 /*======================================================================*/
run(void)740 void run(void) {
741 Stack theStack = NULL;
742 Context ctx;
743
744 openFiles();
745 load(ctx); // Load program
746
747 do {
748 ctx.clear();
749 if (ctx._break)
750 break;
751
752 if (theStack)
753 deleteStack(theStack);
754
755 theStack = createStack(STACKSIZE);
756 setInterpreterStack(theStack);
757
758 initStateStack();
759
760 // Initialise and start the adventure
761 init(ctx);
762
763 while (!g_vm->shouldQuit()) {
764 if (!(ctx._break && ctx._label == "forfeit")) {
765 if (ctx._break) {
766 assert(ctx._label.hasPrefix("return"));
767
768 if (ctx._label == "returnError") {
769 forgetGameState();
770 forceNewPlayerInput();
771 } else if (ctx._label == "returnUndo") {
772 forceNewPlayerInput();
773 }
774
775 ctx.clear();
776 } else {
777 if (debugOption)
778 debug(ctx, FALSE, 0, 0);
779
780 if (!ctx._break) {
781 if (stackDepth(theStack) != 0)
782 syserr("Stack is not empty in main loop");
783
784 if (!current.meta)
785 runPendingEvents(ctx);
786 }
787 }
788
789 recursionDepth = 0;
790
791 // Move all characters, hero first
792 rememberGameState();
793 current.meta = FALSE;
794 }
795
796 moveActor(ctx, header->theHero);
797
798 if (!ctx._break) {
799 if (gameStateChanged)
800 rememberCommands();
801 else
802 forgetGameState();
803
804 if (!current.meta) {
805 current.tick++;
806
807 // Remove this call? Since Eval is done up there after each event...
808 resetAndEvaluateRules(ctx, rules, header->version);
809
810 if (!ctx._break) {
811 // Then all the other actors...
812 for (uint i = 1; i <= header->instanceMax; i++) {
813 if (i != header->theHero && isAActor(i)) {
814 moveActor(ctx, i);
815 if (ctx._break)
816 break;
817
818 resetAndEvaluateRules(ctx, rules, header->version);
819 if (ctx._break)
820 break;
821 }
822 }
823 }
824 }
825 }
826
827 if (ctx._break && ctx._label == "restart")
828 break;
829 }
830 } while (!g_vm->shouldQuit() && ctx._label == "restart");
831 }
832
833 } // End of namespace Alan3
834 } // End of namespace Glk
835