diff options
Diffstat (limited to 'engines/glk/level9/level9_main.cpp')
-rw-r--r-- | engines/glk/level9/level9_main.cpp | 3599 |
1 files changed, 3599 insertions, 0 deletions
diff --git a/engines/glk/level9/level9_main.cpp b/engines/glk/level9/level9_main.cpp new file mode 100644 index 0000000000..719d05bcb8 --- /dev/null +++ b/engines/glk/level9/level9_main.cpp @@ -0,0 +1,3599 @@ +/* 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. + * + */ + +/* + * The input routine will repond to the following 'hash' commands + * #save saves position file directly (bypasses any + * disk change prompts) + * #restore restores position file directly (bypasses any + * protection code) + * #quit terminates current game, RunGame() will return FALSE + * #cheat tries to bypass restore protection on v3,4 games + * (can be slow) + * #dictionary lists game dictionary (press a key to interrupt) + * #picture <n> show picture <n> + * #seed <n> set the random number seed to the value <n> + * #play plays back a file as the input to the game + * +\***********************************************************************/ + +#include "glk/level9/level9_main.h" +#include "glk/level9/level9.h" +#include "common/file.h" +#include "common/system.h" + +namespace Glk { +namespace Level9 { + +/* #define L9DEBUG */ +/* #define CODEFOLLOW */ +/* #define FULLSCAN */ + +/* "L901" */ +#define L9_ID 0x4c393031 + +#define IBUFFSIZE 500 +#define RAMSAVESLOTS 10 +#define GFXSTACKSIZE 100 +#define FIRSTLINESIZE 96 + +/* Typedefs */ +typedef struct { + L9UINT16 vartable[256]; + L9BYTE listarea[LISTAREASIZE]; +} SaveStruct; + +/* Enumerations */ +enum L9GameTypes { L9_V1, L9_V2, L9_V3, L9_V4 }; +enum L9MsgTypes { MSGT_V1, MSGT_V2 }; +/* + Graphics type Resolution Scale stack reset + ------------------------------------------------- + GFX_V2 160 x 128 yes + GFX_V3A 160 x 96 yes + GFX_V3B 160 x 96 no + GFX_V3C 320 x 96 no +*/ +enum L9GfxTypes { GFX_V2, GFX_V3A, GFX_V3B, GFX_V3C }; + +/* Global Variables */ +L9BYTE *startfile = NULL, *pictureaddress = NULL, *picturedata = NULL; +L9BYTE *startdata; +L9UINT32 FileSize, picturesize; + +L9BYTE *L9Pointers[12]; +L9BYTE *absdatablock, *list2ptr, *list3ptr, *list9startptr, *acodeptr; +L9BYTE *startmd, *endmd, *endwdp5, *wordtable, *dictdata, *defdict; +L9UINT16 dictdatalen; +L9BYTE *startmdV2; + +int wordcase; +int unpackcount; +char unpackbuf[8]; +L9BYTE *dictptr; +char threechars[34]; +int L9GameType; +int L9MsgType; +char LastGame[MAX_PATH]; +char FirstLine[FIRSTLINESIZE]; +int FirstLinePos = 0; +int FirstPicture = -1; + +#if defined(AMIGA) && defined(_DCC) +__far SaveStruct ramsavearea[RAMSAVESLOTS]; +#else +SaveStruct ramsavearea[RAMSAVESLOTS]; +#endif + +GameState workspace; + +L9UINT16 randomseed; +L9UINT16 constseed = 0; +L9BOOL Running; + +char ibuff[IBUFFSIZE]; +L9BYTE *ibuffptr; +char obuff[34]; +Common::SeekableReadStream *scriptfile = NULL; + +L9BOOL Cheating = FALSE; +int CheatWord; +GameState CheatWorkspace; + +int reflectflag, scale, gintcolour, option; +int l9textmode = 0, drawx = 0, drawy = 0, screencalled = 0, showtitle = 1; +L9BYTE *gfxa5 = NULL; +Bitmap *bitmap = NULL; +int gfx_mode = GFX_V2; + +L9BYTE *GfxA5Stack[GFXSTACKSIZE]; +int GfxA5StackPos = 0; +int GfxScaleStack[GFXSTACKSIZE]; +int GfxScaleStackPos = 0; + +char lastchar = '.'; +char lastactualchar = 0; +int d5; + +L9BYTE *codeptr; /* instruction codes */ +L9BYTE code; + +L9BYTE *list9ptr; + +int unpackd3; + +L9BYTE exitreversaltable[16] = {0x00, 0x04, 0x06, 0x07, 0x01, 0x08, 0x02, 0x03, 0x05, 0x0a, 0x09, 0x0c, 0x0b, 0xff, 0xff, 0x0f}; + +L9UINT16 gnostack[128]; +L9BYTE gnoscratch[32]; +int object, gnosp, numobjectfound, searchdepth, inithisearchpos; + + +struct L9V1GameInfo { + L9BYTE dictVal1, dictVal2; + int dictStart, L9Ptrs[5], absData, msgStart, msgLen; +}; +struct L9V1GameInfo L9V1Games[] = { + 0x1a, 0x24, 301, 0x0000, -0x004b, 0x0080, -0x002b, 0x00d0, 0x03b0, 0x0f80, 0x4857, /* Colossal Adventure */ + 0x20, 0x3b, 283, -0x0583, 0x0000, -0x0508, -0x04e0, 0x0000, 0x0800, 0x1000, 0x39d1, /* Adventure Quest */ + 0x14, 0xff, 153, -0x00d6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0a20, 0x16bf, 0x420d, /* Dungeon Adventure */ + 0x15, 0x5d, 252, -0x3e70, 0x0000, -0x3d30, -0x3ca0, 0x0100, 0x4120, -0x3b9d, 0x3988, /* Lords of Time */ + 0x15, 0x6c, 284, -0x00f0, 0x0000, -0x0050, -0x0050, -0x0050, 0x0300, 0x1930, 0x3c17, /* Snowball */ +}; +int L9V1Game = -1; + + +/* Prototypes */ +L9BOOL LoadGame2(char *filename, char *picname); +int getlongcode(void); +L9BOOL GetWordV2(char *buff, int Word); +L9BOOL GetWordV3(char *buff, int Word); +void show_picture(int pic); + + +#ifdef CODEFOLLOW +#define CODEFOLLOWFILE "c:\\temp\\level9.txt" +FILE *f; +L9UINT16 *cfvar, *cfvar2; +char *codes[] = { + "Goto", + "intgosub", + "intreturn", + "printnumber", + "messagev", + "messagec", + "function", + "input", + "varcon", + "varvar", + "_add", + "_sub", + "ilins", + "ilins", + "jump", + "Exit", + "ifeqvt", + "ifnevt", + "ifltvt", + "ifgtvt", + "screen", + "cleartg", + "picture", + "getnextobject", + "ifeqct", + "ifnect", + "ifltct", + "ifgtct", + "printinput", + "ilins", + "ilins", + "ilins", +}; +char *functions[] = { + "calldriver", + "L9Random", + "save", + "restore", + "clearworkspace", + "clearstack" +}; +char *drivercalls[] = { + "init", + "drivercalcchecksum", + "driveroswrch", + "driverosrdch", + "driverinputline", + "driversavefile", + "driverloadfile", + "settext", + "resettask", + "returntogem", + "10 *", + "loadgamedatafile", + "randomnumber", + "13 *", + "driver14", + "15 *", + "driverclg", + "line", + "fill", + "driverchgcol", + "20 *", + "21 *", + "ramsave", + "ramload", + "24 *", + "lensdisplay", + "26 *", + "27 *", + "28 *", + "29 *", + "allocspace", + "31 *", + "showbitmap", + "33 *", + "checkfordisc" +}; +#endif + +void initdict(L9BYTE *ptr) { + dictptr = ptr; + unpackcount = 8; +} + +char getdictionarycode(void) { + if (unpackcount != 8) return unpackbuf[unpackcount++]; + else { + /* unpackbytes */ + L9BYTE d1 = *dictptr++, d2; + unpackbuf[0] = d1 >> 3; + d2 = *dictptr++; + unpackbuf[1] = ((d2 >> 6) + (d1 << 2)) & 0x1f; + d1 = *dictptr++; + unpackbuf[2] = (d2 >> 1) & 0x1f; + unpackbuf[3] = ((d1 >> 4) + (d2 << 4)) & 0x1f; + d2 = *dictptr++; + unpackbuf[4] = ((d1 << 1) + (d2 >> 7)) & 0x1f; + d1 = *dictptr++; + unpackbuf[5] = (d2 >> 2) & 0x1f; + unpackbuf[6] = ((d2 << 3) + (d1 >> 5)) & 0x1f; + unpackbuf[7] = d1 & 0x1f; + unpackcount = 1; + return unpackbuf[0]; + } +} + +int getdictionary(int d0) { + if (d0 >= 0x1a) return getlongcode(); + else return d0 + 0x61; +} + +int getlongcode(void) { + int d0, d1; + d0 = getdictionarycode(); + if (d0 == 0x10) { + wordcase = 1; + d0 = getdictionarycode(); + return getdictionary(d0); /* reentrant? */ + } + d1 = getdictionarycode(); + return 0x80 | ((d0 << 5) & 0xe0) | (d1 & 0x1f); +} + +void printchar(char c) { + if (Cheating) return; + + if (c & 128) + lastchar = (c &= 0x7f); + else if (c != 0x20 && c != 0x0d && (c < '\"' || c >= '.')) { + if (lastchar == '!' || lastchar == '?' || lastchar == '.') c = toupper(c); + lastchar = c; + } + /* eat multiple CRs */ + if (c != 0x0d || lastactualchar != 0x0d) { + os_printchar(c); + if (FirstLinePos < FIRSTLINESIZE - 1) + FirstLine[FirstLinePos++] = tolower(c); + } + lastactualchar = c; +} + +void printstring(const char *buf) { + int i; + for (i = 0; i < (int) strlen(buf); i++) printchar(buf[i]); +} + +void printdecimald0(int d0) { + char temp[12]; + sprintf(temp, "%d", d0); + printstring(temp); +} + +void printautocase(int d0) { + if (d0 & 128) printchar((char) d0); + else { + if (wordcase) printchar((char) toupper(d0)); + else if (d5 < 6) printchar((char) d0); + else { + wordcase = 0; + printchar((char) toupper(d0)); + } + } +} + +void displaywordref(L9UINT16 Off) { + static int mdtmode = 0; + + wordcase = 0; + d5 = (Off >> 12) & 7; + Off &= 0xfff; + if (Off < 0xf80) { + /* dwr01 */ + L9BYTE *a0, *oPtr, *a3; + int d0, d2, i; + + if (mdtmode == 1) printchar(0x20); + mdtmode = 1; + + /* setindex */ + a0 = dictdata; + d2 = dictdatalen; + + /* dwr02 */ + oPtr = a0; + while (d2 && Off >= L9WORD(a0 + 2)) { + a0 += 4; + d2--; + } + /* dwr04 */ + if (a0 == oPtr) { + a0 = defdict; + } else { + a0 -= 4; + Off -= L9WORD(a0 + 2); + a0 = startdata + L9WORD(a0); + } + /* dwr04b */ + Off++; + initdict(a0); + a3 = (L9BYTE *) threechars; /* a3 not set in original, prevent possible spam */ + + /* dwr05 */ + while (TRUE) { + d0 = getdictionarycode(); + if (d0 < 0x1c) { + /* dwr06 */ + if (d0 >= 0x1a) d0 = getlongcode(); + else d0 += 0x61; + *a3++ = d0; + } else { + d0 &= 3; + a3 = (L9BYTE *) threechars + d0; + if (--Off == 0) break; + } + } + for (i = 0; i < d0; i++) printautocase(threechars[i]); + + /* dwr10 */ + while (TRUE) { + d0 = getdictionarycode(); + if (d0 >= 0x1b) return; + printautocase(getdictionary(d0)); + } + } + + else { + if (d5 & 2) printchar(0x20); /* prespace */ + mdtmode = 2; + Off &= 0x7f; + if (Off != 0x7e) printchar((char)Off); + if (d5 & 1) printchar(0x20); /* postspace */ + } +} + +int getmdlength(L9BYTE **Ptr) { + int tot = 0, len; + do { + len = (*(*Ptr)++ -1) & 0x3f; + tot += len; + } while (len == 0x3f); + return tot; +} + +void printmessage(int Msg) { + L9BYTE *Msgptr = startmd; + L9BYTE Data; + + int len; + L9UINT16 Off; + + while (Msg > 0 && Msgptr - endmd <= 0) { + Data = *Msgptr; + if (Data & 128) { + Msgptr++; + Msg -= Data & 0x7f; + } else { + len = getmdlength(&Msgptr); + Msgptr += len; + } + Msg--; + } + if (Msg < 0 || *Msgptr & 128) return; + + len = getmdlength(&Msgptr); + if (len == 0) return; + + while (len) { + Data = *Msgptr++; + len--; + if (Data & 128) { + /* long form (reverse word) */ + Off = (Data << 8) + *Msgptr++; + len--; + } else { + Off = (wordtable[Data * 2] << 8) + wordtable[Data * 2 + 1]; + } + if (Off == 0x8f80) break; + displaywordref(Off); + } +} + +/* v2 message stuff */ + +int msglenV2(L9BYTE **ptr) { + int i = 0; + L9BYTE a; + + /* catch berzerking code */ + if (*ptr >= startdata + FileSize) return 0; + + while ((a = **ptr) == 0) { + (*ptr)++; + + if (*ptr >= startdata + FileSize) return 0; + + i += 255; + } + i += a; + return i; +} + +void printcharV2(char c) { + if (c == 0x25) c = 0xd; + else if (c == 0x5f) c = 0x20; + printautocase(c); +} + +void displaywordV2(L9BYTE *ptr, int msg) { + int n; + L9BYTE a; + if (msg == 0) return; + while (--msg) { + ptr += msglenV2(&ptr); + } + n = msglenV2(&ptr); + + while (--n > 0) { + a = *++ptr; + if (a < 3) return; + + if (a >= 0x5e) displaywordV2(startmdV2 - 1, a - 0x5d); + else printcharV2((char)(a + 0x1d)); + } +} + +int msglenV1(L9BYTE **ptr) { + L9BYTE *ptr2 = *ptr; + while (ptr2 < startdata + FileSize && *ptr2++ != 1); + return ptr2 - *ptr; +} + +void displaywordV1(L9BYTE *ptr, int msg) { + int n; + L9BYTE a; + while (msg--) { + ptr += msglenV1(&ptr); + } + n = msglenV1(&ptr); + + while (--n > 0) { + a = *ptr++; + if (a < 3) return; + + if (a >= 0x5e) displaywordV1(startmdV2, a - 0x5e); + else printcharV2((char)(a + 0x1d)); + } +} + +L9BOOL amessageV2(L9BYTE *ptr, int msg, long *w, long *c) { + int n; + L9BYTE a; + static int depth = 0; + if (msg == 0) return FALSE; + while (--msg) { + ptr += msglenV2(&ptr); + } + if (ptr >= startdata + FileSize) return FALSE; + n = msglenV2(&ptr); + + while (--n > 0) { + a = *++ptr; + if (a < 3) return TRUE; + + if (a >= 0x5e) { + if (++depth > 10 || !amessageV2(startmdV2 - 1, a - 0x5d, w, c)) { + depth--; + return FALSE; + } + depth--; + } else { + char ch = a + 0x1d; + if (ch == 0x5f || ch == ' ')(*w)++; + else (*c)++; + } + } + return TRUE; +} + +L9BOOL amessageV1(L9BYTE *ptr, int msg, long *w, long *c) { + int n; + L9BYTE a; + static int depth = 0; + + while (msg--) { + ptr += msglenV1(&ptr); + } + if (ptr >= startdata + FileSize) return FALSE; + n = msglenV1(&ptr); + + while (--n > 0) { + a = *ptr++; + if (a < 3) return TRUE; + + if (a >= 0x5e) { + if (++depth > 10 || !amessageV1(startmdV2, a - 0x5e, w, c)) { + depth--; + return FALSE; + } + depth--; + } else { + char ch = a + 0x1d; + if (ch == 0x5f || ch == ' ')(*w)++; + else (*c)++; + } + } + return TRUE; +} + +L9BOOL analyseV2(double *wl) { + long words = 0, chars = 0; + int i; + for (i = 1; i < 256; i++) { + long w = 0, c = 0; + if (amessageV2(startmd, i, &w, &c)) { + words += w; + chars += c; + } else return FALSE; + } + *wl = words ? (double) chars / words : 0.0; + return TRUE; +} + +L9BOOL analyseV1(double *wl) { + long words = 0, chars = 0; + int i; + for (i = 0; i < 256; i++) { + long w = 0, c = 0; + if (amessageV1(startmd, i, &w, &c)) { + words += w; + chars += c; + } else return FALSE; + } + + *wl = words ? (double) chars / words : 0.0; + return TRUE; +} + +void printmessageV2(int Msg) { + if (L9MsgType == MSGT_V2) displaywordV2(startmd, Msg); + else displaywordV1(startmd, Msg); +} + +void L9Allocate(L9BYTE **ptr, L9UINT32 Size) { + if (*ptr) free(*ptr); + *ptr = (L9BYTE *)malloc(Size); + if (*ptr == NULL) { + error("Unable to allocate memory for the game! Exiting..."); + } +} + +void FreeMemory(void) { + if (startfile) { + free(startfile); + startfile = NULL; + } + if (pictureaddress) { + free(pictureaddress); + pictureaddress = NULL; + } + if (bitmap) { + free(bitmap); + bitmap = NULL; + } + if (scriptfile) { + delete scriptfile; + scriptfile = NULL; + } + picturedata = NULL; + picturesize = 0; + gfxa5 = NULL; +} + +L9BOOL load(const char *filename) { + Common::File f; + if (!f.open(filename)) + return FALSE; + + if ((FileSize = f.size()) < 256) { + f.close(); + error("File is too small to contain a Level 9 game"); + return FALSE; + } + + L9Allocate(&startfile, FileSize); + if (f.read(startfile, FileSize) != FileSize) { + f.close(); + return FALSE; + } + + f.close(); + return TRUE; +} + +L9UINT16 scanmovewa5d0(L9BYTE *Base, L9UINT32 *Pos) { + L9UINT16 ret = L9WORD(Base + *Pos); + (*Pos) += 2; + return ret; +} + +L9UINT32 scangetaddr(int Code, L9BYTE *Base, L9UINT32 *Pos, L9UINT32 acode, int *Mask) { + (*Mask) |= 0x20; + if (Code & 0x20) { + /* getaddrshort */ + signed char diff = Base[*Pos]; + (*Pos)++; + return (*Pos) + diff - 1; + } else { + return acode + scanmovewa5d0(Base, Pos); + } +} + +void scangetcon(int Code, L9UINT32 *Pos, int *Mask) { + (*Pos)++; + if (!(Code & 64))(*Pos)++; + (*Mask) |= 0x40; +} + +L9BOOL CheckCallDriverV4(L9BYTE *Base, L9UINT32 Pos) { + int i, j; + + /* Look back for an assignment from a variable + * to list9[0], which is used to specify the + * driver call. + */ + for (i = 0; i < 2; i++) { + int x = Pos - ((i + 1) * 3); + if ((Base[x] == 0x89) && (Base[x + 1] == 0x00)) { + /* Get the variable being copied to list9[0] */ + int var = Base[x + 2]; + + /* Look back for an assignment to the variable. */ + for (j = 0; j < 2; j++) { + int y = x - ((j + 1) * 3); + if ((Base[y] == 0x48) && (Base[y + 2] == var)) { + /* If this a V4 driver call? */ + switch (Base[y + 1]) { + case 0x0E: + case 0x20: + case 0x22: + return TRUE; + } + return FALSE; + } + } + } + } + return FALSE; +} + +L9BOOL ValidateSequence(L9BYTE *Base, L9BYTE *Image, L9UINT32 iPos, L9UINT32 acode, L9UINT32 *Size, L9UINT32 size, L9UINT32 *Min, L9UINT32 *Max, L9BOOL Rts, L9BOOL *JumpKill, L9BOOL *DriverV4) { + L9UINT32 Pos; + L9BOOL Finished = FALSE, Valid; + L9UINT32 Strange = 0; + int ScanCodeMask; + int Code; + *JumpKill = FALSE; + + if (iPos >= size) + return FALSE; + Pos = iPos; + if (Pos < *Min) *Min = Pos; + + if (Image[Pos]) return TRUE; /* hit valid code */ + + do { + Code = Base[Pos]; + Valid = TRUE; + if (Image[Pos]) break; /* converged to found code */ + Image[Pos++] = 2; + if (Pos > *Max) *Max = Pos; + + ScanCodeMask = 0x9f; + if (Code & 0x80) { + ScanCodeMask = 0xff; + if ((Code & 0x1f) > 0xa) + Valid = FALSE; + Pos += 2; + } else switch (Code & 0x1f) { + case 0: { /* goto */ + L9UINT32 Val = scangetaddr(Code, Base, &Pos, acode, &ScanCodeMask); + Valid = ValidateSequence(Base, Image, Val, acode, Size, size, Min, Max, TRUE/*Rts*/, JumpKill, DriverV4); + Finished = TRUE; + break; + } + case 1: { /* intgosub */ + L9UINT32 Val = scangetaddr(Code, Base, &Pos, acode, &ScanCodeMask); + Valid = ValidateSequence(Base, Image, Val, acode, Size, size, Min, Max, TRUE, JumpKill, DriverV4); + break; + } + case 2: /* intreturn */ + Valid = Rts; + Finished = TRUE; + break; + case 3: /* printnumber */ + Pos++; + break; + case 4: /* messagev */ + Pos++; + break; + case 5: /* messagec */ + scangetcon(Code, &Pos, &ScanCodeMask); + break; + case 6: /* function */ + switch (Base[Pos++]) { + case 2:/* random */ + Pos++; + break; + case 1:/* calldriver */ + if (DriverV4) { + if (CheckCallDriverV4(Base, Pos - 2)) + *DriverV4 = TRUE; + } + break; + case 3:/* save */ + case 4:/* restore */ + case 5:/* clearworkspace */ + case 6:/* clear stack */ + break; + case 250: /* printstr */ + while (Base[Pos++]); + break; + + default: +#ifdef L9DEBUG + /* printf("scan: illegal function call: %d",Base[Pos-1]); */ +#endif + Valid = FALSE; + break; + } + break; + case 7: /* input */ + Pos += 4; + break; + case 8: /* varcon */ + scangetcon(Code, &Pos, &ScanCodeMask); + Pos++; + break; + case 9: /* varvar */ + Pos += 2; + break; + case 10: /* _add */ + Pos += 2; + break; + case 11: /* _sub */ + Pos += 2; + break; + case 14: /* jump */ +#ifdef L9DEBUG + /* printf("jmp at codestart: %ld",acode); */ +#endif + *JumpKill = TRUE; + Finished = TRUE; + break; + case 15: /* exit */ + Pos += 4; + break; + case 16: /* ifeqvt */ + case 17: /* ifnevt */ + case 18: /* ifltvt */ + case 19: { /* ifgtvt */ + L9UINT32 Val; + Pos += 2; + Val = scangetaddr(Code, Base, &Pos, acode, &ScanCodeMask); + Valid = ValidateSequence(Base, Image, Val, acode, Size, size, Min, Max, Rts, JumpKill, DriverV4); + break; + } + case 20: /* screen */ + if (Base[Pos++]) Pos++; + break; + case 21: /* cleartg */ + Pos++; + break; + case 22: /* picture */ + Pos++; + break; + case 23: /* getnextobject */ + Pos += 6; + break; + case 24: /* ifeqct */ + case 25: /* ifnect */ + case 26: /* ifltct */ + case 27: { /* ifgtct */ + L9UINT32 Val; + Pos++; + scangetcon(Code, &Pos, &ScanCodeMask); + Val = scangetaddr(Code, Base, &Pos, acode, &ScanCodeMask); + Valid = ValidateSequence(Base, Image, Val, acode, Size, size, Min, Max, Rts, JumpKill, DriverV4); + break; + } + case 28: /* printinput */ + break; + case 12: /* ilins */ + case 13: /* ilins */ + case 29: /* ilins */ + case 30: /* ilins */ + case 31: /* ilins */ +#ifdef L9DEBUG + /* printf("scan: illegal instruction"); */ +#endif + Valid = FALSE; + break; + } + if (Valid && (Code & ~ScanCodeMask)) + Strange++; + } while (Valid && !Finished && Pos < size); /* && Strange==0); */ + (*Size) += Pos - iPos; + return Valid; /* && Strange==0; */ +} + +L9BYTE calcchecksum(L9BYTE *ptr, L9UINT32 num) { + L9BYTE d1 = 0; + while (num-- != 0) d1 += *ptr++; + return d1; +} + +/* +L9BOOL Check(L9BYTE* StartFile,L9UINT32 FileSize,L9UINT32 Offset) +{ + L9UINT16 d0,num; + int i; + L9BYTE* Image; + L9UINT32 Size=0,Min,Max; + L9BOOL ret,JumpKill; + + for (i=0;i<12;i++) + { + d0=L9WORD (StartFile+Offset+0x12 + i*2); + if (d0>=0x8000+LISTAREASIZE) return FALSE; + } + + num=L9WORD(StartFile+Offset)+1; + if (Offset+num>FileSize) return FALSE; + if (calcchecksum(StartFile+Offset,num)) return FALSE; + + Image=calloc(FileSize,1); + + Min=Max=Offset+d0; + ret=ValidateSequence(StartFile,Image,Offset+d0,Offset+d0,&Size,FileSize,&Min,&Max,FALSE,&JumpKill,NULL); + free(Image); + return ret; +} +*/ + +long Scan(L9BYTE *StartFile, L9UINT32 size) { + L9BYTE *Chk = (L9BYTE *)malloc(size + 1); + L9BYTE *Image = (L9BYTE *)calloc(size, 1); + L9UINT32 i, num, Size, MaxSize = 0; + int j; + L9UINT16 d0 = 0, l9, md, ml, dd, dl; + L9UINT32 Min, Max; + long Offset = -1; + L9BOOL JumpKill, DriverV4; + + if ((Chk == NULL) || (Image == NULL)) { + error("Unable to allocate memory for game scan! Exiting..."); + } + + Chk[0] = 0; + for (i = 1; i <= size; i++) + Chk[i] = Chk[i - 1] + StartFile[i - 1]; + + for (i = 0; i < size - 33; i++) { + num = L9WORD(StartFile + i) + 1; + /* + Chk[i] = 0 +...+ i-1 + Chk[i+n] = 0 +...+ i+n-1 + Chk[i+n] - Chk[i] = i + ... + i+n + */ + if (num > 0x2000 && i + num <= size && Chk[i + num] == Chk[i]) { + md = L9WORD(StartFile + i + 0x2); + ml = L9WORD(StartFile + i + 0x4); + dd = L9WORD(StartFile + i + 0xa); + dl = L9WORD(StartFile + i + 0xc); + + if (ml > 0 && md > 0 && i + md + ml <= size && dd > 0 && dl > 0 && i + dd + dl * 4 <= size) { + /* v4 files may have acodeptr in 8000-9000, need to fix */ + for (j = 0; j < 12; j++) { + d0 = L9WORD(StartFile + i + 0x12 + j * 2); + if (j != 11 && d0 >= 0x8000 && d0 < 0x9000) { + if (d0 >= 0x8000 + LISTAREASIZE) break; + } else if (i + d0 > size) break; + } + /* list9 ptr must be in listarea, acode ptr in data */ + if (j < 12 /*|| (d0>=0x8000 && d0<0x9000)*/) continue; + + l9 = L9WORD(StartFile + i + 0x12 + 10 * 2); + if (l9 < 0x8000 || l9 >= 0x8000 + LISTAREASIZE) continue; + + Size = 0; + Min = Max = i + d0; + DriverV4 = 0; + if (ValidateSequence(StartFile, Image, i + d0, i + d0, &Size, size, &Min, &Max, FALSE, &JumpKill, &DriverV4)) { +#ifdef L9DEBUG + printf("Found valid header at %ld, code size %ld", i, Size); +#endif + if (Size > MaxSize && Size > 100) { + Offset = i; + MaxSize = Size; + L9GameType = DriverV4 ? L9_V4 : L9_V3; + } + } + } + } + } + free(Chk); + free(Image); + return Offset; +} + +long ScanV2(L9BYTE *StartFile, L9UINT32 size) { + L9BYTE *Chk = (L9BYTE *)malloc(size + 1); + L9BYTE *Image = (L9BYTE *)calloc(size, 1); + L9UINT32 i, Size, MaxSize = 0, num; + int j; + L9UINT16 d0 = 0, l9; + L9UINT32 Min, Max; + long Offset = -1; + L9BOOL JumpKill; + + if ((Chk == NULL) || (Image == NULL)) { + error("Unable to allocate memory for game scan! Exiting..."); + } + + Chk[0] = 0; + for (i = 1; i <= size; i++) + Chk[i] = Chk[i - 1] + StartFile[i - 1]; + + for (i = 0; i < size - 28; i++) { + num = L9WORD(StartFile + i + 28) + 1; + if (i + num <= size && ((Chk[i + num] - Chk[i + 32]) & 0xff) == StartFile[i + 0x1e]) { + for (j = 0; j < 14; j++) { + d0 = L9WORD(StartFile + i + j * 2); + if (j != 13 && d0 >= 0x8000 && d0 < 0x9000) { + if (d0 >= 0x8000 + LISTAREASIZE) break; + } else if (i + d0 > size) break; + } + /* list9 ptr must be in listarea, acode ptr in data */ + if (j < 14 /*|| (d0>=0x8000 && d0<0x9000)*/) continue; + + l9 = L9WORD(StartFile + i + 6 + 9 * 2); + if (l9 < 0x8000 || l9 >= 0x8000 + LISTAREASIZE) continue; + + Size = 0; + Min = Max = i + d0; + if (ValidateSequence(StartFile, Image, i + d0, i + d0, &Size, size, &Min, &Max, FALSE, &JumpKill, NULL)) { +#ifdef L9DEBUG + printf("Found valid V2 header at %ld, code size %ld", i, Size); +#endif + if (Size > MaxSize && Size > 100) { + Offset = i; + MaxSize = Size; + } + } + } + } + free(Chk); + free(Image); + return Offset; +} + +long ScanV1(L9BYTE *StartFile, L9UINT32 size) { + L9BYTE *Image = (L9BYTE *)calloc(size, 1); + L9UINT32 i, Size; + int Replace; + L9BYTE *ImagePtr; + long MaxPos = -1; + L9UINT32 MaxCount = 0; + L9UINT32 Min, Max; //, MaxMax, MaxMin; + L9BOOL JumpKill; // , MaxJK; + + int dictOff1 = 0, dictOff2 = 0; + L9BYTE dictVal1 = 0xff, dictVal2 = 0xff; + + if (Image == NULL) { + error("Unable to allocate memory for game scan! Exiting..."); + } + + for (i = 0; i < size; i++) { + if ((StartFile[i] == 0 && StartFile[i + 1] == 6) || (StartFile[i] == 32 && StartFile[i + 1] == 4)) { + Size = 0; + Min = Max = i; + Replace = 0; + if (ValidateSequence(StartFile, Image, i, i, &Size, size, &Min, &Max, FALSE, &JumpKill, NULL)) { + if (Size > MaxCount && Size > 100 && Size < 10000) { + MaxCount = Size; + //MaxMin = Min; + //MaxMax = Max; + + MaxPos = i; + //MaxJK = JumpKill; + } + Replace = 0; + } + for (ImagePtr = Image + Min; ImagePtr <= Image + Max; ImagePtr++) { + if (*ImagePtr == 2) + *ImagePtr = Replace; + } + } + } +#ifdef L9DEBUG + printf("V1scan found code at %ld size %ld", MaxPos, MaxCount); +#endif + + /* V1 dictionary detection from L9Cut by Paul David Doherty */ + for (i = 0; i < size - 20; i++) { + if (StartFile[i] == 'A') { + if (StartFile[i + 1] == 'T' && StartFile[i + 2] == 'T' && StartFile[i + 3] == 'A' && StartFile[i + 4] == 'C' && StartFile[i + 5] == 0xcb) { + dictOff1 = i; + dictVal1 = StartFile[dictOff1 + 6]; + break; + } + } + } + for (i = dictOff1; i < size - 20; i++) { + if (StartFile[i] == 'B') { + if (StartFile[i + 1] == 'U' && StartFile[i + 2] == 'N' && StartFile[i + 3] == 'C' && StartFile[i + 4] == 0xc8) { + dictOff2 = i; + dictVal2 = StartFile[dictOff2 + 5]; + break; + } + } + } + L9V1Game = -1; + if (dictVal1 != 0xff || dictVal2 != 0xff) { + for (i = 0; i < sizeof L9V1Games / sizeof L9V1Games[0]; i++) { + if ((L9V1Games[i].dictVal1 == dictVal1) && (L9V1Games[i].dictVal2 == dictVal2)) { + L9V1Game = i; + dictdata = StartFile + dictOff1 - L9V1Games[i].dictStart; + } + } + } + +#ifdef L9DEBUG + if (L9V1Game >= 0) + printf("V1scan found known dictionary: %d", L9V1Game); +#endif + + free(Image); + + if (MaxPos > 0) { + acodeptr = StartFile + MaxPos; + return 0; + } + return -1; +} + +#ifdef FULLSCAN +void FullScan(L9BYTE *StartFile, L9UINT32 size) { + L9BYTE *Image = (L9BYTE *)calloc(size, 1); + L9UINT32 i, Size; + int Replace; + L9BYTE *ImagePtr; + L9UINT32 MaxPos = 0; + L9UINT32 MaxCount = 0; + L9UINT32 Min, Max, MaxMin, MaxMax; + int Offset; + L9BOOL JumpKill, MaxJK; + for (i = 0; i < size; i++) { + Size = 0; + Min = Max = i; + Replace = 0; + if (ValidateSequence(StartFile, Image, i, i, &Size, size, &Min, &Max, FALSE, &JumpKill, NULL)) { + if (Size > MaxCount) { + MaxCount = Size; + MaxMin = Min; + MaxMax = Max; + + MaxPos = i; + MaxJK = JumpKill; + } + Replace = 0; + } + for (ImagePtr = Image + Min; ImagePtr <= Image + Max; ImagePtr++) { + if (*ImagePtr == 2) + *ImagePtr = Replace; + } + } + printf("%ld %ld %ld %ld %s", MaxPos, MaxCount, MaxMin, MaxMax, MaxJK ? "jmp killed" : ""); + /* search for reference to MaxPos */ + Offset = 0x12 + 11 * 2; + for (i = 0; i < size - Offset - 1; i++) { + if ((L9WORD(StartFile + i + Offset)) + i == MaxPos) { + printf("possible v3,4 Code reference at : %ld", i); + /* startdata=StartFile+i; */ + } + } + Offset = 13 * 2; + for (i = 0; i < size - Offset - 1; i++) { + if ((L9WORD(StartFile + i + Offset)) + i == MaxPos) + printf("possible v2 Code reference at : %ld", i); + } + free(Image); +} +#endif + +L9BOOL findsubs(L9BYTE *testptr, L9UINT32 testsize, L9BYTE **picdata, L9UINT32 *picsize) { + int i, j, length, count; + L9BYTE *picptr, *startptr, *tmpptr; + + if (testsize < 16) return FALSE; + + /* + Try to traverse the graphics subroutines. + + Each subroutine starts with a header: nn | nl | ll + nnn : the subroutine number ( 0x000 - 0x7ff ) + lll : the subroutine length ( 0x004 - 0x3ff ) + + The first subroutine usually has the number 0x000. + Each subroutine ends with 0xff. + + findsubs() searches for the header of the second subroutine + (pattern: 0xff | nn | nl | ll) and then tries to find the + first and next subroutines by evaluating the length fields + of the subroutine headers. + */ + for (i = 4; i < (int)(testsize - 4); i++) { + picptr = testptr + i; + if (*(picptr - 1) != 0xff || (*picptr & 0x80) || (*(picptr + 1) & 0x0c) || (*(picptr + 2) < 4)) + continue; + + count = 0; + startptr = picptr; + + while (TRUE) { + length = ((*(picptr + 1) & 0x0f) << 8) + *(picptr + 2); + if (length > 0x3ff || picptr + length + 4 > testptr + testsize) + break; + + picptr += length; + if (*(picptr - 1) != 0xff) { + picptr -= length; + break; + } + if ((*picptr & 0x80) || (*(picptr + 1) & 0x0c) || (*(picptr + 2) < 4)) + break; + + count++; + } + + if (count > 10) { + /* Search for the start of the first subroutine */ + for (j = 4; j < 0x3ff; j++) { + tmpptr = startptr - j; + if (*tmpptr == 0xff || tmpptr < testptr) + break; + + length = ((*(tmpptr + 1) & 0x0f) << 8) + *(tmpptr + 2); + if (tmpptr + length == startptr) { + startptr = tmpptr; + break; + } + } + + if (*tmpptr != 0xff) { + *picdata = startptr; + *picsize = picptr - startptr; + return TRUE; + } + } + } + return FALSE; +} + +L9BOOL intinitialise(const char *filename, char *picname) { + /* init */ + /* driverclg */ + + int i; + int hdoffset; + long Offset; + Common::File f; + + if (pictureaddress) { + free(pictureaddress); + pictureaddress = NULL; + } + picturedata = NULL; + picturesize = 0; + gfxa5 = NULL; + + if (!load(filename)) { + error("\rUnable to load: %s\r", filename); + return FALSE; + } + + /* try to load graphics */ + if (picname) { + if (f.open(picname)) { + picturesize = f.size(); + L9Allocate(&pictureaddress, picturesize); + if (f.read(pictureaddress, picturesize) != picturesize) { + free(pictureaddress); + pictureaddress = NULL; + picturesize = 0; + } + f.close(); + } + } + screencalled = 0; + l9textmode = 0; + +#ifdef FULLSCAN + FullScan(startfile, FileSize); +#endif + + Offset = Scan(startfile, FileSize); + if (Offset < 0) { + Offset = ScanV2(startfile, FileSize); + L9GameType = L9_V2; + if (Offset < 0) { + Offset = ScanV1(startfile, FileSize); + L9GameType = L9_V1; + if (Offset < 0) { + error("\rUnable to locate valid Level 9 game in file: %s\r", filename); + return FALSE; + } + } + } + + startdata = startfile + Offset; + FileSize -= Offset; + + /* setup pointers */ + if (L9GameType == L9_V1) { + if (L9V1Game < 0) { + error("\rWhat appears to be V1 game data was found, but the game was not recognised.\rEither this is an unknown V1 game file or, more likely, it is corrupted.\r"); + return FALSE; + } + for (i = 0; i < 6; i++) { + int off = L9V1Games[L9V1Game].L9Ptrs[i]; + if (off < 0) + L9Pointers[i + 2] = acodeptr + off; + else + L9Pointers[i + 2] = workspace.listarea + off; + } + absdatablock = acodeptr - L9V1Games[L9V1Game].absData; + } else { + /* V2,V3,V4 */ + hdoffset = L9GameType == L9_V2 ? 4 : 0x12; + for (i = 0; i < 12; i++) { + L9UINT16 d0 = L9WORD(startdata + hdoffset + i * 2); + L9Pointers[i] = (i != 11 && d0 >= 0x8000 && d0 <= 0x9000) ? workspace.listarea + d0 - 0x8000 : startdata + d0; + } + absdatablock = L9Pointers[0]; + dictdata = L9Pointers[1]; + list2ptr = L9Pointers[3]; + list3ptr = L9Pointers[4]; + /*list9startptr */ + list9startptr = L9Pointers[10]; + acodeptr = L9Pointers[11]; + } + + switch (L9GameType) { + case L9_V1: { + double a1; + startmd = acodeptr + L9V1Games[L9V1Game].msgStart; + startmdV2 = startmd + L9V1Games[L9V1Game].msgLen; + + if (analyseV1(&a1) && a1 > 2 && a1 < 10) { + L9MsgType = MSGT_V1; +#ifdef L9DEBUG + printf("V1 msg table: wordlen=%.2lf", a1); +#endif + } else { + error("\rUnable to identify V1 message table in file: %s\r", filename); + return FALSE; + } + break; + } + case L9_V2: { + double a2, a1; + startmd = startdata + L9WORD(startdata + 0x0); + startmdV2 = startdata + L9WORD(startdata + 0x2); + + /* determine message type */ + if (analyseV2(&a2) && a2 > 2 && a2 < 10) { + L9MsgType = MSGT_V2; +#ifdef L9DEBUG + printf("V2 msg table: wordlen=%.2lf", a2); +#endif + } else if (analyseV1(&a1) && a1 > 2 && a1 < 10) { + L9MsgType = MSGT_V1; +#ifdef L9DEBUG + printf("V1 msg table: wordlen=%.2lf", a1); +#endif + } else { + error("\rUnable to identify V2 message table in file: %s\r", filename); + return FALSE; + } + break; + } + case L9_V3: + case L9_V4: + startmd = startdata + L9WORD(startdata + 0x2); + endmd = startmd + L9WORD(startdata + 0x4); + defdict = startdata + L9WORD(startdata + 6); + endwdp5 = defdict + 5 + L9WORD(startdata + 0x8); + dictdata = startdata + L9WORD(startdata + 0x0a); + dictdatalen = L9WORD(startdata + 0x0c); + wordtable = startdata + L9WORD(startdata + 0xe); + break; + } + +#ifndef NO_SCAN_GRAPHICS + /* If there was no graphics file, look in the game data */ + if (pictureaddress) { + if (!findsubs(pictureaddress, picturesize, &picturedata, &picturesize)) { + picturedata = NULL; + picturesize = 0; + } + } else { + if (!findsubs(startdata, FileSize, &picturedata, &picturesize) + && !findsubs(startfile, startdata - startfile, &picturedata, &picturesize)) { + picturedata = NULL; + picturesize = 0; + } + } +#endif + + memset(FirstLine, 0, FIRSTLINESIZE); + FirstLinePos = 0; + + return TRUE; +} + +L9BOOL checksumgamedata(void) { + return calcchecksum(startdata, L9WORD(startdata) + 1) == 0; +} + +L9UINT16 movewa5d0(void) { + L9UINT16 ret = L9WORD(codeptr); + codeptr += 2; + return ret; +} + +L9UINT16 getcon(void) { + if (code & 64) { + /* getconsmall */ + return *codeptr++; + } else return movewa5d0(); +} + +L9BYTE *getaddr(void) { + if (code & 0x20) { + /* getaddrshort */ + signed char diff = *codeptr++; + return codeptr + diff - 1; + } else { + return acodeptr + movewa5d0(); + } +} + +L9UINT16 *getvar(void) { +#ifndef CODEFOLLOW + return workspace.vartable + *codeptr++; +#else + cfvar2 = cfvar; + return cfvar = workspace.vartable + *codeptr++; +#endif +} + +void Goto(void) { + L9BYTE *target = getaddr(); + if (target == codeptr - 2) + Running = FALSE; /* Endless loop! */ + else + codeptr = target; +} + +void intgosub(void) { + L9BYTE *newcodeptr = getaddr(); + if (workspace.stackptr == STACKSIZE) { + error("\rStack overflow error\r"); + Running = FALSE; + return; + } + workspace.stack[workspace.stackptr++] = (L9UINT16)(codeptr - acodeptr); + codeptr = newcodeptr; +} + +void intreturn(void) { + if (workspace.stackptr == 0) { + error("\rStack underflow error\r"); + Running = FALSE; + return; + } + codeptr = acodeptr + workspace.stack[--workspace.stackptr]; +} + +void printnumber(void) { + printdecimald0(*getvar()); +} + +void messagec(void) { + if (L9GameType <= L9_V2) + printmessageV2(getcon()); + else + printmessage(getcon()); +} + +void messagev(void) { + if (L9GameType <= L9_V2) + printmessageV2(*getvar()); + else + printmessage(*getvar()); +} + +void init(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - init"); +#endif +} + +void randomnumber(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - randomnumber"); +#endif + L9SETWORD(a6, g_vm->getRandomNumber(0xffff)); +} + +void driverclg(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driverclg"); +#endif +} + +void _line(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - line"); +#endif +} + +void fill(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - fill"); +#endif +} + +void driverchgcol(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driverchgcol"); +#endif +} + +void drivercalcchecksum(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - calcchecksum"); +#endif +} + +void driveroswrch(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driveroswrch"); +#endif +} + +void driverosrdch(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driverosrdch"); +#endif + + os_flush(); + if (Cheating) { + *a6 = '\r'; + } else { + /* max delay of 1/50 sec */ + *a6 = os_readchar(20); + } +} + +void driversavefile(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driversavefile"); +#endif +} + +void driverloadfile(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driverloadfile"); +#endif +} + +void settext(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - settext"); +#endif +} + +void resettask(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - resettask"); +#endif +} + +void driverinputline(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - driverinputline"); +#endif +} + +void returntogem(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - returntogem"); +#endif +} + +void lensdisplay(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - lensdisplay"); +#endif + + printstring("\rLenslok code is "); + printchar(*a6); + printchar(*(a6 + 1)); + printchar('\r'); +} + +void allocspace(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - allocspace"); +#endif +} + +void driver14(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - call 14"); +#endif + + *a6 = 0; +} + +void showbitmap(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - showbitmap"); +#endif + + os_show_bitmap(a6[1], a6[3], a6[5]); +} + +void checkfordisc(L9BYTE *a6) { +#ifdef L9DEBUG + printf("driver - checkfordisc"); +#endif + + *a6 = 0; + list9startptr[2] = 0; +} + +void driver(int d0, L9BYTE *a6) { + switch (d0) { + case 0: + init(a6); + break; + case 0x0c: + randomnumber(a6); + break; + case 0x10: + driverclg(a6); + break; + case 0x11: + _line(a6); + break; + case 0x12: + fill(a6); + break; + case 0x13: + driverchgcol(a6); + break; + case 0x01: + drivercalcchecksum(a6); + break; + case 0x02: + driveroswrch(a6); + break; + case 0x03: + driverosrdch(a6); + break; + case 0x05: + driversavefile(a6); + break; + case 0x06: + driverloadfile(a6); + break; + case 0x07: + settext(a6); + break; + case 0x08: + resettask(a6); + break; + case 0x04: + driverinputline(a6); + break; + case 0x09: + returntogem(a6); + break; + /* + case 0x16: ramsave(a6); break; + case 0x17: ramload(a6); break; + */ + case 0x19: + lensdisplay(a6); + break; + case 0x1e: + allocspace(a6); + break; + /* v4 */ + case 0x0e: + driver14(a6); + break; + case 0x20: + showbitmap(a6); + break; + case 0x22: + checkfordisc(a6); + break; + } +} + +void ramsave(int i) { +#ifdef L9DEBUG + printf("driver - ramsave %d", i); +#endif + + memmove(ramsavearea + i, workspace.vartable, sizeof(SaveStruct)); +} + +void ramload(int i) { +#ifdef L9DEBUG + printf("driver - ramload %d", i); +#endif + + memmove(workspace.vartable, ramsavearea + i, sizeof(SaveStruct)); +} + +void calldriver(void) { + L9BYTE *a6 = list9startptr; + int d0 = *a6++; +#ifdef CODEFOLLOW + fprintf(f, " %s", drivercalls[d0]); +#endif + + if (d0 == 0x16 || d0 == 0x17) { + int d1 = *a6; + if (d1 > 0xfa) *a6 = 1; + else if (d1 + 1 >= RAMSAVESLOTS) *a6 = 0xff; + else { + *a6 = 0; + if (d0 == 0x16) ramsave(d1 + 1); + else ramload(d1 + 1); + } + *list9startptr = *a6; + } else if (d0 == 0x0b) { + char NewName[MAX_PATH]; + strcpy(NewName, LastGame); + if (*a6 == 0) { + printstring("\rSearching for next sub-game file.\r"); + if (!os_get_game_file(NewName, MAX_PATH)) { + printstring("\rFailed to load game.\r"); + return; + } + } else { + os_set_filenumber(NewName, MAX_PATH, *a6); + } + LoadGame2(NewName, NULL); + } else driver(d0, a6); +} + +void L9Random(void) { +#ifdef CODEFOLLOW + fprintf(f, " %d", randomseed); +#endif + randomseed = (((randomseed << 8) + 0x0a - randomseed) << 2) + randomseed + 1; + *getvar() = randomseed & 0xff; +#ifdef CODEFOLLOW + fprintf(f, " %d", randomseed); +#endif +} + +void save(void) { + L9UINT16 checksum; + int i; +#ifdef L9DEBUG + printf("function - save"); +#endif + /* does a full save, workpace, stack, codeptr, stackptr, game name, checksum */ + + workspace.Id = L9_ID; + workspace.codeptr = codeptr - acodeptr; + workspace.listsize = LISTAREASIZE; + workspace.stacksize = STACKSIZE; + workspace.filenamesize = MAX_PATH; + workspace.checksum = 0; + strcpy(workspace.filename, LastGame); + + checksum = 0; + for (i = 0; i < (int)sizeof(GameState); i++) checksum += ((L9BYTE *) &workspace)[i]; + workspace.checksum = checksum; + + if (os_save_file((L9BYTE *) &workspace, sizeof(workspace))) printstring("\rGame saved.\r"); + else printstring("\rUnable to save game.\r"); +} + +L9BOOL CheckFile(GameState *gs) { + L9UINT16 checksum; + int i; + char c = 'Y'; + + if (gs->Id != L9_ID) return FALSE; + checksum = gs->checksum; + gs->checksum = 0; + for (i = 0; i < (int)sizeof(GameState); i++) + checksum -= *((L9BYTE *) gs + i); + if (checksum) return FALSE; + if (scumm_stricmp(gs->filename, LastGame)) { + printstring("\rWarning: game path name does not match, you may be about to load this position file into the wrong story file.\r"); + printstring("Are you sure you want to restore? (Y/N)"); + os_flush(); + + c = '\0'; + while ((c != 'y') && (c != 'Y') && (c != 'n') && (c != 'N')) + c = os_readchar(20); + } + if ((c == 'y') || (c == 'Y')) + return TRUE; + return FALSE; +} + +void NormalRestore(void) { + GameState temp; + int Bytes; +#ifdef L9DEBUG + printf("function - restore"); +#endif + if (Cheating) { + /* not really an error */ + Cheating = FALSE; + error("\rWord is: %s\r", ibuff); + } + + if (os_load_file((L9BYTE *) &temp, &Bytes, sizeof(GameState))) { + if (Bytes == V1FILESIZE) { + printstring("\rGame restored.\r"); + memset(workspace.listarea, 0, LISTAREASIZE); + memmove(workspace.vartable, &temp, V1FILESIZE); + } else if (CheckFile(&temp)) { + printstring("\rGame restored.\r"); + /* only copy in workspace */ + memmove(workspace.vartable, temp.vartable, sizeof(SaveStruct)); + } else { + printstring("\rSorry, unrecognised format. Unable to restore\r"); + } + } else printstring("\rUnable to restore game.\r"); +} + +void restore(void) { + int Bytes; + GameState temp; + if (os_load_file((L9BYTE *) &temp, &Bytes, sizeof(GameState))) { + if (Bytes == V1FILESIZE) { + printstring("\rGame restored.\r"); + /* only copy in workspace */ + memset(workspace.listarea, 0, LISTAREASIZE); + memmove(workspace.vartable, &temp, V1FILESIZE); + } else if (CheckFile(&temp)) { + printstring("\rGame restored.\r"); + /* full restore */ + memmove(&workspace, &temp, sizeof(GameState)); + codeptr = acodeptr + workspace.codeptr; + } else { + printstring("\rSorry, unrecognised format. Unable to restore\r"); + } + } else printstring("\rUnable to restore game.\r"); +} + +void playback(void) { + if (scriptfile) + delete scriptfile; + scriptfile = os_open_script_file(); + + if (scriptfile) + printstring("\rPlaying back input from script file.\r"); + else + printstring("\rUnable to play back script file.\r"); +} + +void l9_fgets(char *s, int n, Common::SeekableReadStream *f) { + int c = '\0'; + int count = 0; + + while ((c != '\n') && (c != '\r') && (c != EOF) && (count < n - 1)) { + c = f->readByte(); + *s++ = c; + count++; + } + *s = '\0'; + + if (c == EOF) { + s--; + *s = '\n'; + } else if (c == '\r') { + s--; + *s = '\n'; + + c = f->readByte(); + if ((c != '\r') && (c != EOF)) + f->seek(-1, SEEK_CUR); + } +} + +L9BOOL scriptinput(char *buffer, int size) { + while (scriptfile != NULL) { + if (scriptfile->eos()) { + delete scriptfile; + scriptfile = NULL; + } else { + char *p = buffer; + *p = '\0'; + l9_fgets(buffer, size, scriptfile); + while (*p != '\0') { + switch (*p) { + case '\n': + case '\r': + case '[': + case ';': + *p = '\0'; + break; + case '#': + if ((p == buffer) && (scumm_strnicmp(p, "#seed ", 6) == 0)) + p++; + else + *p = '\0'; + break; + default: + p++; + break; + } + } + if (*buffer != '\0') { + printstring(buffer); + lastchar = lastactualchar = '.'; + return TRUE; + } + } + } + return FALSE; +} + +void clearworkspace(void) { + memset(workspace.vartable, 0, sizeof(workspace.vartable)); +} + +void ilins(int d0) { + error("\rIllegal instruction: %d\r", d0); + Running = FALSE; +} + +void function(void) { + int d0 = *codeptr++; +#ifdef CODEFOLLOW + fprintf(f, " %s", d0 == 250 ? "printstr" : functions[d0 - 1]); +#endif + + switch (d0) { + case 1: + if (L9GameType == L9_V1) + StopGame(); + else + calldriver(); + break; + case 2: + L9Random(); + break; + case 3: + save(); + break; + case 4: + NormalRestore(); + break; + case 5: + clearworkspace(); + break; + case 6: + workspace.stackptr = 0; + break; + case 250: + printstring((char *) codeptr); + while (*codeptr++); + break; + + default: + ilins(d0); + } +} + +void findmsgequiv(int d7) { + int d4 = -1, d0; + L9BYTE *a2 = startmd; + + do { + d4++; + if (a2 > endmd) return; + d0 = *a2; + if (d0 & 0x80) { + a2++; + d4 += d0 & 0x7f; + } else if (d0 & 0x40) { + int d6 = getmdlength(&a2); + do { + int d1; + if (d6 == 0) break; + + d1 = *a2++; + d6--; + if (d1 & 0x80) { + if (d1 < 0x90) { + a2++; + d6--; + } else { + d0 = (d1 << 8) + *a2++; + d6--; + if (d7 == (d0 & 0xfff)) { + d0 = ((d0 << 1) & 0xe000) | d4; + list9ptr[1] = d0; + list9ptr[0] = d0 >> 8; + list9ptr += 2; + if (list9ptr >= list9startptr + 0x20) return; + } + } + } + } while (TRUE); + } else { + int len = getmdlength(&a2); + a2 += len; + } + } while (TRUE); +} + +L9BOOL unpackword(void) { + L9BYTE *a3; + + if (unpackd3 == 0x1b) return TRUE; + + a3 = (L9BYTE *) threechars + (unpackd3 & 3); + + /*uw01 */ + while (TRUE) { + L9BYTE d0 = getdictionarycode(); + if (dictptr >= endwdp5) return TRUE; + if (d0 >= 0x1b) { + *a3 = 0; + unpackd3 = d0; + return FALSE; + } + *a3++ = getdictionary(d0); + } +} + +L9BOOL initunpack(L9BYTE *ptr) { + initdict(ptr); + unpackd3 = 0x1c; + return unpackword(); +} + +int partword(char c) { + c = tolower(c); + + if (c == 0x27 || c == 0x2d) return 0; + if (c < 0x30) return 1; + if (c < 0x3a) return 0; + if (c < 0x61) return 1; + if (c < 0x7b) return 0; + return 1; +} + +L9UINT32 readdecimal(char *buff) { + return atol(buff); +} + +void checknumber(void) { + if (*obuff >= 0x30 && *obuff < 0x3a) { + if (L9GameType == L9_V4) { + *list9ptr = 1; + L9SETWORD(list9ptr + 1, readdecimal(obuff)); + L9SETWORD(list9ptr + 3, 0); + } else { + L9SETDWORD(list9ptr, readdecimal(obuff)); + L9SETWORD(list9ptr + 4, 0); + } + } else { + L9SETWORD(list9ptr, 0x8000); + L9SETWORD(list9ptr + 2, 0); + } +} + +void NextCheat(void) { + /* restore game status */ + memmove(&workspace, &CheatWorkspace, sizeof(GameState)); + codeptr = acodeptr + workspace.codeptr; + + if (!((L9GameType <= L9_V2) ? GetWordV2(ibuff, CheatWord++) : GetWordV3(ibuff, CheatWord++))) { + Cheating = FALSE; + printstring("\rCheat failed.\r"); + *ibuff = 0; + } +} + +void StartCheat(void) { + Cheating = TRUE; + CheatWord = 0; + + /* save current game status */ + memmove(&CheatWorkspace, &workspace, sizeof(GameState)); + CheatWorkspace.codeptr = codeptr - acodeptr; + + NextCheat(); +} + +/* v3,4 input routine */ + +L9BOOL GetWordV3(char *buff, int Word) { + int i; + int subdict = 0; + /* 26*4-1=103 */ + + initunpack(startdata + L9WORD(dictdata)); + unpackword(); + + while (Word--) { + if (unpackword()) { + if (++subdict == dictdatalen) return FALSE; + initunpack(startdata + L9WORD(dictdata + (subdict << 2))); + Word++; /* force unpack again */ + } + } + strcpy(buff, threechars); + for (i = 0; i < (int)strlen(buff); i++) buff[i] &= 0x7f; + return TRUE; +} + +L9BOOL CheckHash(void) { + if (scumm_stricmp(ibuff, "#cheat") == 0) StartCheat(); + else if (scumm_stricmp(ibuff, "#save") == 0) { + save(); + return TRUE; + } else if (scumm_stricmp(ibuff, "#restore") == 0) { + restore(); + return TRUE; + } else if (scumm_stricmp(ibuff, "#quit") == 0) { + StopGame(); + printstring("\rGame Terminated\r"); + return TRUE; + } else if (scumm_stricmp(ibuff, "#dictionary") == 0) { + CheatWord = 0; + printstring("\r"); + while ((L9GameType <= L9_V2) ? GetWordV2(ibuff, CheatWord++) : GetWordV3(ibuff, CheatWord++)) { + error("%s ", ibuff); + if (os_stoplist() || !Running) break; + } + printstring("\r"); + return TRUE; + } else if (scumm_strnicmp(ibuff, "#picture ", 9) == 0) { + int pic = 0; + if (sscanf(ibuff + 9, "%d", &pic) == 1) { + if (L9GameType == L9_V4) + os_show_bitmap(pic, 0, 0); + else + show_picture(pic); + } + + lastactualchar = 0; + printchar('\r'); + return TRUE; + } else if (scumm_strnicmp(ibuff, "#seed ", 6) == 0) { + int seed = 0; + if (sscanf(ibuff + 6, "%d", &seed) == 1) + randomseed = constseed = seed; + lastactualchar = 0; + printchar('\r'); + return TRUE; + } else if (scumm_stricmp(ibuff, "#play") == 0) { + playback(); + return TRUE; + } + return FALSE; +} + +L9BOOL IsInputChar(char c) { + if (c == '-' || c == '\'') + return TRUE; + if ((L9GameType >= L9_V3) && (c == '.' || c == ',')) + return TRUE; + return Common::isAlnum(c); +} + +L9BOOL corruptinginput(void) { + L9BYTE *a0, *a2, *a6; + int d0, d1, d2, keywordnumber, abrevword; + char *iptr; + + list9ptr = list9startptr; + + if (ibuffptr == NULL) { + if (Cheating) NextCheat(); + else { + /* flush */ + os_flush(); + lastchar = lastactualchar = '.'; + /* get input */ + if (!scriptinput(ibuff, IBUFFSIZE)) { + if (!os_input(ibuff, IBUFFSIZE)) + return FALSE; /* fall through */ + } + if (CheckHash()) + return FALSE; + + /* check for invalid chars */ + for (iptr = ibuff; *iptr != 0; iptr++) { + if (!IsInputChar(*iptr)) + *iptr = ' '; + } + + /* force CR but prevent others */ + os_printchar(lastactualchar = '\r'); + } + ibuffptr = (L9BYTE *) ibuff; + } + + a2 = (L9BYTE *) obuff; + a6 = ibuffptr; + + /*ip05 */ + while (TRUE) { + d0 = *a6++; + if (d0 == 0) { + ibuffptr = NULL; + L9SETWORD(list9ptr, 0); + return TRUE; + } + if (partword((char)d0) == 0) break; + if (d0 != 0x20) { + ibuffptr = a6; + L9SETWORD(list9ptr, 0); + L9SETWORD(list9ptr + 2, 0); + list9ptr[1] = d0; + *a2 = 0x20; + keywordnumber = -1; + return TRUE; + } + } + + a6--; + /*ip06loop */ + do { + d0 = *a6++; + if (partword((char)d0) == 1) break; + d0 = tolower(d0); + *a2++ = d0; + } while (a2 < (L9BYTE *) obuff + 0x1f); + /*ip06a */ + *a2 = 0x20; + a6--; + ibuffptr = a6; + abrevword = -1; + keywordnumber = -1; + list9ptr = list9startptr; + /* setindex */ + a0 = dictdata; + d2 = dictdatalen; + d0 = *obuff - 0x61; + if (d0 < 0) { + a6 = defdict; + d1 = 0; + } else { + /*ip10 */ + d1 = 0x67; + if (d0 < 0x1a) { + d1 = d0 << 2; + d0 = obuff[1]; + if (d0 != 0x20) d1 += ((d0 - 0x61) >> 3) & 3; + } + /*ip13 */ + if (d1 >= d2) { + checknumber(); + return TRUE; + } + a0 += d1 << 2; + a6 = startdata + L9WORD(a0); + d1 = L9WORD(a0 + 2); + } + /*ip13gotwordnumber */ + + initunpack(a6); + /*ip14 */ + d1--; + do { + d1++; + if (unpackword()) { + /* ip21b */ + if (abrevword == -1) break; /* goto ip22 */ + else d0 = abrevword; /* goto ip18b */ + } else { + L9BYTE *a1 = (L9BYTE *) threechars; + int d6 = -1; + + a0 = (L9BYTE *) obuff; + /*ip15 */ + do { + d6++; + d0 = tolower(*a1++ & 0x7f); + d2 = *a0++; + } while (d0 == d2); + + if (d2 != 0x20) { + /* ip17 */ + if (abrevword == -1) continue; + else d0 = -1; + } else if (d0 == 0) d0 = d1; + else if (abrevword != -1) break; + else if (d6 >= 4) d0 = d1; + else { + abrevword = d1; + continue; + } + } + /*ip18b */ + findmsgequiv(d1); + + abrevword = -1; + if (list9ptr != list9startptr) { + L9SETWORD(list9ptr, 0); + return TRUE; + } + } while (TRUE); + /* ip22 */ + checknumber(); + return TRUE; +} + +/* version 2 stuff hacked from bbc v2 files */ + +L9BOOL IsDictionaryChar(char c) { + switch (c) { + case '?': + case '-': + case '\'': + case '/': + return TRUE; + case '!': + case '.': + case ',': + return TRUE; + } + return Common::isUpper(c) || Common::isDigit(c); +} + +L9BOOL GetWordV2(char *buff, int Word) { + L9BYTE *ptr = dictdata, x; + + while (Word--) { + do { + x = *ptr++; + } while (x > 0 && x < 0x7f); + if (x == 0) return FALSE; /* no more words */ + ptr++; + } + do { + x = *ptr++; + if (!IsDictionaryChar(x & 0x7f)) return FALSE; + *buff++ = x & 0x7f; + } while (x > 0 && x < 0x7f); + *buff = 0; + return TRUE; +} + +L9BOOL inputV2(int *wordcount) { + L9BYTE a, x; + L9BYTE *ibuffp, *obuffptr, *ptr, *list0ptr; + char *iptr; + + if (Cheating) NextCheat(); + else { + os_flush(); + lastchar = lastactualchar = '.'; + /* get input */ + if (!scriptinput(ibuff, IBUFFSIZE)) { + if (!os_input(ibuff, IBUFFSIZE)) + return FALSE; /* fall through */ + } + if (CheckHash()) + return FALSE; + + /* check for invalid chars */ + for (iptr = ibuff; *iptr != 0; iptr++) { + if (!IsInputChar(*iptr)) + *iptr = ' '; + } + + /* force CR but prevent others */ + os_printchar(lastactualchar = '\r'); + } + /* add space onto end */ + ibuffp = (L9BYTE *) strchr(ibuff, 0); + *ibuffp++ = 32; + *ibuffp = 0; + + *wordcount = 0; + ibuffp = (L9BYTE *) ibuff; + obuffptr = (L9BYTE *) obuff; + /* ibuffp=76,77 */ + /* obuffptr=84,85 */ + /* list0ptr=7c,7d */ + list0ptr = dictdata; + + while (*ibuffp == 32) ++ibuffp; + + ptr = ibuffp; + do { + while (*ptr == 32) ++ptr; + if (*ptr == 0) break; + (*wordcount)++; + do { + a = *++ptr; + } while (a != 32 && a != 0); + } while (*ptr > 0); + + while (TRUE) { + ptr = ibuffp; /* 7a,7b */ + while (*ibuffp == 32) ++ibuffp; + + while (TRUE) { + a = *ibuffp; + x = *list0ptr++; + + if (a == 32) break; + if (a == 0) { + *obuffptr++ = 0; + return TRUE; + } + + ++ibuffp; + if (!IsDictionaryChar(x & 0x7f)) x = 0; + if (tolower(x & 0x7f) != tolower(a)) { + while (x > 0 && x < 0x7f) x = *list0ptr++; + if (x == 0) { + do { + a = *ibuffp++; + if (a == 0) { + *obuffptr = 0; + return TRUE; + } + } while (a != 32); + while (*ibuffp == 32) ++ibuffp; + list0ptr = dictdata; + ptr = ibuffp; + } else { + list0ptr++; + ibuffp = ptr; + } + } else if (x >= 0x7f) break; + } + + a = *ibuffp; + if (a != 32) { + ibuffp = ptr; + list0ptr += 2; + continue; + } + --list0ptr; + while (*list0ptr++ < 0x7e); + *obuffptr++ = *list0ptr; + while (*ibuffp == 32) ++ibuffp; + list0ptr = dictdata; + } +} + +void input(void) { + if (L9GameType == L9_V3 && FirstPicture >= 0) { + show_picture(FirstPicture); + FirstPicture = -1; + } + + /* if corruptinginput() returns false then, input will be called again + next time around instructionloop, this is used when save() and restore() + are called out of line */ + + codeptr--; + if (L9GameType <= L9_V2) { + int wordcount; + if (inputV2(&wordcount)) { + L9BYTE *obuffptr = (L9BYTE *) obuff; + codeptr++; + *getvar() = *obuffptr++; + *getvar() = *obuffptr++; + *getvar() = *obuffptr; + *getvar() = wordcount; + } + } else if (corruptinginput()) codeptr += 5; +} + +void varcon(void) { + L9UINT16 d6 = getcon(); + *getvar() = d6; + +#ifdef CODEFOLLOW + fprintf(f, " Var[%d]=%d)", cfvar - workspace.vartable, *cfvar); +#endif +} + +void varvar(void) { + L9UINT16 d6 = *getvar(); + *getvar() = d6; + +#ifdef CODEFOLLOW + fprintf(f, " Var[%d]=Var[%d] (=%d)", cfvar - workspace.vartable, cfvar2 - workspace.vartable, d6); +#endif +} + +void _add(void) { + L9UINT16 d0 = *getvar(); + *getvar() += d0; + +#ifdef CODEFOLLOW + fprintf(f, " Var[%d]+=Var[%d] (+=%d)", cfvar - workspace.vartable, cfvar2 - workspace.vartable, d0); +#endif +} + +void _sub(void) { + L9UINT16 d0 = *getvar(); + *getvar() -= d0; + +#ifdef CODEFOLLOW + fprintf(f, " Var[%d]-=Var[%d] (-=%d)", cfvar - workspace.vartable, cfvar2 - workspace.vartable, d0); +#endif +} + +void jump(void) { + L9UINT16 d0 = L9WORD(codeptr); + L9BYTE *a0; + codeptr += 2; + + a0 = acodeptr + ((d0 + ((*getvar()) << 1)) & 0xffff); + codeptr = acodeptr + L9WORD(a0); +} + +/* bug */ +void exit1(L9BYTE *d4, L9BYTE *d5p, L9BYTE d6, L9BYTE d7) { + L9BYTE *a0 = absdatablock; + L9BYTE d1 = d7, d0; + if (--d1) { + do { + d0 = *a0; + if (L9GameType == L9_V4) { + if ((d0 == 0) && (*(a0 + 1) == 0)) + goto notfn4; + } + a0 += 2; + } while ((d0 & 0x80) == 0 || --d1); + } + + do { + *d4 = *a0++; + if (((*d4) & 0xf) == d6) { + *d5p = *a0; + return; + } + a0++; + } while (((*d4) & 0x80) == 0); + + /* notfn4 */ +notfn4: + d6 = exitreversaltable[d6]; + a0 = absdatablock; + *d5p = 1; + + do { + *d4 = *a0++; + if (((*d4) & 0x10) == 0 || ((*d4) & 0xf) != d6) a0++; + else if (*a0++ == d7) return; + /* exit6noinc */ + if ((*d4) & 0x80)(*d5p)++; + } while (*d4); + *d5p = 0; +} + +void Exit(void) { + L9BYTE d4, d5v; + L9BYTE d7 = (L9BYTE) * getvar(); + L9BYTE d6 = (L9BYTE) * getvar(); +#ifdef CODEFOLLOW + fprintf(f, " d7=%d d6=%d", d7, d6); +#endif + exit1(&d4, &d5v, d6, d7); + + *getvar() = (d4 & 0x70) >> 4; + *getvar() = d5v; +#ifdef CODEFOLLOW + fprintf(f, " Var[%d]=%d(d4=%d) Var[%d]=%d", + cfvar2 - workspace.vartable, (d4 & 0x70) >> 4, d4, cfvar - workspace.vartable, d5v); +#endif +} + +void ifeqvt(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = *getvar(); + L9BYTE *a0 = getaddr(); + if (d0 == d1) codeptr = a0; + +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]=Var[%d] goto %d (%s)", cfvar2 - workspace.vartable, cfvar - workspace.vartable, (L9UINT32)(a0 - acodeptr), d0 == d1 ? "Yes" : "No"); +#endif +} + +void ifnevt(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = *getvar(); + L9BYTE *a0 = getaddr(); + if (d0 != d1) codeptr = a0; + +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]!=Var[%d] goto %d (%s)", cfvar2 - workspace.vartable, cfvar - workspace.vartable, (L9UINT32)(a0 - acodeptr), d0 != d1 ? "Yes" : "No"); +#endif +} + +void ifltvt(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = *getvar(); + L9BYTE *a0 = getaddr(); + if (d0 < d1) codeptr = a0; + +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]<Var[%d] goto %d (%s)", cfvar2 - workspace.vartable, cfvar - workspace.vartable, (L9UINT32)(a0 - acodeptr), d0 < d1 ? "Yes" : "No"); +#endif +} + +void ifgtvt(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = *getvar(); + L9BYTE *a0 = getaddr(); + if (d0 > d1) codeptr = a0; + +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]>Var[%d] goto %d (%s)", cfvar2 - workspace.vartable, cfvar - workspace.vartable, (L9UINT32)(a0 - acodeptr), d0 > d1 ? "Yes" : "No"); +#endif +} + +int scalex(int x) { + return (gfx_mode != GFX_V3C) ? (x >> 6) : (x >> 5); +} + +int scaley(int y) { + return (gfx_mode == GFX_V2) ? 127 - (y >> 7) : 95 - (((y >> 5) + (y >> 6)) >> 3); +} + +void detect_gfx_mode(void) { + if (L9GameType == L9_V3) { + /* These V3 games use graphics logic similar to the V2 games */ + if (strstr(FirstLine, "price of magik") != 0) + gfx_mode = GFX_V3A; + else if (strstr(FirstLine, "the archers") != 0) + gfx_mode = GFX_V3A; + else if (strstr(FirstLine, "secret diary of adrian mole") != 0) + gfx_mode = GFX_V3A; + else if ((strstr(FirstLine, "worm in paradise") != 0) + && (strstr(FirstLine, "silicon dreams") == 0)) + gfx_mode = GFX_V3A; + else if (strstr(FirstLine, "growing pains of adrian mole") != 0) + gfx_mode = GFX_V3B; + else if (strstr(FirstLine, "jewels of darkness") != 0 && picturesize < 11000) + gfx_mode = GFX_V3B; + else if (strstr(FirstLine, "silicon dreams") != 0) { + if (picturesize > 11000 + || (startdata[0] == 0x14 && startdata[1] == 0x7d) /* Return to Eden /SD (PC) */ + || (startdata[0] == 0xd7 && startdata[1] == 0x7c)) /* Worm in Paradise /SD (PC) */ + gfx_mode = GFX_V3C; + else + gfx_mode = GFX_V3B; + } else + gfx_mode = GFX_V3C; + } else + gfx_mode = GFX_V2; +} + +void _screen(void) { + int mode = 0; + + if (L9GameType == L9_V3 && strlen(FirstLine) == 0) { + if (*codeptr++) + codeptr++; + return; + } + + detect_gfx_mode(); + l9textmode = *codeptr++; + if (l9textmode) { + if (L9GameType == L9_V4) + mode = 2; + else if (picturedata) + mode = 1; + } + os_graphics(mode); + + screencalled = 1; + +#ifdef L9DEBUG + printf("screen %s", l9textmode ? "graphics" : "text"); +#endif + + if (l9textmode) { + codeptr++; + /* clearg */ + /* gintclearg */ + os_cleargraphics(); + + /* title pic */ + if (showtitle == 1 && mode == 2) { + showtitle = 0; + os_show_bitmap(0, 0, 0); + } + } + /* screent */ +} + +void cleartg(void) { + int d0 = *codeptr++; +#ifdef L9DEBUG + printf("cleartg %s", d0 ? "graphics" : "text"); +#endif + + if (d0) { + /* clearg */ + if (l9textmode) + /* gintclearg */ + os_cleargraphics(); + } + /* cleart */ + /* oswrch(0x0c) */ +} + +L9BOOL validgfxptr(L9BYTE *a5) { + return ((a5 >= picturedata) && (a5 < picturedata + picturesize)); +} + +L9BOOL findsub(int d0, L9BYTE **a5) { + int d1, d2, d3, d4; + + d1 = d0 << 4; + d2 = d1 >> 8; + *a5 = picturedata; + /* findsubloop */ + while (TRUE) { + d3 = *(*a5)++; + if (!validgfxptr(*a5)) + return FALSE; + if (d3 & 0x80) + return FALSE; + if (d2 == d3) { + if ((d1 & 0xff) == (*(*a5) & 0xf0)) { + (*a5) += 2; + return TRUE; + } + } + + d3 = *(*a5)++ & 0x0f; + if (!validgfxptr(*a5)) + return FALSE; + + d4 = **a5; + if ((d3 | d4) == 0) + return FALSE; + + (*a5) += (d3 << 8) + d4 - 2; + if (!validgfxptr(*a5)) + return FALSE; + } +} + +void gosubd0(int d0, L9BYTE **a5) { + if (GfxA5StackPos < GFXSTACKSIZE) { + GfxA5Stack[GfxA5StackPos] = *a5; + GfxA5StackPos++; + GfxScaleStack[GfxScaleStackPos] = scale; + GfxScaleStackPos++; + + if (findsub(d0, a5) == FALSE) { + GfxA5StackPos--; + *a5 = GfxA5Stack[GfxA5StackPos]; + GfxScaleStackPos--; + scale = GfxScaleStack[GfxScaleStackPos]; + } + } +} + +void newxy(int x, int y) { + drawx += (x * scale) & ~7; + drawy += (y * scale) & ~7; +} + +/* sdraw instruction plus arguments are stored in an 8 bit word. + 76543210 + iixxxyyy + where i is instruction code + x is x argument, high bit is sign + y is y argument, high bit is sign +*/ +void sdraw(int d7) { + int x, y, x1, y1; + + /* getxy1 */ + x = (d7 & 0x18) >> 3; + if (d7 & 0x20) + x = (x | 0xfc) - 0x100; + y = (d7 & 0x3) << 2; + if (d7 & 0x4) + y = (y | 0xf0) - 0x100; + + if (reflectflag & 2) + x = -x; + if (reflectflag & 1) + y = -y; + + /* gintline */ + x1 = drawx; + y1 = drawy; + newxy(x, y); + +#ifdef L9DEBUG + printf("gfx - sdraw (%d,%d) (%d,%d) colours %d,%d", + x1, y1, drawx, drawy, gintcolour & 3, option & 3); +#endif + + os_drawline(scalex(x1), scaley(y1), scalex(drawx), scaley(drawy), + gintcolour & 3, option & 3); +} + +/* smove instruction plus arguments are stored in an 8 bit word. + 76543210 + iixxxyyy + where i is instruction code + x is x argument, high bit is sign + y is y argument, high bit is sign +*/ +void smove(int d7) { + int x, y; + + /* getxy1 */ + x = (d7 & 0x18) >> 3; + if (d7 & 0x20) + x = (x | 0xfc) - 0x100; + y = (d7 & 0x3) << 2; + if (d7 & 0x4) + y = (y | 0xf0) - 0x100; + + if (reflectflag & 2) + x = -x; + if (reflectflag & 1) + y = -y; + newxy(x, y); +} + +void sgosub(int d7, L9BYTE **a5) { + int d0 = d7 & 0x3f; +#ifdef L9DEBUG + printf("gfx - sgosub 0x%.2x", d0); +#endif + gosubd0(d0, a5); +} + +/* draw instruction plus arguments are stored in a 16 bit word. + FEDCBA9876543210 + iiiiixxxxxxyyyyy + where i is instruction code + x is x argument, high bit is sign + y is y argument, high bit is sign +*/ +void draw(int d7, L9BYTE **a5) { + int xy, x, y, x1, y1; + + /* getxy2 */ + xy = (d7 << 8) + (*(*a5)++); + x = (xy & 0x3e0) >> 5; + if (xy & 0x400) + x = (x | 0xe0) - 0x100; + y = (xy & 0xf) << 2; + if (xy & 0x10) + y = (y | 0xc0) - 0x100; + + if (reflectflag & 2) + x = -x; + if (reflectflag & 1) + y = -y; + + /* gintline */ + x1 = drawx; + y1 = drawy; + newxy(x, y); + +#ifdef L9DEBUG + printf("gfx - draw (%d,%d) (%d,%d) colours %d,%d", + x1, y1, drawx, drawy, gintcolour & 3, option & 3); +#endif + + os_drawline(scalex(x1), scaley(y1), scalex(drawx), scaley(drawy), + gintcolour & 3, option & 3); +} + +/* move instruction plus arguments are stored in a 16 bit word. + FEDCBA9876543210 + iiiiixxxxxxyyyyy + where i is instruction code + x is x argument, high bit is sign + y is y argument, high bit is sign +*/ +void _move(int d7, L9BYTE **a5) { + int xy, x, y; + + /* getxy2 */ + xy = (d7 << 8) + (*(*a5)++); + x = (xy & 0x3e0) >> 5; + if (xy & 0x400) + x = (x | 0xe0) - 0x100; + y = (xy & 0xf) << 2; + if (xy & 0x10) + y = (y | 0xc0) - 0x100; + + if (reflectflag & 2) + x = -x; + if (reflectflag & 1) + y = -y; + newxy(x, y); +} + +void icolour(int d7) { + gintcolour = d7 & 3; +#ifdef L9DEBUG + printf("gfx - icolour 0x%.2x", gintcolour); +#endif +} + +void size(int d7) { + static int sizetable[7] = { 0x02, 0x04, 0x06, 0x07, 0x09, 0x0c, 0x10 }; + + d7 &= 7; + if (d7) { + int d0 = (scale * sizetable[d7 - 1]) >> 3; + scale = (d0 < 0x100) ? d0 : 0xff; + } else { + /* sizereset */ + scale = 0x80; + if (gfx_mode == GFX_V2 || gfx_mode == GFX_V3A) + GfxScaleStackPos = 0; + } + +#ifdef L9DEBUG + printf("gfx - size 0x%.2x", scale); +#endif +} + +void gintfill(int d7) { + if ((d7 & 7) == 0) + /* filla */ + d7 = gintcolour; + else + d7 &= 3; + /* fillb */ + +#ifdef L9DEBUG + printf("gfx - gintfill (%d,%d) colours %d,%d", drawx, drawy, d7 & 3, option & 3); +#endif + + os_fill(scalex(drawx), scaley(drawy), d7 & 3, option & 3); +} + +void gosub(int d7, L9BYTE **a5) { + int d0 = ((d7 & 7) << 8) + (*(*a5)++); +#ifdef L9DEBUG + printf("gfx - gosub 0x%.2x", d0); +#endif + gosubd0(d0, a5); +} + +void reflect(int d7) { +#ifdef L9DEBUG + printf("gfx - reflect 0x%.2x", d7); +#endif + + if (d7 & 4) { + d7 &= 3; + d7 ^= reflectflag; + } + /* reflect1 */ + reflectflag = d7; +} + +void notimp(void) { +#ifdef L9DEBUG + printf("gfx - notimp"); +#endif +} + +void gintchgcol(L9BYTE **a5) { + int d0 = *(*a5)++; + +#ifdef L9DEBUG + printf("gfx - gintchgcol %d %d", (d0 >> 3) & 3, d0 & 7); +#endif + + os_setcolour((d0 >> 3) & 3, d0 & 7); +} + +void amove(L9BYTE **a5) { + drawx = 0x40 * (*(*a5)++); + drawy = 0x40 * (*(*a5)++); +#ifdef L9DEBUG + printf("gfx - amove (%d,%d)", drawx, drawy); +#endif +} + +void opt(L9BYTE **a5) { + int d0 = *(*a5)++; +#ifdef L9DEBUG + printf("gfx - opt 0x%.2x", d0); +#endif + + if (d0) + d0 = (d0 & 3) | 0x80; + /* optend */ + option = d0; +} + +void restorescale(void) { +#ifdef L9DEBUG + printf("gfx - restorescale"); +#endif + if (GfxScaleStackPos > 0) + scale = GfxScaleStack[GfxScaleStackPos - 1]; +} + +L9BOOL rts(L9BYTE **a5) { + if (GfxA5StackPos > 0) { + GfxA5StackPos--; + *a5 = GfxA5Stack[GfxA5StackPos]; + if (GfxScaleStackPos > 0) { + GfxScaleStackPos--; + scale = GfxScaleStack[GfxScaleStackPos]; + } + return TRUE; + } + return FALSE; +} + +L9BOOL getinstruction(L9BYTE **a5) { + int d7 = *(*a5)++; + if ((d7 & 0xc0) != 0xc0) { + switch ((d7 >> 6) & 3) { + case 0: + sdraw(d7); + break; + case 1: + smove(d7); + break; + case 2: + sgosub(d7, a5); + break; + } + } else if ((d7 & 0x38) != 0x38) { + switch ((d7 >> 3) & 7) { + case 0: + draw(d7, a5); + break; + case 1: + _move(d7, a5); + break; + case 2: + icolour(d7); + break; + case 3: + size(d7); + break; + case 4: + gintfill(d7); + break; + case 5: + gosub(d7, a5); + break; + case 6: + reflect(d7); + break; + } + } else { + switch (d7 & 7) { + case 0: + notimp(); + break; + case 1: + gintchgcol(a5); + break; + case 2: + notimp(); + break; + case 3: + amove(a5); + break; + case 4: + opt(a5); + break; + case 5: + restorescale(); + break; + case 6: + notimp(); + break; + case 7: + return rts(a5); + } + } + return TRUE; +} + +void absrunsub(int d0) { + L9BYTE *a5; + if (!findsub(d0, &a5)) + return; + while (getinstruction(&a5)); +} + +void show_picture(int pic) { + if (L9GameType == L9_V3 && strlen(FirstLine) == 0) { + FirstPicture = pic; + return; + } + + if (picturedata) { + /* Some games don't call the screen() opcode before drawing + graphics, so here graphics are enabled if necessary. */ + if ((screencalled == 0) && (l9textmode == 0)) { + detect_gfx_mode(); + l9textmode = 1; + os_graphics(1); + } + +#ifdef L9DEBUG + printf("picture %d", pic); +#endif + + os_cleargraphics(); + /* gintinit */ + gintcolour = 3; + option = 0x80; + reflectflag = 0; + drawx = 0x1400; + drawy = 0x1400; + /* sizereset */ + scale = 0x80; + + GfxA5StackPos = 0; + GfxScaleStackPos = 0; + absrunsub(0); + if (!findsub(pic, &gfxa5)) + gfxa5 = NULL; + } +} + +void picture(void) { + show_picture(*getvar()); +} + +void GetPictureSize(int *width, int *height) { + if (L9GameType == L9_V4) { + if (width != NULL) + *width = 0; + if (height != NULL) + *height = 0; + } else { + if (width != NULL) + *width = (gfx_mode != GFX_V3C) ? 160 : 320; + if (height != NULL) + *height = (gfx_mode == GFX_V2) ? 128 : 96; + } +} + +L9BOOL RunGraphics(void) { + if (gfxa5) { + if (!getinstruction(&gfxa5)) + gfxa5 = NULL; + return TRUE; + } + return FALSE; +} + +void initgetobj(void) { + int i; + numobjectfound = 0; + object = 0; + for (i = 0; i < 32; i++) gnoscratch[i] = 0; +} + +void getnextobject(void) { + int d2, d3, d4; + L9UINT16 *hisearchposvar, *searchposvar; + +#ifdef L9DEBUG + printf("getnextobject"); +#endif + + d2 = *getvar(); + hisearchposvar = getvar(); + searchposvar = getvar(); + d3 = *hisearchposvar; + d4 = *searchposvar; + + /* gnoabs */ + do { + if ((d3 | d4) == 0) { + /* initgetobjsp */ + gnosp = 128; + searchdepth = 0; + initgetobj(); + break; + } + + if (numobjectfound == 0) inithisearchpos = d3; + + /* gnonext */ + do { + if (d4 == list2ptr[++object]) { + /* gnomaybefound */ + int d6 = list3ptr[object] & 0x1f; + if (d6 != d3) { + if (d6 == 0 || d3 == 0) continue; + if (d3 != 0x1f) { + gnoscratch[d6] = d6; + continue; + } + d3 = d6; + } + /* gnofound */ + numobjectfound++; + gnostack[--gnosp] = object; + gnostack[--gnosp] = 0x1f; + + *hisearchposvar = d3; + *searchposvar = d4; + *getvar() = object; + *getvar() = numobjectfound; + *getvar() = searchdepth; + return; + } + } while (object <= d2); + + if (inithisearchpos == 0x1f) { + gnoscratch[d3] = 0; + d3 = 0; + + /* gnoloop */ + do { + if (gnoscratch[d3]) { + gnostack[--gnosp] = d4; + gnostack[--gnosp] = d3; + } + } while (++d3 < 0x1f); + } + /* gnonewlevel */ + if (gnosp != 128) { + d3 = gnostack[gnosp++]; + d4 = gnostack[gnosp++]; + } else d3 = d4 = 0; + + numobjectfound = 0; + if (d3 == 0x1f) searchdepth++; + + initgetobj(); + } while (d4); + + /* gnofinish */ + /* gnoreturnargs */ + *hisearchposvar = 0; + *searchposvar = 0; + *getvar() = object = 0; + *getvar() = numobjectfound; + *getvar() = searchdepth; +} + +void ifeqct(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = getcon(); + L9BYTE *a0 = getaddr(); + if (d0 == d1) codeptr = a0; +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]=%d goto %d (%s)", cfvar - workspace.vartable, d1, (L9UINT32)(a0 - acodeptr), d0 == d1 ? "Yes" : "No"); +#endif +} + +void ifnect(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = getcon(); + L9BYTE *a0 = getaddr(); + if (d0 != d1) codeptr = a0; +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]!=%d goto %d (%s)", cfvar - workspace.vartable, d1, (L9UINT32)(a0 - acodeptr), d0 != d1 ? "Yes" : "No"); +#endif +} + +void ifltct(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = getcon(); + L9BYTE *a0 = getaddr(); + if (d0 < d1) codeptr = a0; +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]<%d goto %d (%s)", cfvar - workspace.vartable, d1, (L9UINT32)(a0 - acodeptr), d0 < d1 ? "Yes" : "No"); +#endif +} + +void ifgtct(void) { + L9UINT16 d0 = *getvar(); + L9UINT16 d1 = getcon(); + L9BYTE *a0 = getaddr(); + if (d0 > d1) codeptr = a0; +#ifdef CODEFOLLOW + fprintf(f, " if Var[%d]>%d goto %d (%s)", cfvar - workspace.vartable, d1, (L9UINT32)(a0 - acodeptr), d0 > d1 ? "Yes" : "No"); +#endif +} + +void printinput(void) { + L9BYTE *ptr = (L9BYTE *) obuff; + char c; + while ((c = *ptr++) != ' ') printchar(c); + +#ifdef L9DEBUG + printf("printinput"); +#endif +} + +void listhandler(void) { + L9BYTE *a4, *MinAccess, *MaxAccess; + L9UINT16 val; + L9UINT16 *var; +#ifdef CODEFOLLOW + int offset; +#endif + + if ((code & 0x1f) > 0xa) { + error("\rillegal list access %d\r", code & 0x1f); + Running = FALSE; + return; + } + a4 = L9Pointers[1 + (code & 0x1f)]; + + if (a4 >= workspace.listarea && a4 < workspace.listarea + LISTAREASIZE) { + MinAccess = workspace.listarea; + MaxAccess = workspace.listarea + LISTAREASIZE; + } else { + MinAccess = startdata; + MaxAccess = startdata + FileSize; + } + + if (code >= 0xe0) { + /* listvv */ +#ifndef CODEFOLLOW + a4 += *getvar(); + val = *getvar(); +#else + offset = *getvar(); + a4 += offset; + var = getvar(); + val = *var; + fprintf(f, " list %d [%d]=Var[%d] (=%d)", code & 0x1f, offset, var - workspace.vartable, val); +#endif + + if (a4 >= MinAccess && a4 < MaxAccess) *a4 = (L9BYTE) val; +#ifdef L9DEBUG + else printf("Out of range list access"); +#endif + } else if (code >= 0xc0) { + /* listv1c */ +#ifndef CODEFOLLOW + a4 += *codeptr++; + var = getvar(); +#else + offset = *codeptr++; + a4 += offset; + var = getvar(); + fprintf(f, " Var[%d]= list %d [%d])", var - workspace.vartable, code & 0x1f, offset); + if (a4 >= MinAccess && a4 < MaxAccess) fprintf(f, " (=%d)", *a4); +#endif + + if (a4 >= MinAccess && a4 < MaxAccess) *var = *a4; + else { + *var = 0; +#ifdef L9DEBUG + printf("Out of range list access"); +#endif + } + } else if (code >= 0xa0) { + /* listv1v */ +#ifndef CODEFOLLOW + a4 += *getvar(); + var = getvar(); +#else + offset = *getvar(); + a4 += offset; + var = getvar(); + + fprintf(f, " Var[%d] =list %d [%d]", var - workspace.vartable, code & 0x1f, offset); + if (a4 >= MinAccess && a4 < MaxAccess) fprintf(f, " (=%d)", *a4); +#endif + + if (a4 >= MinAccess && a4 < MaxAccess) *var = *a4; + else { + *var = 0; +#ifdef L9DEBUG + printf("Out of range list access"); +#endif + } + } else { +#ifndef CODEFOLLOW + a4 += *codeptr++; + val = *getvar(); +#else + offset = *codeptr++; + a4 += offset; + var = getvar(); + val = *var; + fprintf(f, " list %d [%d]=Var[%d] (=%d)", code & 0x1f, offset, var - workspace.vartable, val); +#endif + + if (a4 >= MinAccess && a4 < MaxAccess) *a4 = (L9BYTE) val; +#ifdef L9DEBUG + else printf("Out of range list access"); +#endif + } +} + +void executeinstruction(void) { +#ifdef CODEFOLLOW + f = fopen(CODEFOLLOWFILE, "a"); + fprintf(f, "%ld (s:%d) %x", (L9UINT32)(codeptr - acodeptr) - 1, workspace.stackptr, code); + if (!(code & 0x80)) + fprintf(f, " = %s", codes[code & 0x1f]); +#endif + + if (code & 0x80) + listhandler(); + else { + switch (code & 0x1f) { + case 0: + Goto(); + break; + case 1: + intgosub(); + break; + case 2: + intreturn(); + break; + case 3: + printnumber(); + break; + case 4: + messagev(); + break; + case 5: + messagec(); + break; + case 6: + function(); + break; + case 7: + input(); + break; + case 8: + varcon(); + break; + case 9: + varvar(); + break; + case 10: + _add(); + break; + case 11: + _sub(); + break; + case 12: + ilins(code & 0x1f); + break; + case 13: + ilins(code & 0x1f); + break; + case 14: + jump(); + break; + case 15: + Exit(); + break; + case 16: + ifeqvt(); + break; + case 17: + ifnevt(); + break; + case 18: + ifltvt(); + break; + case 19: + ifgtvt(); + break; + case 20: + _screen(); + break; + case 21: + cleartg(); + break; + case 22: + picture(); + break; + case 23: + getnextobject(); + break; + case 24: + ifeqct(); + break; + case 25: + ifnect(); + break; + case 26: + ifltct(); + break; + case 27: + ifgtct(); + break; + case 28: + printinput(); + break; + case 29: + ilins(code & 0x1f); + break; + case 30: + ilins(code & 0x1f); + break; + case 31: + ilins(code & 0x1f); + break; + } + } +#ifdef CODEFOLLOW + fprintf(f, "\n"); + f.close(); +#endif +} + +L9BOOL LoadGame2(const char *filename, char *picname) { +#ifdef CODEFOLLOW + f = fopen(CODEFOLLOWFILE, "w"); + fprintf(f, "Code follow file...\n"); + f.close(); +#endif + + /* may be already running a game, maybe in input routine */ + Running = FALSE; + ibuffptr = NULL; + + /* intstart */ + if (!intinitialise(filename, picname)) return FALSE; + /* if (!checksumgamedata()) return FALSE; */ + + codeptr = acodeptr; + if (constseed > 0) + randomseed = constseed; + else + randomseed = (L9UINT16)g_system->getMillis(); + strcpy(LastGame, filename); + return Running = TRUE; +} + +L9BOOL LoadGame(const char *filename, char *picname) { + L9BOOL ret = LoadGame2(filename, picname); + showtitle = 1; + clearworkspace(); + workspace.stackptr = 0; + /* need to clear listarea as well */ + memset((L9BYTE *) workspace.listarea, 0, LISTAREASIZE); + return ret; +} + +/* can be called from input to cause fall through for exit */ +void StopGame(void) { + Running = FALSE; +} + +L9BOOL RunGame(void) { + code = *codeptr++; + /* printf("%d",code); */ + executeinstruction(); + return Running; +} + +void RestoreGame(char *filename) { + int Bytes; + GameState temp; + Common::File f; + + if (f.open(filename)) { + // TODO: This is horribly unportable + Bytes = f.read(&temp, sizeof(GameState)); + if (Bytes == V1FILESIZE) { + printstring("\rGame restored.\r"); + /* only copy in workspace */ + memset(workspace.listarea, 0, LISTAREASIZE); + memmove(workspace.vartable, &temp, V1FILESIZE); + } else if (CheckFile(&temp)) { + printstring("\rGame restored.\r"); + /* full restore */ + memmove(&workspace, &temp, sizeof(GameState)); + codeptr = acodeptr + workspace.codeptr; + } else + printstring("\rSorry, unrecognised format. Unable to restore\r"); + } else + printstring("\rUnable to restore game.\r"); +} + +} // End of namespace Level9 +} // End of namespace Glk |