Files
scummvm-cursorfix/engines/glk/level9/level9_main.cpp
2026-02-02 04:50:13 +01:00

3095 lines
60 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* 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 */
struct SaveStruct {
L9UINT16 vartable[256];
L9BYTE listarea[LISTAREASIZE];
};
/* Enumerations */
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, *pictureaddress, *picturedata;
byte *startdata;
uint32 FileSize, picturesize;
byte *L9Pointers[12];
byte *absdatablock, *list2ptr, *list3ptr, *list9startptr, *acodeptr;
byte *startmd, *endmd, *endwdp5, *wordtable, *dictdata, *defdict;
L9UINT16 dictdatalen;
byte *startmdV2;
int wordcase;
int unpackcount;
char unpackbuf[8];
L9BYTE *dictptr;
char threechars[34];
int L9MsgType;
char LastGame[MAX_PATH];
char FirstLine[FIRSTLINESIZE];
int FirstLinePos;
int FirstPicture;
SaveStruct ramsavearea[RAMSAVESLOTS];
GameState workspace;
L9UINT16 randomseed;
L9UINT16 constseed;
L9BOOL Running;
char ibuff[IBUFFSIZE];
L9BYTE *ibuffptr;
char obuff[34];
Common::SeekableReadStream *scriptfile;
L9BOOL Cheating;
int CheatWord;
GameState CheatWorkspace;
int reflectflag, scale, gintcolour, option;
int l9textmode, drawx, drawy, screencalled, showtitle;
L9BYTE *gfxa5;
Bitmap *bitmap;
int gfx_mode;
L9BYTE *GfxA5Stack[GFXSTACKSIZE];
int GfxA5StackPos;
int GfxScaleStack[GFXSTACKSIZE];
int GfxScaleStackPos;
char lastchar;
char lastactualchar;
int d5;
L9BYTE *codeptr; /* instruction codes */
L9BYTE code;
L9BYTE *list9ptr;
int unpackd3;
const 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;
/* Prototypes */
L9BOOL LoadGame2(const char *filename, char *picname);
int getlongcode();
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;
const char *const 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",
};
const char *const functions[] = {
"calldriver",
"L9Random",
"save",
"restore",
"clearworkspace",
"clearstack"
};
const char *const 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 level9_initialize() {
FirstLinePos = 0;
FirstPicture = -1;
constseed = 0;
scriptfile = nullptr;
Cheating = FALSE;
l9textmode = 0;
drawx = 0;
drawy = 0;
screencalled = 0;
showtitle = 1;
gfxa5 = nullptr;
gfx_mode = GFX_V2;
GfxA5StackPos = 0;
GfxScaleStackPos = 0;
lastchar = '.';
lastactualchar = 0;
g_vm->_detection._l9V1Game = -1;
startfile = pictureaddress = picturedata = nullptr;
}
void initdict(L9BYTE *ptr) {
dictptr = ptr;
unpackcount = 8;
}
char getdictionarycode() {
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() {
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];
Common::sprintf_s(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 == nullptr) {
error("Unable to allocate memory for the game! Exiting...");
}
}
void FreeMemory() {
if (startfile) {
free(startfile);
startfile = nullptr;
}
if (pictureaddress) {
free(pictureaddress);
pictureaddress = nullptr;
}
if (bitmap) {
free(bitmap);
bitmap = nullptr;
}
if (scriptfile) {
delete scriptfile;
scriptfile = nullptr;
}
picturedata = nullptr;
picturesize = 0;
gfxa5 = nullptr;
}
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;
}
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 = nullptr;
}
picturedata = nullptr;
picturesize = 0;
gfxa5 = nullptr;
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 = nullptr;
picturesize = 0;
}
f.close();
}
}
screencalled = 0;
l9textmode = 0;
Offset = g_vm->_detection.scanner(startfile, FileSize, &dictdata, &acodeptr);
if (Offset < 0) {
error("Unable to locate valid Level 9 game in file: %s", filename);
return FALSE;
}
startdata = startfile + Offset;
FileSize -= Offset;
/* setup pointers */
if (g_vm->_detection._gameType == L9_V1) {
if (g_vm->_detection._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 < 5; i++) {
int off = g_vm->_detection.v1Game().L9Ptrs[i];
if (off < 0)
L9Pointers[i + 2] = acodeptr + off;
else
L9Pointers[i + 2] = workspace.listarea + off;
}
absdatablock = acodeptr - g_vm->_detection.v1Game().absData;
} else {
/* V2,V3,V4 */
hdoffset = g_vm->_detection._gameType == 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 (g_vm->_detection._gameType) {
case L9_V1: {
double a1;
startmd = acodeptr + g_vm->_detection.v1Game().msgStart;
startmdV2 = startmd + g_vm->_detection.v1Game().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 = nullptr;
picturesize = 0;
}
} else {
if (!findsubs(startdata, FileSize, &picturedata, &picturesize)
&& !findsubs(startfile, startdata - startfile, &picturedata, &picturesize)) {
picturedata = nullptr;
picturesize = 0;
}
}
#endif
memset(FirstLine, 0, FIRSTLINESIZE);
FirstLinePos = 0;
return TRUE;
}
static byte calcChecksum(byte *ptr, uint32 num) {
byte d1 = 0;
while (num-- != 0) d1 += *ptr++;
return d1;
}
L9BOOL checksumgamedata() {
return calcChecksum(startdata, L9WORD(startdata) + 1) == 0;
}
L9UINT16 movewa5d0() {
L9UINT16 ret = L9WORD(codeptr);
codeptr += 2;
return ret;
}
L9UINT16 getcon() {
if (code & 64) {
/* getconsmall */
return *codeptr++;
} else return movewa5d0();
}
L9BYTE *getaddr() {
if (code & 0x20) {
/* getaddrshort */
signed char diff = *codeptr++;
return codeptr + diff - 1;
} else {
return acodeptr + movewa5d0();
}
}
L9UINT16 *getvar() {
#ifndef CODEFOLLOW
return workspace.vartable + *codeptr++;
#else
cfvar2 = cfvar;
return cfvar = workspace.vartable + *codeptr++;
#endif
}
void Goto() {
L9BYTE *target = getaddr();
if (target == codeptr - 2)
Running = FALSE; /* Endless loop! */
else
codeptr = target;
}
void intgosub() {
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() {
if (workspace.stackptr == 0) {
error("\rStack underflow error\r");
Running = FALSE;
return;
}
codeptr = acodeptr + workspace.stack[--workspace.stackptr];
}
void printnumber() {
printdecimald0(*getvar());
}
void messagec() {
if (g_vm->_detection._gameType <= L9_V2)
printmessageV2(getcon());
else
printmessage(getcon());
}
void messagev() {
if (g_vm->_detection._gameType <= 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].vartable, workspace.vartable, sizeof(workspace.vartable));
memmove(ramsavearea[i].listarea, workspace.listarea, sizeof(workspace.listarea));
}
void ramload(int i) {
#ifdef L9DEBUG
printf("driver - ramload %d", i);
#endif
memmove(workspace.vartable, ramsavearea[i].vartable, sizeof(workspace.vartable));
memmove(workspace.listarea, ramsavearea[i].listarea, sizeof(workspace.listarea));
}
void calldriver() {
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];
Common::strcpy_s(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, nullptr);
} else driver(d0, a6);
}
void L9Random() {
#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
}
L9BOOL CheckFile(GameState *gs) {
L9UINT16 checksum;
if (gs->Id != L9_ID)
return FALSE;
checksum = gs->checksum;
gs->calculateChecksum();
if (checksum != gs->checksum)
return FALSE;
return TRUE;
}
void save() {
if (g_vm->saveGame().getCode() == Common::kNoError)
printstring("\rGame saved.\r");
else
printstring("\rUnable to save game.\r");
}
void restore() {
if (g_vm->loadGame().getCode() == Common::kNoError)
printstring("\rGame restored.\r");
else
printstring("\rUnable to restore game.\r");
}
void NormalRestore() {
#ifdef L9DEBUG
printf("function - restore");
#endif
if (Cheating) {
/* not really an error */
Cheating = FALSE;
error("\rWord is: %s\r", ibuff);
}
restore();
}
void playback() {
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 != nullptr) {
if (scriptfile->eos()) {
delete scriptfile;
scriptfile = nullptr;
} 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() {
memset(workspace.vartable, 0, sizeof(workspace.vartable));
}
void ilins(int d0) {
error("\rIllegal instruction: %d\r", d0);
Running = FALSE;
}
void function() {
int d0 = *codeptr++;
#ifdef CODEFOLLOW
fprintf(f, " %s", d0 == 250 ? "printstr" : functions[d0 - 1]);
#endif
switch (d0) {
case 1:
if (g_vm->_detection._gameType == 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() {
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() {
if (*obuff >= 0x30 && *obuff < 0x3a) {
if (g_vm->_detection._gameType == 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() {
/* restore game status */
memmove(&workspace, &CheatWorkspace, sizeof(GameState));
codeptr = acodeptr + workspace.codeptr;
if (!((g_vm->_detection._gameType <= L9_V2) ? GetWordV2(ibuff, CheatWord++) : GetWordV3(ibuff, CheatWord++))) {
Cheating = FALSE;
printstring("\rCheat failed.\r");
*ibuff = 0;
}
}
void StartCheat() {
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 */
}
}
Common::strcpy_s(buff, IBUFFSIZE, threechars);
for (i = 0; i < (int)strlen(buff); i++) buff[i] &= 0x7f;
return TRUE;
}
L9BOOL CheckHash() {
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 ((g_vm->_detection._gameType <= 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 (g_vm->_detection._gameType == 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 ((g_vm->_detection._gameType >= L9_V3) && (c == '.' || c == ','))
return TRUE;
return Common::isAlnum(c);
}
L9BOOL corruptinginput() {
L9BYTE *a0, *a2, *a6;
int d0, d1, d2, keywordnumber, abrevword;
char *iptr;
list9ptr = list9startptr;
if (ibuffptr == nullptr) {
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 = nullptr;
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();
// Unused variables
(void)keywordnumber;
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() {
if (g_vm->_detection._gameType == 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 (g_vm->_detection._gameType <= 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() {
L9UINT16 d6 = getcon();
*getvar() = d6;
#ifdef CODEFOLLOW
fprintf(f, " Var[%d]=%d)", cfvar - workspace.vartable, *cfvar);
#endif
}
void varvar() {
L9UINT16 d6 = *getvar();
*getvar() = d6;
#ifdef CODEFOLLOW
fprintf(f, " Var[%d]=Var[%d] (=%d)", cfvar - workspace.vartable, cfvar2 - workspace.vartable, d6);
#endif
}
void _add() {
L9UINT16 d0 = *getvar();
*getvar() += d0;
#ifdef CODEFOLLOW
fprintf(f, " Var[%d]+=Var[%d] (+=%d)", cfvar - workspace.vartable, cfvar2 - workspace.vartable, d0);
#endif
}
void _sub() {
L9UINT16 d0 = *getvar();
*getvar() -= d0;
#ifdef CODEFOLLOW
fprintf(f, " Var[%d]-=Var[%d] (-=%d)", cfvar - workspace.vartable, cfvar2 - workspace.vartable, d0);
#endif
}
void jump() {
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 (g_vm->_detection._gameType == 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() {
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() {
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() {
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() {
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() {
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() {
if (g_vm->_detection._gameType == L9_V3) {
/* These V3 games use graphics logic similar to the V2 games */
if (strstr(FirstLine, "price of magik") != nullptr)
gfx_mode = GFX_V3A;
else if (strstr(FirstLine, "the archers") != nullptr)
gfx_mode = GFX_V3A;
else if (strstr(FirstLine, "secret diary of adrian mole") != nullptr)
gfx_mode = GFX_V3A;
else if ((strstr(FirstLine, "worm in paradise") != nullptr)
&& (strstr(FirstLine, "silicon dreams") == nullptr))
gfx_mode = GFX_V3A;
else if (strstr(FirstLine, "growing pains of adrian mole") != nullptr)
gfx_mode = GFX_V3B;
else if (strstr(FirstLine, "jewels of darkness") != nullptr && picturesize < 11000)
gfx_mode = GFX_V3B;
else if (strstr(FirstLine, "silicon dreams") != nullptr) {
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() {
int mode = 0;
if (g_vm->_detection._gameType == L9_V3 && strlen(FirstLine) == 0) {
if (*codeptr++)
codeptr++;
return;
}
detect_gfx_mode();
l9textmode = *codeptr++;
if (l9textmode) {
if (g_vm->_detection._gameType == 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() {
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() {
#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() {
#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 (g_vm->_detection._gameType == 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 = nullptr;
}
}
void picture() {
show_picture(*getvar());
}
void GetPictureSize(int *width, int *height) {
if (g_vm->_detection._gameType == L9_V4) {
if (width != nullptr)
*width = 0;
if (height != nullptr)
*height = 0;
} else {
if (width != nullptr)
*width = (gfx_mode != GFX_V3C) ? 160 : 320;
if (height != nullptr)
*height = (gfx_mode == GFX_V2) ? 128 : 96;
}
}
L9BOOL RunGraphics() {
if (gfxa5) {
if (!getinstruction(&gfxa5))
gfxa5 = nullptr;
return TRUE;
}
return FALSE;
}
void initgetobj() {
int i;
numobjectfound = 0;
object = 0;
for (i = 0; i < 32; i++) gnoscratch[i] = 0;
}
void getnextobject() {
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() {
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() {
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() {
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() {
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() {
L9BYTE *ptr = (L9BYTE *) obuff;
char c;
while ((c = *ptr++) != ' ') printchar(c);
#ifdef L9DEBUG
printf("printinput");
#endif
}
void listhandler() {
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() {
#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 = nullptr;
/* intstart */
if (!intinitialise(filename, picname)) return FALSE;
/* if (!checksumgamedata()) return FALSE; */
codeptr = acodeptr;
if (constseed > 0)
randomseed = constseed;
else
randomseed = (L9UINT16)g_system->getMillis();
Common::strcpy_s(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() {
Running = FALSE;
}
L9BOOL RunGame() {
code = *codeptr++;
/* printf("%d",code); */
executeinstruction();
if (g_vm->shouldQuit())
Running = false;
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");
}
/*----------------------------------------------------------------------*/
void GameState::synchronize(Common::Serializer &s) {
if (s.isSaving()) {
Id = L9_ID;
this->codeptr = ::Glk::Level9::codeptr - ::Glk::Level9::acodeptr;
listsize = LISTAREASIZE;
stacksize = STACKSIZE;
calculateChecksum();
}
s.syncAsUint32LE(Id);
s.syncAsUint16LE(codeptr);
s.syncAsUint16LE(stackptr);
s.syncAsUint16LE(listsize);
s.syncAsUint16LE(stacksize);
s.syncAsUint16LE(checksum);
for (int i = 0; i < 256; ++i)
s.syncAsUint16LE(vartable[i]);
s.syncBytes(listarea, LISTAREASIZE);
for (int i = 0; i < STACKSIZE; ++i)
s.syncAsUint16LE(stack[i]);
}
#define CHECKSUMW(V) checksum += (V & 0xff) + ((V >> 8) & 0xff)
#define CHECKSUML(V) checksum += (V & 0xff) + ((V >> 8) & 0xff) + ((V >> 16) & 0xff) + ((V >> 24) & 0xff)
void GameState::calculateChecksum() {
checksum = 0;
CHECKSUML(Id);
CHECKSUMW(codeptr);
CHECKSUMW(stackptr);
CHECKSUMW(listsize);
CHECKSUMW(stacksize);
for (int i = 0; i < 256; ++i)
CHECKSUMW(vartable[i]);
for (int i = 0; i < LISTAREASIZE; ++i)
checksum += listarea[i];
for (int i = 0; i < STACKSIZE; ++i)
CHECKSUMW(stack[i]);
}
} // End of namespace Level9
} // End of namespace Glk