/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #define V27COMPATIBLE #include "glk/alan2/sysdep.h" #include "glk/alan2/types.h" #include "glk/alan2/main.h" //#include #ifdef USE_READLINE #include "glk/alan2/readline.h" #endif #ifdef HAVE_SHORT_FILENAMES #include "glk/alan2/av.h" #else #include "glk/alan2/alan_version.h" #endif #include "glk/alan2/args.h" #include "glk/alan2/parse.h" #include "glk/alan2/inter.h" #include "glk/alan2/rules.h" #ifdef REVERSED #include "glk/alan2/reverse.h" #endif #include "glk/alan2/debug.h" #include "glk/alan2/stack.h" #include "glk/alan2/exe.h" #include "glk/alan2/term.h" #ifdef GLK #include "common/file.h" #include "glk/alan2/alan2.h" #include "glk/alan2/glkio.h" #endif namespace Glk { namespace Alan2 { /* PUBLIC DATA */ /* The Amachine memory */ Aword *memory; //static AcdHdr dummyHeader; /* Dummy to use until memory allocated */ AcdHdr *header; int memTop; /* Top of load memory */ int conjWord; /* First conjunction in dictonary, for ',' */ /* Amachine variables */ CurVars cur; /* Amachine structures */ WrdElem *dict; /* Dictionary pointer */ ActElem *acts; /* Actor table pointer */ LocElem *locs; /* Location table pointer */ VrbElem *vrbs; /* Verb table pointer */ StxElem *stxs; /* Syntax table pointer */ ObjElem *objs; /* Object table pointer */ CntElem *cnts; /* Container table pointer */ RulElem *ruls; /* Rule table pointer */ EvtElem *evts; /* Event table pointer */ MsgElem *msgs; /* Message table pointer */ Aword *scores; /* Score table pointer */ Aword *freq; /* Cumulative character frequencies */ int dictsize; Boolean verbose = FALSE; Boolean errflg = TRUE; Boolean trcflg = FALSE; Boolean dbgflg = FALSE; Boolean stpflg = FALSE; Boolean logflg = FALSE; Boolean statusflg = TRUE; Boolean fail = FALSE; Boolean anyOutput = FALSE; /* The files and filenames */ const char *advnam; Common::File *txtfil; Common::WriteStream *logfil; /* Screen formatting info */ int col, lin; int paglen, pagwidth; Boolean needsp = FALSE; Boolean skipsp = FALSE; /* Restart jump buffer */ //jmp_buf restart_label; /* PRIVATE DATA */ //static jmp_buf jmpbuf; /* Error return long jump buffer */ /*====================================================================== terminate() Terminate the execution of the adventure, e.g. close windows, return buffers... */ void terminate(int code) { #ifdef __amiga__ #ifdef AZTEC_C #include extern struct _dev *_devtab; char buf[85]; if (con) { /* Running from WB, created a console so kill it */ /* Running from WB, so we created a console and hacked the Aztec C device table to use it for all I/O so now we need to make it close it (once!) */ _devtab[1].fd = _devtab[2].fd = 0; } else #else /* Geek Gadgets GCC */ #include #include #include if (_WBenchMsg != NULL) { Close(window); if (_WBenchMsg->sm_ArgList != NULL) UnLock(CurrentDir(cd)); } else #endif #endif newline(); free(memory); if (logflg) fclose(logfil); #ifdef __MWERKS__ printf("Command-Q to close window."); #endif #ifdef GLK g_vm->glk_exit(); #else exit(code); #endif } /*====================================================================== usage() */ void usage() { printf("Usage:\n\n"); printf(" %s [] \n\n", PROGNAME); printf("where the possible optional switches are:\n"); #ifdef GLK g_vm->glk_set_style(style_Preformatted); #endif printf(" -v verbose mode\n"); printf(" -l log player commands and game output to a file\n"); printf(" -i ignore version and checksum errors\n"); printf(" -n no Status Line\n"); printf(" -d enter debug mode\n"); printf(" -t trace game execution\n"); printf(" -s single instruction trace\n"); #ifdef GLK g_vm->glk_set_style(style_Normal); #endif } /*====================================================================== syserr() Print a little text blaming the user for the system error. */ void syserr(const char *str) { #ifdef GLK ::error("%s", str); #else output("$n$nAs you enter the twilight zone of Adventures, you stumble \ and fall to your knees. In front of you, you can vaguely see the outlines \ of an Adventure that never was.$n$nSYSTEM ERROR: "); output(str); output("$n$n"); if (logflg) fclose(logfil); newline(); #ifdef __amiga__ #ifdef AZTEC_C { char buf[80]; if (con) { /* Running from WB, wait for user ack. */ printf("press RETURN to quit"); gets(buf); } } #endif #endif terminate(0); #endif } /*====================================================================== error() Print an error message, force new player input and abort. */ void error(MsgKind msgno /* IN - The error message number */) { if (msgno != MSGMAX) prmsg(msgno); wrds[wrdidx] = EOF; /* Force new player input */ dscrstkp = 0; /* Reset describe stack */ //longjmp(jmpbuf,TRUE); ::error("Error occurred"); } /*====================================================================== statusline() Print the the status line on the top of the screen. */ void statusline() { #ifdef GLK uint glkWidth; char line[100]; int pcol = col; uint i; if (NULL == glkStatusWin) return; g_vm->glk_set_window(glkStatusWin); g_vm->glk_window_clear(glkStatusWin); g_vm->glk_window_get_size(glkStatusWin, &glkWidth, NULL); g_vm->glk_set_style(style_User1); for (i = 0; i < glkWidth; i++) g_vm->glk_put_char(' '); col = 1; g_vm->glk_window_move_cursor(glkStatusWin, 1, 0); needsp = FALSE; say(where(HERO)); if (header->maxscore > 0) sprintf(line, "Score %d(%d)/%d moves", cur.score, (int)header->maxscore, cur.tick); else sprintf(line, "%d moves", cur.tick); g_vm->glk_window_move_cursor(glkStatusWin, glkWidth - col - strlen(line), 0); printf(line); needsp = FALSE; col = pcol; g_vm->glk_set_window(glkMainWin); #else #ifdef HAVE_ANSI char line[100]; int i; int pcol = col; if (!statusflg) return; /* ansi_position(1,1); ansi_bold_on(); */ printf("\x1b[1;1H"); printf("\x1b[7m"); col = 1; say(where(HERO)); if (header->maxscore > 0) sprintf(line, "Score %ld(%ld)/%ld moves", cur.score, (int)header->maxscore, cur.tick); else sprintf(line, "%ld moves", cur.tick); for (i=0; i < pagwidth - col - strlen(line); i++) putchar(' '); printf(line); printf("\x1b[m"); printf("\x1b[%d;1H", paglen); needsp = FALSE; col = pcol; #endif #endif } /*====================================================================== logprint() Print some text and log it if logging is on. */ void logprint(char str[]) { printf(str); if (logflg) fprintf(logfil, "%s", str); } /*====================================================================== newline() Make a newline, but check for screen full. */ void newline() { #ifdef GLK g_vm->glk_put_char('\n'); #else char buf[256]; col = 1; if (lin >= paglen - 1) { logprint("\n"); needsp = FALSE; prmsg(M_MORE); #ifdef USE_READLINE (void) readline(buf); #else fgets(buf, 256, stdin); #endif getPageSize(); lin = 0; } else logprint("\n"); lin++; needsp = FALSE; #endif } /*====================================================================== para() Make a new paragraph, i.e one empty line (one or two newlines). */ void para() { if (col != 1) newline(); newline(); } /*====================================================================== clear() Clear the screen. */ void clear() { #ifdef GLK g_vm->glk_window_clear(glkMainWin); #else #ifdef HAVE_ANSI if (!statusflg) return; printf("\x1b[2J"); printf("\x1b[%d;1H", paglen); #endif #endif } /*====================================================================== allocate() Safely allocate new memory. */ void *allocate(unsigned long len /* IN - Length to allocate */) { void *p = (void *)malloc((size_t)len); if (p == NULL) syserr("Out of memory."); return p; } /*---------------------------------------------------------------------- just() Justify a string so that it wraps at end of screen. */ static void just(char str[]) { #ifdef GLK logprint(str); #else int i; char ch; if (col >= pagwidth && !skipsp) newline(); while (strlen(str) > pagwidth - col) { i = pagwidth - col - 1; while (!isSpace(str[i]) && i > 0) /* First find wrap point */ i--; if (i == 0 && col == 1) /* If it doesn't fit at all */ /* Wrap immediately after this word */ while (!isSpace(str[i]) && str[i] != '\0') i++; if (i > 0) { /* If it fits ... */ ch = str[i]; /* Save space or NULL */ str[i] = '\0'; /* Terminate string */ logprint(str); /* and print it */ skipsp = FALSE; /* If skipping, now we're done */ str[i] = ch; /* Restore character */ /* Skip white after printed portion */ for (str = &str[i]; isSpace(str[0]) && str[0] != '\0'; str++); } newline(); /* Then start a new line */ } logprint(str); /* Print tail */ col = col + strlen(str); /* Update column */ #endif } /*---------------------------------------------------------------------- space() Output a space if needed. */ static void space() { if (skipsp) skipsp = FALSE; else { if (needsp) { logprint(" "); col++; } } needsp = FALSE; } /*---------------------------------------------------------------------- sayparam() A parameter needs to be said, check for words the player used and use them if possible. */ static void sayparam(int p) { int i; for (i = 0; i <= p; i++) if (params[i].code == EOF) syserr("Nonexistent parameter referenced."); if (params[p].firstWord == EOF) /* Any words he used? */ say(params[p].code); else /* Yes, so use them... */ for (i = params[p].firstWord; i <= params[p].lastWord; i++) { just((char *)addrTo(dict[wrds[i]].wrd)); if (i < params[p].lastWord) just(" "); } } /*---------------------------------------------------------------------- prsym() Print an expanded symbolic reference. N = newline I = indent on a new line P = new paragraph L = current location name O = current object -> first parameter! V = current verb A = current actor T = tabulation $ = no space needed after this */ static void prsym( char *str /* IN - The string starting with '$' */ ) { switch (toLower(str[1])) { case 'n': newline(); needsp = FALSE; break; case 'i': newline(); logprint(" "); col = 5; needsp = FALSE; break; case 'o': sayparam(0); needsp = TRUE; /* We did print something non-white */ break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': sayparam(str[1]-'1'); needsp = TRUE; /* We did print something non-white */ break; case 'l': say(cur.loc); needsp = TRUE; /* We did print something non-white */ break; case 'a': say(cur.act); needsp = TRUE; /* We did print something non-white */ break; case 'v': just((char *)addrTo(dict[vrbwrd].wrd)); needsp = TRUE; /* We did print something non-white */ break; case 'p': para(); needsp = FALSE; break; case 't': { int i; int spaces = 4-(col-1)%4; for (i = 0; i 0) { just(str); /* Output part before '$' */ if (str[strlen(str)-1] == ' ') needsp = FALSE; } *symptr = ch; /* restore '$' */ prsym(symptr); /* Print the symbolic reference */ str = &symptr[2]; /* Advance to after symbol and continue */ } if (str[0] != 0) { just(str); /* Output trailing part */ skipsp = FALSE; if (str[strlen(str)-1] != ' ') needsp = TRUE; } anyOutput = TRUE; free(copy); } /*====================================================================== prmsg() Print a message from the message table. */ void prmsg(MsgKind msg /* IN - message number */) { interpret(msgs[msg].stms); } /*----------------------------------------------------------------------*\ Various check functions endOfTable() isObj, isLoc, isAct, IsCnt & isNum \*----------------------------------------------------------------------*/ /* How to know we are at end of a table */ Boolean eot(Aword *adr) { return *adr == EOF; } Boolean isObj(Aword x) { return x >= OBJMIN && x <= OBJMAX; } Boolean isCnt(Aword x) { return (x >= CNTMIN && x <= CNTMAX) || (isObj(x) && objs[x-OBJMIN].cont != 0) || (isAct(x) && acts[x-ACTMIN].cont != 0); } Boolean isAct(Aword x) { return x >= ACTMIN && x <= ACTMAX; } Boolean isLoc(Aword x) { return x >= LOCMIN && x <= LOCMAX; } Boolean isNum(Aword x) { return x >= LITMIN && x <= LITMAX && litValues[x-LITMIN].type == TYPNUM; } Boolean isStr(Aword x) { return x >= LITMIN && x <= LITMAX && litValues[x-LITMIN].type == TYPSTR; } Boolean isLit(Aword x) { return x >= LITMIN && x <= LITMAX; } /*====================================================================== exitto() Is there an exit from one location to another? */ Boolean exitto(int to, int from) { ExtElem *ext; if (locs[from-LOCMIN].exts == 0) return(FALSE); /* No exits */ for (ext = (ExtElem *) addrTo(locs[from-LOCMIN].exts); !endOfTable(ext); ext++) if (ext->next == to) return(TRUE); return(FALSE); } #ifdef CHECKOBJ /*====================================================================== checkobj() Check that the object given is valid, else print an error message or find out what he wanted. This routine is not used any longer, kept for sentimental reasons ;-) */ void checkobj(obj) Aword *obj; { Aword oldobj; if (*obj != EOF) return; oldobj = EOF; for (cur.obj = OBJMIN; cur.obj <= OBJMAX; cur.obj++) { /* If an object is present and it is possible to perform his action */ if (isHere(cur.obj) && possible()) if (oldobj == EOF) oldobj = cur.obj; else error(WANT); /* And we didn't find multiple objects */ } if (oldobj == EOF) error(WANT); /* But we found ONE */ *obj = cur.obj = oldobj; output("($o)"); /* Then he surely meant this object */ } #endif /*---------------------------------------------------------------------- count() Count the number of items in a container. */ static int count(int cnt /* IN - the container to count */) { int i, j = 0; for (i = OBJMIN; i <= OBJMAX; i++) if (in(i, cnt)) /* Then it's in this container also */ j++; return(j); } /*---------------------------------------------------------------------- sumatr() Sum the values of one attribute in a container. Recursively. */ static int sumatr( Aword atr, /* IN - the attribute to sum over */ Aword cnt /* IN - the container to sum */ ) { int i; int sum = 0; for (i = OBJMIN; i <= OBJMAX; i++) if (objs[i-OBJMIN].loc == cnt) { /* Then it's in this container */ if (objs[i-OBJMIN].cont != 0) /* This is also a container! */ sum = sum + sumatr(atr, i); sum = sum + attribute(i, atr); } return(sum); } /*====================================================================== checklim() Checks if a limit for a container is exceeded. */ Boolean checklim( Aword cnt, /* IN - Container code */ Aword obj /* IN - The object to add */ ) { LimElem *lim; Aword props; fail = TRUE; if (!isCnt(cnt)) syserr("Checking limits for a non-container."); /* Find the container properties */ if (isObj(cnt)) props = objs[cnt-OBJMIN].cont; else if (isAct(cnt)) props = acts[cnt-ACTMIN].cont; else props = cnt; if (cnts[props-CNTMIN].lims != 0) { /* Any limits at all? */ for (lim = (LimElem *) addrTo(cnts[props-CNTMIN].lims); !endOfTable(lim); lim++) if (lim->atr == 0) { if (count(cnt) >= lim->val) { interpret(lim->stms); return(TRUE); /* Limit check failed */ } } else { if (sumatr(lim->atr, cnt) + attribute(obj, lim->atr) > lim->val) { interpret(lim->stms); return(TRUE); } } } fail = FALSE; return(FALSE); } /*----------------------------------------------------------------------*\ Action routines \*----------------------------------------------------------------------*/ /*---------------------------------------------------------------------- trycheck() Tries a check, returns TRUE if it passed, FALSE else. */ static Boolean trycheck( Aaddr adr, /* IN - ACODE address to check table */ Boolean act /* IN - Act if it fails ? */ ) { ChkElem *chk; chk = (ChkElem *) addrTo(adr); if (chk->exp == 0) { interpret(chk->stms); return(FALSE); } else { while (!endOfTable(chk)) { interpret(chk->exp); if (!(Abool)pop()) { if (act) interpret(chk->stms); return(FALSE); } chk++; } return(TRUE); } } /*====================================================================== go() Move hero in a direction. */ void go(int dir) { ExtElem *ext; Boolean ok; Aword oldloc; ext = (ExtElem *) addrTo(locs[cur.loc-LOCMIN].exts); if (locs[cur.loc-LOCMIN].exts != 0) while (!endOfTable(ext)) { if (ext->code == dir) { ok = TRUE; if (ext->checks != 0) { if (trcflg) { printf("\n\n"); } ok = trycheck(ext->checks, TRUE); } if (ok) { oldloc = cur.loc; if (ext->action != 0) { if (trcflg) { printf("\n\n"); } interpret(ext->action); } /* Still at the same place? */ if (where(HERO) == oldloc) { if (trcflg) { printf("\n\n"); } locate(HERO, ext->next); } } return; } ext++; } error(M_NO_WAY); } /*---------------------------------------------------------------------- findalt() Find the verb alternative wanted in a verb list and return the address to it. */ static AltElem *findalt( Aword vrbsadr, /* IN - Address to start of list */ Aword param /* IN - Which parameter to match */ ) { VrbElem *vrb; AltElem *alt; if (vrbsadr == 0) return(NULL); for (vrb = (VrbElem *) addrTo(vrbsadr); !endOfTable(vrb); vrb++) if (vrb->code == cur.vrb) { for (alt = (AltElem *) addrTo(vrb->alts); !endOfTable(alt); alt++) if (alt->param == param || alt->param == 0) return alt; return NULL; } return NULL; } /*====================================================================== possible() Check if current action is possible according to the CHECKs. */ Boolean possible() { AltElem *alt[MAXPARAMS+2]; /* List of alt-pointers, one for each param */ int i; /* Parameter index */ fail = FALSE; alt[0] = findalt(header->vrbs, 0); /* Perform global checks */ if (alt[0] != 0 && alt[0]->checks != 0) { if (!trycheck(alt[0]->checks, FALSE)) return FALSE; if (fail) return FALSE; } /* Now CHECKs in this location */ alt[1] = findalt(locs[cur.loc-LOCMIN].vrbs, 0); if (alt[1] != 0 && alt[1]->checks != 0) if (!trycheck(alt[1]->checks, FALSE)) return FALSE; for (i = 0; params[i].code != EOF; i++) { alt[i+2] = findalt(objs[params[i].code-OBJMIN].vrbs, i+1); /* CHECKs in a possible parameter */ if (alt[i+2] != 0 && alt[i+2]->checks != 0) if (!trycheck(alt[i+2]->checks, FALSE)) return FALSE; } for (i = 0; i < 2 || params[i-2].code != EOF; i++) if (alt[i] != 0 && alt[i]->action != 0) break; if (i >= 2 && params[i-2].code == EOF) /* Didn't find any code for this verb/object combination */ return FALSE; else return TRUE; } /*---------------------------------------------------------------------- do_it() Execute the action commanded by hero. */ static void do_it() { AltElem *alt[MAXPARAMS+2]; /* List of alt-pointers, one for each param */ Boolean done[MAXPARAMS+2]; /* Is it done */ int i; /* Parameter index */ char trace[80]; /* Trace string buffer */ fail = FALSE; alt[0] = findalt(header->vrbs, 0); /* Perform global checks */ if (alt[0] != 0 && alt[0]->checks != 0) { if (trcflg) printf("\n\n", cur.vrb); if (!trycheck(alt[0]->checks, TRUE)) return; if (fail) return; } /* Now CHECKs in this location */ alt[1] = findalt(locs[cur.loc-LOCMIN].vrbs, 0); if (alt[1] != 0 && alt[1]->checks != 0) { if (trcflg) printf("\n\n", cur.vrb); if (!trycheck(alt[1]->checks, TRUE)) return; if (fail) return; } for (i = 0; params[i].code != EOF; i++) { if (isLit(params[i].code)) alt[i+2] = 0; else { if (isObj(params[i].code)) alt[i+2] = findalt(objs[params[i].code-OBJMIN].vrbs, i+1); else if (isAct(params[i].code)) alt[i+2] = findalt(acts[params[i].code-ACTMIN].vrbs, i+1); else syserr("Illegal parameter type."); /* CHECKs in the parameters */ if (alt[i+2] != 0 && alt[i+2]->checks != 0) { if (trcflg) printf("\n\n", cur.vrb, i); if (!trycheck(alt[i+2]->checks, TRUE)) return; if (fail) return; } } } /* Check for anything to execute... */ for (i = 0; i < 2 || params[i-2].code != EOF; i++) if (alt[i] != 0 && alt[i]->action != 0) break; if (i >= 2 && params[i-2].code == EOF) /* Didn't find any code for this verb/object combination */ error(M_CANT0); /* Perform actions! */ /* First try any BEFORE or ONLY from outside in */ done[0] = FALSE; done[1] = FALSE; for (i = 2; params[i-2].code != EOF; i++) done[i] = FALSE; i--; while (i >= 0) { if (alt[i] != 0) if (alt[i]->qual == (Aword)Q_BEFORE || alt[i]->qual == (Aword)Q_ONLY) { if (alt[i]->action != 0) { if (trcflg) { if (i == 0) strcpy(trace, "GLOBAL"); else if (i == 1) strcpy(trace, "in LOCATION"); else sprintf(trace, "in PARAMETER %d", i-1); if (alt[i]->qual == (Aword)Q_BEFORE) printf("\n\n", cur.vrb, trace); else printf("\n\n", cur.vrb, trace); } interpret(alt[i]->action); if (fail) return; if (alt[i]->qual == (Aword)Q_ONLY) return; } done[i] = TRUE; } i--; } /* Then execute any not declared as AFTER, i.e. the default */ for (i = 0; i < 2 || params[i-2].code != EOF; i++) { if (alt[i] != 0) if (alt[i]->qual != (Aword)Q_AFTER) { if (!done[i] && alt[i]->action != 0) { if (trcflg) { if (i == 0) strcpy(trace, "GLOBAL"); else if (i == 1) strcpy(trace, "in LOCATION"); else sprintf(trace, "in PARAMETER %d", i-1); printf("\n\n", cur.vrb, trace); } interpret(alt[i]->action); if (fail) return; } done[i] = TRUE; } } /* Finally, the ones declared as after */ i--; while (i >= 0) { if (alt[i] != 0) if (!done[i] && alt[i]->action != 0) { if (trcflg) { if (i == 0) strcpy(trace, "GLOBAL"); else if (i == 1) strcpy(trace, "in LOCATION"); else sprintf(trace, "in PARAMETER %d", i-1); printf("\n\n", cur.vrb, trace); } interpret(alt[i]->action); if (fail) return; } i--; } } /*====================================================================== action() Execute all activities commanded. Handles possible multiple actions such as THEM or lists of objects. */ void action(ParamElem plst[] /* IN - Plural parameter list */) { int i, mpos; char marker[10]; if (plural) { /* The code == 0 means this is a multiple position. We must loop over this position (and replace it by each present in the plst) */ for (mpos = 0; params[mpos].code != 0; mpos++); /* Find multiple position */ sprintf(marker, "($%d)", mpos+1); /* Prepare a printout with $1/2/3 */ for (i = 0; plst[i].code != EOF; i++) { params[mpos] = plst[i]; output(marker); do_it(); if (plst[i+1].code != EOF) para(); } params[mpos].code = 0; } else do_it(); } /*----------------------------------------------------------------------*\ Event Handling eventchk() \*----------------------------------------------------------------------*/ /*---------------------------------------------------------------------- eventchk() Check if any events are pending. If so execute them. */ static void eventchk() { while (etop != 0 && eventq[etop-1].time == cur.tick) { etop--; if (isLoc(eventq[etop].where)) cur.loc = eventq[etop].where; else cur.loc = where(eventq[etop].where); if (trcflg) { printf("\n\n"); } interpret(evts[eventq[etop].event-EVTMIN].code); } } /*----------------------------------------------------------------------*\ Main program and initialisation codfil filenames checkvers() load() checkdebug() initheader() initstrings() start() init() main() \*----------------------------------------------------------------------*/ Common::SeekableReadStream *codfil; char codfnm[256]; static char txtfnm[256]; static char logfnm[256]; /*---------------------------------------------------------------------- checkvers() */ static void checkvers(AcdHdr *header) { char vers[4]; char state[2]; /* Construct our own version */ vers[0] = alan.version.version; vers[1] = alan.version.revision; /* Check version of .ACD file */ if (dbgflg) { state[0] = header->vers[3]; state[1] = '\0'; printf("", advnam, (int)(header->vers[0]), (int)(header->vers[1]), (int)(header->vers[2]), (header->vers[3])==0? "": state); newline(); } /* Compatible if version and revision match... */ if (strncmp(header->vers, vers, 2) != 0) { #ifdef V25COMPATIBLE if (header->vers[0] == 2 && header->vers[1] == 5) /* Check for 2.5 version */ /* This we can convert later if needed... */; else #endif #ifdef V27COMPATIBLE if (header->vers[0] == 2 && header->vers[1] == 7) /* Check for 2.7 version */ /* This we can convert later if needed... */; else #endif if (errflg) { char str[80]; sprintf(str, "Incompatible version of ACODE program. Game is %ld.%ld, interpreter %ld.%ld.", (long) (header->vers[0]), (long) (header->vers[1]), (long) alan.version.version, (long) alan.version.revision); syserr(str); } else output("\n"); } } /*---------------------------------------------------------------------- load() */ static void load() { AcdHdr tmphdr; Aword crc = 0; int i; char err[100]; Aword *ptr = (Aword *)&tmphdr + 1; codfil->seek(0); codfil->read(&tmphdr.vers[0], 4); for (i = 1; i < sizeof(tmphdr) / sizeof(Aword); ++i, ++ptr) *ptr = codfil->readUint32BE(); checkvers(&tmphdr); /* Allocate and load memory */ /* No memory allocated yet? */ if (memory == NULL) { #ifdef V25COMPATIBLE if (tmphdr.vers[0] == 2 && tmphdr.vers[1] == 5) /* We need some more memory to expand 2.5 format*/ memory = allocate((tmphdr.size+tmphdr.objmax-tmphdr.objmin+1+2)*sizeof(Aword)); else #endif memory = (Aword *)allocate(tmphdr.size * sizeof(Aword)); } memTop = tmphdr.size; header = (AcdHdr *) addrTo(0); if ((tmphdr.size * sizeof(Aword)) > codfil->size()) ::error("Header size is greater than filesize"); codfil->seek(0); codfil->read(&header->vers[0], 4); for (i = 1, ptr = memory + 1; i < tmphdr.size; ++i, ++ptr) *ptr = codfil->readUint32LE(); /* Calculate checksum */ for (i = sizeof(tmphdr)/sizeof(Aword); i < memTop; i++) { crc += memory[i]&0xff; crc += (memory[i]>>8)&0xff; crc += (memory[i]>>16)&0xff; crc += (memory[i]>>24)&0xff; #ifdef CRCLOG printf("%6x\t%6lx\t%6lx\n", i, crc, memory[i]); #endif } if (crc != tmphdr.acdcrc) { sprintf(err, "Checksum error in .ACD file (0x%lx instead of 0x%lx).", (unsigned long) crc, (unsigned long) tmphdr.acdcrc); if (errflg) syserr(err); else { output("$n"); } } #if defined(SCUMM_LITTLE_ENDIAN) if (dbgflg||trcflg||stpflg) output("$n"); #endif #ifdef V25COMPATIBLE /* Check for 2.5 version */ if (tmphdr.vers[0] == 2 && tmphdr.vers[1] == 5) { if (dbgflg||trcflg||stpflg) output("$n"); } #endif } /*---------------------------------------------------------------------- checkdebug() */ static void checkdebug() { /* Make sure he can't debug if not allowed! */ if (!header->debug) { if (dbgflg|trcflg|stpflg) printf("\n", advnam); para(); dbgflg = FALSE; trcflg = FALSE; stpflg = FALSE; } #ifndef GLK if (dbgflg) /* If debugging */ srand(0); /* use no randomization */ else srand(time(0)); /* seed random generator */ #endif } /*---------------------------------------------------------------------- initheader() */ static void initheader() { dict = (WrdElem *) addrTo(header->dict); /* Find out number of entries in dictionary */ for (dictsize = 0; !endOfTable(&dict[dictsize]); dictsize++); vrbs = (VrbElem *) addrTo(header->vrbs); stxs = (StxElem *) addrTo(header->stxs); locs = (LocElem *) addrTo(header->locs); acts = (ActElem *) addrTo(header->acts); objs = (ObjElem *) addrTo(header->objs); evts = (EvtElem *) addrTo(header->evts); cnts = (CntElem *) addrTo(header->cnts); ruls = (RulElem *) addrTo(header->ruls); msgs = (MsgElem *) addrTo(header->msgs); scores = (Aword *) addrTo(header->scores); if (header->pack) freq = (Aword *) addrTo(header->freq); } /*---------------------------------------------------------------------- initstrings() */ static void initstrings() { IniElem *init; for (init = (IniElem *) addrTo(header->init); !endOfTable(init); init++) { getstr(init->fpos, init->len); memory[init->adr] = pop(); } } /*---------------------------------------------------------------------- start() */ static void start() { int startloc; cur.tick = -1; cur.loc = startloc = where(HERO); cur.act = HERO; cur.score = 0; if (trcflg) printf("\n\n"); interpret(header->start); para(); acts[HERO-ACTMIN].loc = 0; locate(HERO, startloc); } /*---------------------------------------------------------------------- init() Initialization, program load etc. */ static void init() { int i; /* Initialise some status */ etop = 0; /* No pending events */ looking = FALSE; /* Not looking now */ dscrstkp = 0; /* No describe in progress */ load(); initheader(); checkdebug(); /* Initialise string attributes */ initstrings(); getPageSize(); /* Find first conjunction and use that for ',' handling */ for (i = 0; i < dictsize; i++) if (isConj(i)) { conjWord = i; break; } /* Start the adventure */ clear(); start(); } /*---------------------------------------------------------------------- movactor() Let the current actor move. If player, ask him. */ static void movactor() { ScrElem *scr; StepElem *step; ActElem *act = (ActElem *) &acts[cur.act-ACTMIN]; cur.loc = where(cur.act); if (cur.act == HERO) { parse(); if (g_vm->shouldQuit()) return; fail = FALSE; /* fail only aborts one actor */ rules(); } else if (act->script != 0) { for (scr = (ScrElem *) addrTo(act->scradr); !endOfTable(scr); scr++) if (scr->code == act->script) { /* Find correct step in the list by indexing */ step = (StepElem *) addrTo(scr->steps); step = (StepElem *) &step[act->step]; /* Now execute it, maybe. First check wait count */ if (step->after > act->count) { /* Wait some more */ if (trcflg) { printf("\n\n", act->script, act->step+1, step->after-act->count); } act->count++; rules(); return; } else act->count = 0; /* Then check possible expression */ if (step->exp != 0) { if (trcflg) { printf("\n\n", act->script, act->step+1); } interpret(step->exp); if (!(Abool)pop()) { rules(); return; /* Hadn't happened yet */ } } /* OK, so finally let him do his thing */ act->step++; /* Increment step number before executing... */ if (trcflg) { printf("\n\n", act->script, act->step); } interpret(step->stm); step++; /* ... so that we can see if he is USEing another script now */ if (act->step != 0 && endOfTable(step)) /* No more steps in this script, so stop him */ act->script = 0; fail = FALSE; /* fail only aborts one actor */ rules(); return; } syserr("Unknown actor script."); } else if (trcflg) { printf("\n\n"); rules(); return; } } /*---------------------------------------------------------------------- openFiles() Open the necessary files. */ static void openFiles() { char str[256]; char *usr = ""; time_t tick; #ifndef GLK /* Open Acode file */ strcpy(codfnm, advnam); strcat(codfnm, ".acd"); if ((codfil = fopen(codfnm, READ_MODE)) == NULL) { strcpy(str, "Can't open adventure code file '"); strcat(str, codfnm); strcat(str, "'."); syserr(str); } #endif #ifdef GARGLK { char *s = strrchr(codfnm, '\\'); if (!s) s = strrchr(codfnm, '/'); g_vm->garglk_set_story_name(s ? s + 1 : codfnm); } #endif /* Open Text file */ strcpy(txtfnm, advnam); strcat(txtfnm, ".dat"); Common::File *f = new Common::File(); if (!f->open(txtfnm)) { delete f; Common::String s = Common::String::format("Can't open adventure text data file '%s'.", txtfnm); ::error(s.c_str()); } // If logging open log file if (logflg) { sprintf(logfnm, "%s.log", advnam); logfil = g_system->getSavefileManager()->openForSaving(logfnm); logflg = logfil != nullptr; } } /*====================================================================== run() Run the adventure */ void run() { openFiles(); // Set default line and column col = lin = 1; //setjmp(restart_label); /* Return here if he wanted to restart */ init(); /* Load, initialise and start the adventure */ while (TRUE) { #ifdef MALLOC if (malloc_verify() == 0) syserr("Error in heap."); #endif if (dbgflg) debug(); eventchk(); cur.tick++; // (void) setjmp(jmpbuf); // Move all characters for (cur.act = ACTMIN; cur.act <= ACTMAX; cur.act++) { movactor(); if (g_vm->shouldQuit()) return; } } } } // End of namespace Alan2 } // End of namespace Glk