1
/*----------------------------------------------------------------------*\
5
Main module of interpreter for ALAN Adventure Language
7
\*----------------------------------------------------------------------*/
21
#ifdef HAVE_SHORT_FILENAMES
24
#include "alan.version.h"
46
/* The Amachine memory */
48
static AcdHdr dummyHeader; /* Dummy to use until memory allocated */
49
AcdHdr *header = &dummyHeader;
51
int memTop = 0; /* Top of load memory */
53
int conjWord; /* First conjunction in dictonary, for ',' */
56
/* Amachine variables */
59
/* Amachine structures */
60
WrdElem *dict; /* Dictionary pointer */
61
ActElem *acts; /* Actor table pointer */
62
LocElem *locs; /* Location table pointer */
63
VrbElem *vrbs; /* Verb table pointer */
64
StxElem *stxs; /* Syntax table pointer */
65
ObjElem *objs; /* Object table pointer */
66
CntElem *cnts; /* Container table pointer */
67
RulElem *ruls; /* Rule table pointer */
68
EvtElem *evts; /* Event table pointer */
69
MsgElem *msgs; /* Message table pointer */
70
Aword *scores; /* Score table pointer */
71
Aword *freq; /* Cumulative character frequencies */
75
Boolean verbose = FALSE;
76
Boolean errflg = TRUE;
77
Boolean trcflg = FALSE;
78
Boolean dbgflg = FALSE;
79
Boolean stpflg = FALSE;
80
Boolean logflg = FALSE;
81
Boolean statusflg = TRUE;
83
Boolean anyOutput = FALSE;
86
/* The files and filenames */
92
/* Screen formatting info */
96
Boolean needsp = FALSE;
97
Boolean skipsp = FALSE;
99
/* Restart jump buffer */
100
jmp_buf restart_label;
104
static jmp_buf jmpbuf; /* Error return long jump buffer */
108
/*======================================================================
112
Terminate the execution of the adventure, e.g. close windows,
117
void terminate(int code)
126
extern struct _dev *_devtab;
129
if (con) { /* Running from WB, created a console so kill it */
130
/* Running from WB, so we created a console and
131
hacked the Aztec C device table to use it for all I/O
132
so now we need to make it close it (once!) */
133
_devtab[1].fd = _devtab[2].fd = 0;
136
/* Geek Gadgets GCC */
137
#include <workbench/startup.h>
138
#include <clib/dos_protos.h>
139
#include <clib/intuition_protos.h>
141
if (_WBenchMsg != NULL) {
143
if (_WBenchMsg->sm_ArgList != NULL)
144
UnLock(CurrentDir(cd));
154
printf("Command-Q to close window.");
164
/*======================================================================
175
printf("Usage:\n\n");
176
printf(" %s [<switches>] <adventure>\n\n", PROGNAME);
177
printf("where the possible optional switches are:\n");
179
glk_set_style(style_Preformatted);
181
printf(" -v verbose mode\n");
182
printf(" -l log player commands and game output to a file\n");
183
printf(" -i ignore version and checksum errors\n");
184
printf(" -n no Status Line\n");
185
printf(" -d enter debug mode\n");
186
printf(" -t trace game execution\n");
187
printf(" -s single instruction trace\n");
189
glk_set_style(style_Normal);
194
/*======================================================================
198
Print a little text blaming the user for the system error.
202
void syserr(char *str)
208
output("$n$nAs you enter the twilight zone of Adventures, you stumble \
209
and fall to your knees. In front of you, you can vaguely see the outlines \
210
of an Adventure that never was.$n$nSYSTEM ERROR: ");
223
if (con) { /* Running from WB, wait for user ack. */
224
printf("press RETURN to quit");
235
/*======================================================================
239
Print an error message, force new player input and abort.
243
void error(MsgKind msgno) /* IN - The error message number */
246
MsgKind msgno; /* IN - The error message number */
251
wrds[wrdidx] = EOF; /* Force new player input */
252
dscrstkp = 0; /* Reset describe stack */
253
longjmp(jmpbuf,TRUE);
257
/*======================================================================
261
Print the the status line on the top of the screen.
264
void statusline(void)
272
if (NULL == glkStatusWin)
275
glk_set_window(glkStatusWin);
276
glk_window_clear(glkStatusWin);
277
glk_window_get_size(glkStatusWin, &glkWidth, NULL);
279
glk_set_style(style_User1);
280
for (i = 0; i < glkWidth; i++)
284
glk_window_move_cursor(glkStatusWin, 1, 0);
287
if (header->maxscore > 0)
288
sprintf(line, "Score %d(%d)/%d moves", cur.score, (int)header->maxscore, cur.tick);
290
sprintf(line, "%d moves", cur.tick);
291
glk_window_move_cursor(glkStatusWin, glkWidth - col - strlen(line), 0);
297
glk_set_window(glkMainWin);
304
if (!statusflg) return;
305
/* ansi_position(1,1); ansi_bold_on(); */
310
if (header->maxscore > 0)
311
sprintf(line, "Score %ld(%ld)/%ld moves", cur.score, (int)header->maxscore, cur.tick);
313
sprintf(line, "%ld moves", cur.tick);
314
for (i=0; i < pagwidth - col - strlen(line); i++) putchar(' ');
317
printf("\x1b[%d;1H", paglen);
326
/*======================================================================
330
Print some text and log it if logging is on.
333
void logprint(char str[])
337
fprintf(logfil, "%s", str);
342
/*======================================================================
346
Make a newline, but check for screen full.
361
if (lin >= paglen - 1) {
366
(void) readline(buf);
368
fgets(buf, 256, stdin);
381
/*======================================================================
385
Make a new paragraph, i.e one empty line (one or two newlines).
400
/*======================================================================
414
glk_window_clear(glkMainWin);
417
if (!statusflg) return;
419
printf("\x1b[%d;1H", paglen);
425
/*======================================================================
429
Safely allocate new memory.
433
void *allocate(unsigned long len) /* IN - Length to allocate */
436
unsigned long len; /* IN - Length to allocate */
439
void *p = (void *)malloc((size_t)len);
442
syserr("Out of memory.");
448
/*----------------------------------------------------------------------
452
Justify a string so that it wraps at end of screen.
456
static void just(char str[])
458
static void just(str)
468
if (col >= pagwidth && !skipsp)
471
while (strlen(str) > pagwidth - col) {
472
i = pagwidth - col - 1;
473
while (!isSpace(str[i]) && i > 0) /* First find wrap point */
475
if (i == 0 && col == 1) /* If it doesn't fit at all */
476
/* Wrap immediately after this word */
477
while (!isSpace(str[i]) && str[i] != '\0')
479
if (i > 0) { /* If it fits ... */
480
ch = str[i]; /* Save space or NULL */
481
str[i] = '\0'; /* Terminate string */
482
logprint(str); /* and print it */
483
skipsp = FALSE; /* If skipping, now we're done */
484
str[i] = ch; /* Restore character */
485
/* Skip white after printed portion */
486
for (str = &str[i]; isSpace(str[0]) && str[0] != '\0'; str++);
488
newline(); /* Then start a new line */
490
logprint(str); /* Print tail */
491
col = col + strlen(str); /* Update column */
496
/*----------------------------------------------------------------------
500
Output a space if needed.
504
static void space(void)
522
/*----------------------------------------------------------------------
526
A parameter needs to be said, check for words the player used and use
531
static void sayparam(int p)
533
static void sayparam(p)
539
for (i = 0; i <= p; i++)
540
if (params[i].code == EOF)
541
syserr("Nonexistent parameter referenced.");
543
if (params[p].firstWord == EOF) /* Any words he used? */
545
else /* Yes, so use them... */
546
for (i = params[p].firstWord; i <= params[p].lastWord; i++) {
547
just((char *)addrTo(dict[wrds[i]].wrd));
548
if (i < params[p].lastWord)
554
/*----------------------------------------------------------------------
558
Print an expanded symbolic reference.
561
I = indent on a new line
563
L = current location name
564
O = current object -> first parameter!
568
$ = no space needed after this
572
char *str /* IN - The string starting with '$' */
575
static void prsym(str)
576
char *str; /* IN - The string starting with '$' */
579
switch (toLower(str[1])) {
592
needsp = TRUE; /* We did print something non-white */
603
sayparam(str[1]-'1');
604
needsp = TRUE; /* We did print something non-white */
608
needsp = TRUE; /* We did print something non-white */
612
needsp = TRUE; /* We did print something non-white */
615
just((char *)addrTo(dict[vrbwrd].wrd));
616
needsp = TRUE; /* We did print something non-white */
624
int spaces = 4-(col-1)%4;
626
for (i = 0; i<spaces; i++) logprint(" ");
642
/*======================================================================
646
Output a string to suit the screen. Any symbolic inserts ('$') are
647
recogniced and performed.
651
void output(char original[])
653
void output(original)
661
copy = strdup(original);
664
if (str[0] != '$' || str[1] != '$')
665
space(); /* Output space if needed (& not inhibited) */
667
while ((symptr = strchr(str, '$')) != (char *) NULL) {
668
ch = *symptr; /* Terminate before symbol */
670
if (strlen(str) > 0) {
671
just(str); /* Output part before '$' */
672
if (str[strlen(str)-1] == ' ')
675
*symptr = ch; /* restore '$' */
676
prsym(symptr); /* Print the symbolic reference */
677
str = &symptr[2]; /* Advance to after symbol and continue */
680
just(str); /* Output trailing part */
682
if (str[strlen(str)-1] != ' ')
690
/*======================================================================
694
Print a message from the message table.
698
void prmsg(MsgKind msg) /* IN - message number */
701
MsgKind msg; /* IN - message number */
704
interpret(msgs[msg].stms);
708
/*----------------------------------------------------------------------*\
710
Various check functions
713
isObj, isLoc, isAct, IsCnt & isNum
715
\*----------------------------------------------------------------------*/
717
/* How to know we are at end of a table */
719
Boolean eot(Aword *adr)
730
Boolean isObj(Aword x)
736
return x >= OBJMIN && x <= OBJMAX;
740
Boolean isCnt(Aword x)
746
return (x >= CNTMIN && x <= CNTMAX) ||
747
(isObj(x) && objs[x-OBJMIN].cont != 0) ||
748
(isAct(x) && acts[x-ACTMIN].cont != 0);
752
Boolean isAct(Aword x)
758
return x >= ACTMIN && x <= ACTMAX;
762
Boolean isLoc(Aword x)
768
return x >= LOCMIN && x <= LOCMAX;
772
Boolean isNum(Aword x)
778
return x >= LITMIN && x <= LITMAX && litValues[x-LITMIN].type == TYPNUM;
782
Boolean isStr(Aword x)
788
return x >= LITMIN && x <= LITMAX && litValues[x-LITMIN].type == TYPSTR;
792
Boolean isLit(Aword x)
798
return x >= LITMIN && x <= LITMAX;
803
/*======================================================================
807
Is there an exit from one location to another?
811
Boolean exitto(int to, int from)
813
Boolean exitto(to, from)
819
if (locs[from-LOCMIN].exts == 0)
820
return(FALSE); /* No exits */
822
for (ext = (ExtElem *) addrTo(locs[from-LOCMIN].exts); !endOfTable(ext); ext++)
832
/*======================================================================
836
Check that the object given is valid, else print an error message
837
or find out what he wanted.
839
This routine is not used any longer, kept for sentimental reasons ;-)
851
for (cur.obj = OBJMIN; cur.obj <= OBJMAX; cur.obj++) {
852
/* If an object is present and it is possible to perform his action */
853
if (isHere(cur.obj) && possible())
857
error(WANT); /* And we didn't find multiple objects */
861
error(WANT); /* But we found ONE */
863
*obj = cur.obj = oldobj;
864
output("($o)"); /* Then he surely meant this object */
871
/*----------------------------------------------------------------------
874
Count the number of items in a container.
878
static int count(int cnt) /* IN - the container to count */
880
static int count(cnt)
881
int cnt; /* IN - the container to count */
886
for (i = OBJMIN; i <= OBJMAX; i++)
888
/* Then it's in this container also */
895
/*----------------------------------------------------------------------
898
Sum the values of one attribute in a container. Recursively.
903
Aword atr, /* IN - the attribute to sum over */
904
Aword cnt /* IN - the container to sum */
907
static int sumatr(atr, cnt)
908
Aword atr; /* IN - the attribute to sum over */
909
Aword cnt; /* IN - the container to sum */
915
for (i = OBJMIN; i <= OBJMAX; i++)
916
if (objs[i-OBJMIN].loc == cnt) { /* Then it's in this container */
917
if (objs[i-OBJMIN].cont != 0) /* This is also a container! */
918
sum = sum + sumatr(atr, i);
919
sum = sum + attribute(i, atr);
927
/*======================================================================
930
Checks if a limit for a container is exceeded.
935
Aword cnt, /* IN - Container code */
936
Aword obj /* IN - The object to add */
939
Boolean checklim(cnt, obj)
940
Aword cnt; /* IN - Container code */
941
Aword obj; /* IN - The object to add */
949
syserr("Checking limits for a non-container.");
951
/* Find the container properties */
953
props = objs[cnt-OBJMIN].cont;
955
props = acts[cnt-ACTMIN].cont;
959
if (cnts[props-CNTMIN].lims != 0) { /* Any limits at all? */
960
for (lim = (LimElem *) addrTo(cnts[props-CNTMIN].lims); !endOfTable(lim); lim++)
962
if (count(cnt) >= lim->val) {
963
interpret(lim->stms);
964
return(TRUE); /* Limit check failed */
967
if (sumatr(lim->atr, cnt) + attribute(obj, lim->atr) > lim->val) {
968
interpret(lim->stms);
981
/*----------------------------------------------------------------------*\
985
\*----------------------------------------------------------------------*/
989
/*----------------------------------------------------------------------
992
Tries a check, returns TRUE if it passed, FALSE else.
996
static Boolean trycheck(
997
Aaddr adr, /* IN - ACODE address to check table */
998
Boolean act /* IN - Act if it fails ? */
1001
static Boolean trycheck(adr, act)
1002
Aaddr adr; /* IN - ACODE address to check table */
1003
Boolean act; /* IN - Act if it fails ? */
1008
chk = (ChkElem *) addrTo(adr);
1009
if (chk->exp == 0) {
1010
interpret(chk->stms);
1013
while (!endOfTable(chk)) {
1014
interpret(chk->exp);
1015
if (!(Abool)pop()) {
1017
interpret(chk->stms);
1027
/*======================================================================
1030
Move hero in a direction.
1044
ext = (ExtElem *) addrTo(locs[cur.loc-LOCMIN].exts);
1045
if (locs[cur.loc-LOCMIN].exts != 0)
1046
while (!endOfTable(ext)) {
1047
if (ext->code == dir) {
1049
if (ext->checks != 0) {
1051
printf("\n<EXIT %d (%s) from %d (", dir,
1052
(char *)addrTo(dict[wrds[wrdidx-1]].wrd), cur.loc);
1054
printf("), Checking:>\n");
1056
ok = trycheck(ext->checks, TRUE);
1060
if (ext->action != 0) {
1062
printf("\n<EXIT %d (%s) from %d (", dir,
1063
(char *)addrTo(dict[wrds[wrdidx-1]].wrd), cur.loc);
1065
printf("), Executing:>\n");
1067
interpret(ext->action);
1069
/* Still at the same place? */
1070
if (where(HERO) == oldloc) {
1072
printf("\n<EXIT %d (%s) from %d (", dir,
1073
(char *)addrTo(dict[wrds[wrdidx-1]].wrd), cur.loc);
1075
printf("), Moving:>\n");
1077
locate(HERO, ext->next);
1089
/*----------------------------------------------------------------------
1093
Find the verb alternative wanted in a verb list and return
1098
static AltElem *findalt(
1099
Aword vrbsadr, /* IN - Address to start of list */
1100
Aword param /* IN - Which parameter to match */
1103
static AltElem *findalt(vrbsadr, param)
1104
Aword vrbsadr; /* IN - Address to start of list */
1105
Aword param; /* IN - Which parameter to match */
1114
for (vrb = (VrbElem *) addrTo(vrbsadr); !endOfTable(vrb); vrb++)
1115
if (vrb->code == cur.vrb) {
1116
for (alt = (AltElem *) addrTo(vrb->alts); !endOfTable(alt); alt++)
1117
if (alt->param == param || alt->param == 0)
1127
/*======================================================================
1131
Check if current action is possible according to the CHECKs.
1135
Boolean possible(void)
1140
AltElem *alt[MAXPARAMS+2]; /* List of alt-pointers, one for each param */
1141
int i; /* Parameter index */
1144
alt[0] = findalt(header->vrbs, 0);
1145
/* Perform global checks */
1146
if (alt[0] != 0 && alt[0]->checks != 0) {
1147
if (!trycheck(alt[0]->checks, FALSE)) return FALSE;
1148
if (fail) return FALSE;
1151
/* Now CHECKs in this location */
1152
alt[1] = findalt(locs[cur.loc-LOCMIN].vrbs, 0);
1153
if (alt[1] != 0 && alt[1]->checks != 0)
1154
if (!trycheck(alt[1]->checks, FALSE))
1157
for (i = 0; params[i].code != EOF; i++) {
1158
alt[i+2] = findalt(objs[params[i].code-OBJMIN].vrbs, i+1);
1159
/* CHECKs in a possible parameter */
1160
if (alt[i+2] != 0 && alt[i+2]->checks != 0)
1161
if (!trycheck(alt[i+2]->checks, FALSE))
1165
for (i = 0; i < 2 || params[i-2].code != EOF; i++)
1166
if (alt[i] != 0 && alt[i]->action != 0)
1168
if (i >= 2 && params[i-2].code == EOF)
1169
/* Didn't find any code for this verb/object combination */
1177
/*----------------------------------------------------------------------
1181
Execute the action commanded by hero.
1185
static void do_it(void)
1190
AltElem *alt[MAXPARAMS+2]; /* List of alt-pointers, one for each param */
1191
Boolean done[MAXPARAMS+2]; /* Is it done */
1192
int i; /* Parameter index */
1193
char trace[80]; /* Trace string buffer */
1196
alt[0] = findalt(header->vrbs, 0);
1197
/* Perform global checks */
1198
if (alt[0] != 0 && alt[0]->checks != 0) {
1200
printf("\n<VERB %d, CHECK, GLOBAL:>\n", cur.vrb);
1201
if (!trycheck(alt[0]->checks, TRUE)) return;
1205
/* Now CHECKs in this location */
1206
alt[1] = findalt(locs[cur.loc-LOCMIN].vrbs, 0);
1207
if (alt[1] != 0 && alt[1]->checks != 0) {
1209
printf("\n<VERB %d, CHECK, in LOCATION:>\n", cur.vrb);
1210
if (!trycheck(alt[1]->checks, TRUE)) return;
1214
for (i = 0; params[i].code != EOF; i++) {
1215
if (isLit(params[i].code))
1218
if (isObj(params[i].code))
1219
alt[i+2] = findalt(objs[params[i].code-OBJMIN].vrbs, i+1);
1220
else if (isAct(params[i].code))
1221
alt[i+2] = findalt(acts[params[i].code-ACTMIN].vrbs, i+1);
1223
syserr("Illegal parameter type.");
1224
/* CHECKs in the parameters */
1225
if (alt[i+2] != 0 && alt[i+2]->checks != 0) {
1227
printf("\n<VERB %d, CHECK, in Parameter #%d:>\n", cur.vrb, i);
1228
if (!trycheck(alt[i+2]->checks, TRUE)) return;
1234
/* Check for anything to execute... */
1235
for (i = 0; i < 2 || params[i-2].code != EOF; i++)
1236
if (alt[i] != 0 && alt[i]->action != 0)
1238
if (i >= 2 && params[i-2].code == EOF)
1239
/* Didn't find any code for this verb/object combination */
1242
/* Perform actions! */
1244
/* First try any BEFORE or ONLY from outside in */
1247
for (i = 2; params[i-2].code != EOF; i++)
1252
if (alt[i]->qual == (Aword)Q_BEFORE || alt[i]->qual == (Aword)Q_ONLY) {
1253
if (alt[i]->action != 0) {
1256
strcpy(trace, "GLOBAL");
1258
strcpy(trace, "in LOCATION");
1260
sprintf(trace, "in PARAMETER %d", i-1);
1261
if (alt[i]->qual == (Aword)Q_BEFORE)
1262
printf("\n<VERB %d, %s (BEFORE), Body:>\n", cur.vrb, trace);
1264
printf("\n<VERB %d, %s (ONLY), Body:>\n", cur.vrb, trace);
1266
interpret(alt[i]->action);
1268
if (alt[i]->qual == (Aword)Q_ONLY) return;
1275
/* Then execute any not declared as AFTER, i.e. the default */
1276
for (i = 0; i < 2 || params[i-2].code != EOF; i++) {
1278
if (alt[i]->qual != (Aword)Q_AFTER) {
1279
if (!done[i] && alt[i]->action != 0) {
1282
strcpy(trace, "GLOBAL");
1284
strcpy(trace, "in LOCATION");
1286
sprintf(trace, "in PARAMETER %d", i-1);
1287
printf("\n<VERB %d, %s, Body:>\n", cur.vrb, trace);
1289
interpret(alt[i]->action);
1296
/* Finally, the ones declared as after */
1300
if (!done[i] && alt[i]->action != 0) {
1303
strcpy(trace, "GLOBAL");
1305
strcpy(trace, "in LOCATION");
1307
sprintf(trace, "in PARAMETER %d", i-1);
1308
printf("\n<VERB %d, %s (AFTER), Body:>\n", cur.vrb, trace);
1310
interpret(alt[i]->action);
1319
/*======================================================================
1323
Execute all activities commanded. Handles possible multiple actions
1324
such as THEM or lists of objects.
1329
ParamElem plst[] /* IN - Plural parameter list */
1341
The code == 0 means this is a multiple position. We must loop
1342
over this position (and replace it by each present in the plst)
1344
for (mpos = 0; params[mpos].code != 0; mpos++); /* Find multiple position */
1345
sprintf(marker, "($%d)", mpos+1); /* Prepare a printout with $1/2/3 */
1346
for (i = 0; plst[i].code != EOF; i++) {
1347
params[mpos] = plst[i];
1350
if (plst[i+1].code != EOF)
1353
params[mpos].code = 0;
1360
/*----------------------------------------------------------------------*\
1366
\*----------------------------------------------------------------------*/
1369
/*----------------------------------------------------------------------
1372
Check if any events are pending. If so execute them.
1375
static void eventchk(void)
1377
static void eventchk()
1380
while (etop != 0 && eventq[etop-1].time == cur.tick) {
1382
if (isLoc(eventq[etop].where))
1383
cur.loc = eventq[etop].where;
1385
cur.loc = where(eventq[etop].where);
1387
printf("\n<EVENT %d (at ", eventq[etop].event);
1391
interpret(evts[eventq[etop].event-EVTMIN].code);
1399
/*----------------------------------------------------------------------*\
1401
Main program and initialisation
1415
\*----------------------------------------------------------------------*/
1418
static FILE *codfil;
1419
static char codfnm[256] = "";
1420
static char txtfnm[256] = "";
1421
static char logfnm[256] = "";
1424
/*----------------------------------------------------------------------
1430
static void checkvers(AcdHdr *header)
1432
static void checkvers(header)
1439
/* Construct our own version */
1440
vers[0] = alan.version.version;
1441
vers[1] = alan.version.revision;
1443
/* Check version of .ACD file */
1445
state[0] = header->vers[3];
1447
printf("<Version of '%s' is %d.%d(%d)%s>",
1449
(int)(header->vers[0]),
1450
(int)(header->vers[1]),
1451
(int)(header->vers[2]),
1452
(header->vers[3])==0? "": state);
1456
/* Compatible if version and revision match... */
1457
if (strncmp(header->vers, vers, 2) != 0) {
1458
#ifdef V25COMPATIBLE
1459
if (header->vers[0] == 2 && header->vers[1] == 5) /* Check for 2.5 version */
1460
/* This we can convert later if needed... */;
1463
#ifdef V27COMPATIBLE
1464
if (header->vers[0] == 2 && header->vers[1] == 7) /* Check for 2.7 version */
1465
/* This we can convert later if needed... */;
1470
sprintf(str, "Incompatible version of ACODE program. Game is %ld.%ld, interpreter %ld.%ld.",
1471
(long) (header->vers[0]),
1472
(long) (header->vers[1]),
1473
(long) alan.version.version,
1474
(long) alan.version.revision);
1477
output("<WARNING! Incompatible version of ACODE program.>\n");
1482
/*----------------------------------------------------------------------
1488
static void load(void)
1499
tmp = fread(&tmphdr, sizeof(tmphdr), 1, codfil);
1503
/* Allocate and load memory */
1506
reverseHdr(&tmphdr);
1509
/* No memory allocated yet? */
1510
if (memory == NULL) {
1511
#ifdef V25COMPATIBLE
1512
if (tmphdr.vers[0] == 2 && tmphdr.vers[1] == 5)
1513
/* We need some more memory to expand 2.5 format*/
1514
memory = allocate((tmphdr.size+tmphdr.objmax-tmphdr.objmin+1+2)*sizeof(Aword));
1517
memory = allocate(tmphdr.size*sizeof(Aword));
1519
header = (AcdHdr *) addrTo(0);
1521
memTop = fread(addrTo(0), sizeof(Aword), tmphdr.size, codfil);
1522
if (memTop != tmphdr.size)
1523
syserr("Could not read all ACD code.");
1525
/* Calculate checksum */
1526
for (i = sizeof(tmphdr)/sizeof(Aword); i < memTop; i++) {
1527
crc += memory[i]&0xff;
1528
crc += (memory[i]>>8)&0xff;
1529
crc += (memory[i]>>16)&0xff;
1530
crc += (memory[i]>>24)&0xff;
1532
printf("%6x\t%6lx\t%6lx\n", i, crc, memory[i]);
1535
if (crc != tmphdr.acdcrc) {
1536
sprintf(err, "Checksum error in .ACD file (0x%lx instead of 0x%lx).",
1537
(unsigned long) crc, (unsigned long) tmphdr.acdcrc);
1541
output("<WARNING! $$");
1543
output("$$ Ignored, proceed at your own risk.>$n");
1548
if (dbgflg||trcflg||stpflg)
1549
output("<Hmm, this is a little-endian machine, fixing byte ordering....");
1550
reverseACD(tmphdr.vers[0] == 2 && tmphdr.vers[1] == 5); /* Reverse all words in the ACD file */
1551
if (dbgflg||trcflg||stpflg)
1555
#ifdef V25COMPATIBLE
1556
/* Check for 2.5 version */
1557
if (tmphdr.vers[0] == 2 && tmphdr.vers[1] == 5) {
1558
if (dbgflg||trcflg||stpflg)
1559
output("<Hmm, this is a v2.5 game, please wait while I convert it...");
1561
if (dbgflg||trcflg||stpflg)
1569
/*----------------------------------------------------------------------
1575
static void checkdebug(void)
1577
static void checkdebug()
1580
/* Make sure he can't debug if not allowed! */
1581
if (!header->debug) {
1582
if (dbgflg|trcflg|stpflg)
1583
printf("<Sorry, '%s' is not compiled for debug!>\n", advnam);
1590
if (dbgflg) /* If debugging */
1591
srand(0); /* use no randomization */
1593
srand(time(0)); /* seed random generator */
1597
/*----------------------------------------------------------------------
1603
static void initheader(void)
1605
static void initheader()
1608
dict = (WrdElem *) addrTo(header->dict);
1609
/* Find out number of entries in dictionary */
1610
for (dictsize = 0; !endOfTable(&dict[dictsize]); dictsize++);
1611
vrbs = (VrbElem *) addrTo(header->vrbs);
1612
stxs = (StxElem *) addrTo(header->stxs);
1613
locs = (LocElem *) addrTo(header->locs);
1614
acts = (ActElem *) addrTo(header->acts);
1615
objs = (ObjElem *) addrTo(header->objs);
1616
evts = (EvtElem *) addrTo(header->evts);
1617
cnts = (CntElem *) addrTo(header->cnts);
1618
ruls = (RulElem *) addrTo(header->ruls);
1619
msgs = (MsgElem *) addrTo(header->msgs);
1620
scores = (Aword *) addrTo(header->scores);
1623
freq = (Aword *) addrTo(header->freq);
1627
/*----------------------------------------------------------------------
1633
static void initstrings(void)
1635
static void initstrings()
1640
for (init = (IniElem *) addrTo(header->init); !endOfTable(init); init++) {
1641
getstr(init->fpos, init->len);
1642
memory[init->adr] = pop();
1647
/*----------------------------------------------------------------------
1653
static void start(void)
1661
cur.loc = startloc = where(HERO);
1665
printf("\n<START:>\n");
1666
interpret(header->start);
1669
acts[HERO-ACTMIN].loc = 0;
1670
locate(HERO, startloc);
1675
/*----------------------------------------------------------------------
1678
Initialization, program load etc.
1682
static void init(void)
1689
/* Initialise some status */
1690
etop = 0; /* No pending events */
1691
looking = FALSE; /* Not looking now */
1692
dscrstkp = 0; /* No describe in progress */
1699
/* Initialise string attributes */
1704
/* Find first conjunction and use that for ',' handling */
1705
for (i = 0; i < dictsize; i++)
1711
/* Start the adventure */
1718
/*----------------------------------------------------------------------
1721
Let the current actor move. If player, ask him.
1725
static void movactor(void)
1727
static void movactor()
1732
ActElem *act = (ActElem *) &acts[cur.act-ACTMIN];
1734
cur.loc = where(cur.act);
1735
if (cur.act == HERO) {
1737
fail = FALSE; /* fail only aborts one actor */
1739
} else if (act->script != 0) {
1740
for (scr = (ScrElem *) addrTo(act->scradr); !endOfTable(scr); scr++)
1741
if (scr->code == act->script) {
1742
/* Find correct step in the list by indexing */
1743
step = (StepElem *) addrTo(scr->steps);
1744
step = (StepElem *) &step[act->step];
1745
/* Now execute it, maybe. First check wait count */
1746
if (step->after > act->count) {
1747
/* Wait some more */
1749
printf("\n<ACTOR %d, ", cur.act);
1753
printf("), SCRIPT %ld, STEP %ld, Waiting %ld more>\n",
1754
act->script, act->step+1, step->after-act->count);
1761
/* Then check possible expression */
1762
if (step->exp != 0) {
1764
printf("\n<ACTOR %d, ", cur.act);
1768
printf("), SCRIPT %ld, STEP %ld, Evaluating:>\n",
1769
act->script, act->step+1);
1771
interpret(step->exp);
1772
if (!(Abool)pop()) {
1774
return; /* Hadn't happened yet */
1777
/* OK, so finally let him do his thing */
1778
act->step++; /* Increment step number before executing... */
1780
printf("\n<ACTOR %d, ", cur.act);
1784
printf("), SCRIPT %ld, STEP %ld, Executing:>\n",
1785
act->script, act->step);
1787
interpret(step->stm);
1789
/* ... so that we can see if he is USEing another script now */
1790
if (act->step != 0 && endOfTable(step))
1791
/* No more steps in this script, so stop him */
1793
fail = FALSE; /* fail only aborts one actor */
1797
syserr("Unknown actor script.");
1798
} else if (trcflg) {
1799
printf("\n<ACTOR %d, ", cur.act);
1803
printf("), Idle>\n");
1809
/*----------------------------------------------------------------------
1813
Open the necessary files.
1817
static void openFiles(void)
1819
static void openFiles()
1826
/* Open Acode file */
1827
strcpy(codfnm, advnam);
1828
strcat(codfnm, ".acd");
1829
if ((codfil = fopen(codfnm, READ_MODE)) == NULL) {
1830
strcpy(str, "Can't open adventure code file '");
1831
strcat(str, codfnm);
1838
char *s = strrchr(codfnm, '\\');
1839
if (!s) s = strrchr(codfnm, '/');
1840
garglk_set_story_name(s ? s + 1 : codfnm);
1844
/* Open Text file */
1845
strcpy(txtfnm, advnam);
1846
strcat(txtfnm, ".dat");
1847
if ((txtfil = fopen(txtfnm, READ_MODE)) == NULL) {
1848
strcpy(str, "Can't open adventure text data file '");
1849
strcat(str, txtfnm);
1854
/* If logging open log file */
1858
if((namstart = strrchr(advnam, ']')) == NULL
1859
&& (namstart = strrchr(advnam, '>')) == NULL
1860
&& (namstart = strrchr(advnam, '/')) == NULL
1861
&& (namstart = strrchr(advnam, '\\')) == NULL
1862
&& (namstart = strrchr(advnam, ':')) == NULL)
1863
namstart = &advnam[0];
1868
sprintf(logfnm, "%s%d%s.log", namstart, (int)tick, usr);
1869
if ((logfil = fopen(logfnm, "w")) == NULL)
1875
/*======================================================================
1886
setjmp(restart_label); /* Return here if he wanted to restart */
1888
init(); /* Load, initialise and start the adventure */
1892
if (malloc_verify() == 0) syserr("Error in heap.");
1899
(void) setjmp(jmpbuf);
1901
/* Move all characters */
1902
for (cur.act = ACTMIN; cur.act <= ACTMAX; cur.act++)