diff --git a/generic.c b/generic.c new file mode 100755 index 0000000..484c3b7 --- /dev/null +++ b/generic.c @@ -0,0 +1,166 @@ +#include +#include +#include "level9.h" + +#define TEXTBUFFER_SIZE 10240 +char TextBuffer[TEXTBUFFER_SIZE+1]; +int TextBufferPtr = 0; + +int Column = 0; +#define SCREENWIDTH 76 + +void os_printchar(char c) +{ + if (c == '\r') + { + os_flush(); + putchar('\n'); + Column = 0; + return; + } + if (isprint(c) == 0) return; + if (TextBufferPtr >= TEXTBUFFER_SIZE) os_flush(); + *(TextBuffer + (TextBufferPtr++)) = c; +} + +L9BOOL os_input(char *ibuff, int size) +{ +char *nl; + + os_flush(); + fgets(ibuff, size, stdin); + nl = strchr(ibuff, '\n'); + if (nl) *nl = 0; +} + +char os_readchar(void) +{ + os_flush(); + return getc(stdin); /* will require enter key as well */ +} + +void os_flush(void) +{ +static int semaphore = 0; +int ptr, space, lastspace, searching; + + if (TextBufferPtr < 1) return; + if (semaphore) return; + semaphore = 1; + + *(TextBuffer+TextBufferPtr) = ' '; + ptr = 0; + while (TextBufferPtr + Column > SCREENWIDTH) + { + space = ptr; + lastspace = space; + searching = 1; + while (searching) + { + while (TextBuffer[space] != ' ') space++; + if (space - ptr + Column > SCREENWIDTH) + { + space = lastspace; + printf("%.*s\n", space - ptr, TextBuffer + ptr); + Column = 0; + space++; + if (TextBuffer[space] == ' ') space++; + TextBufferPtr -= (space - ptr); + ptr = space; + searching = 0; + } + else lastspace = space; + space++; + } + } + printf("%.*s", TextBufferPtr, TextBuffer + ptr); + Column += TextBufferPtr; + TextBufferPtr = 0; + + semaphore = 0; +} + +L9BOOL os_save_file(L9BYTE * Ptr, int Bytes) +{ +char name[256]; +char *nl; +FILE *f; + + os_flush(); + printf("Save file: "); + fgets(name, 256, stdin); + nl = strchr(name, '\n'); + if (nl) *nl = 0; + f = fopen(name, "wb"); + if (!f) return FALSE; + fwrite(Ptr, 1, Bytes, f); + fclose(f); + return TRUE; +} + +L9BOOL os_load_file(L9BYTE *Ptr,int *Bytes,int Max) +{ +char name[256]; +char *nl; +FILE *f; + + os_flush(); + printf("Load file: "); + fgets(name, 256, stdin); + nl = strchr(name, '\n'); + if (nl) *nl = 0; + f = fopen(name, "rb"); + if (!f) return FALSE; + *Bytes = fread(Ptr, 1, Max, f); + fclose(f); + return TRUE; +} + +L9BOOL os_get_game_file(char *NewName,int Size) +{ +char *nl; + + os_flush(); + printf("Load next game: "); + fgets(NewName, Size, stdin); + nl = strchr(NewName, '\n'); + if (nl) *nl = 0; + return TRUE; +} + +void os_set_filenumber(char *NewName,int Size,int n) +{ +char *p; +int i; + + p = strrchr(NewName,FILE_DELIM); + if (p == NULL) p = NewName; + for (i = strlen(p)-1; i >= 0; i--) + { + if (isdigit(p[i])) + { + p[i] = '0'+n; + return; + } + } +} + +int main(int argc, char **argv) +{ + printf("Level 9 Interpreter\n\n"); + if (argc != 2) + { + printf("Syntax: %s \n",argv[0]); + return 0; + } + if (!LoadGame(argv[1])) + { + printf("Error: Unable to open game file\n"); + return 0; + } + while (RunGame()); + StopGame(); + FreeMemory(); + return 0; +} + diff --git a/level9.c b/level9.c new file mode 100755 index 0000000..8220243 --- /dev/null +++ b/level9.c @@ -0,0 +1,3001 @@ +#include +#include +#include +#include +#include + +#include "level9.h" + +/* #define _DEBUG */ +/* #define CODEFOLLOW */ +/* #define FULLSCAN */ + +/* The input routine will repond to the following 'hash' commands +#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) +*/ + +/* "L901" */ +#define L9_ID 0x4c393031 + +#define LISTAREASIZE 0x800 +#define STACKSIZE 1024 +#define IBUFFSIZE 500 + +#define V1FILESIZE 0x600 + +L9BYTE* startfile=NULL,*pictureaddress=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]; +enum L9GameTypes {L9_V1,L9_V2,L9_V3,L9_V4}; +int L9GameType; +enum V2MsgTypes {V2M_NORMAL,V2M_ERIK}; +int V2MsgType; +char LastGame[MAX_PATH]; + +#define RAMSAVESLOTS 10 +typedef struct +{ + L9UINT16 vartable[256]; + L9BYTE listarea[LISTAREASIZE]; +} SaveStruct; +typedef struct +{ + L9UINT32 Id; + L9UINT16 codeptr,stackptr,listsize,stacksize,filenamesize,checksum; + L9UINT16 vartable[256]; + L9BYTE listarea[LISTAREASIZE]; + L9UINT16 stack[STACKSIZE]; + char filename[MAX_PATH]; +} GameState; +GameState workspace; + +#if defined(AMIGA) && defined(_DCC) + __far SaveStruct ramsavearea[RAMSAVESLOTS]; +#else + SaveStruct ramsavearea[RAMSAVESLOTS]; +#endif + +L9UINT16 randomseed; +L9BOOL Running; + +char ibuff[IBUFFSIZE]; +char obuff[34]; + +L9BOOL Cheating=FALSE; +int CheatWord; +GameState CheatWorkspace; + +L9BOOL LoadGame2(char *filename); + +#ifdef CODEFOLLOW +#define CODEFOLLOWFILE "c:\\temp\\output" +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", + "Random", + "save", + "restore", + "clearworkspace", + "clearstack" +}; +char *drivercalls[]= +{ +"init", +"drivercalcchecksum", +"driveroswrch", +"driverosrdch", +"driverinputline", +"driversavefile", +"driverloadfile", +"settext", +"resettask", +"returntogem", +"10 *", +"loadgamedatafile", +"randomnumber", +"13 *", +"v4 driver14", +"15 *", +"driverclg", +"line", +"fill", +"driverchgcol", +"20 *", +"21 *", +"ramsave", +"ramload", +"24 *", +"lensdisplay", +"26 *", +"27 *", +"28 *", +"29 *", +"allocspace", +"31 *", +"v4 driver 32", +"33 *", +"v4 driver34" +}; +#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 getlongcode(void); + +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); +} + +char lastchar='.'; +char lastactualchar=0; + +void printchar(char c) +{ + if (Cheating) return; + + if (c&128) lastchar=(c&=0x7f); + else if (c!=0x20 && c!=0x0d && c!=0x7e && (c<'\"' || c>='.')) + { + if (lastchar=='!' || lastchar=='?' || lastchar=='.') c=toupper(c); + lastchar=c; + } +/* eat multiple CRs */ + if (c!=0x0d || lastactualchar!=0x0d) os_printchar(c); + lastactualchar=c; +} + +void printstring(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 error(char *fmt,...) +{ + char buf[256]; + va_list ap; + va_start(ap,fmt); + vsprintf(buf,fmt,ap); + va_end(ap); + printstring(buf); +} + +int d5; + +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=0x1b) return; + printautocase(getdictionary(d0)); + } + } + + else + { + if (d5&2) printchar(0x20); /* prespace */ + mdtmode=2; + Off&=0x7f; + if (L9GameType!=L9_V4 || 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 Msgptr+=getmdlength(&Msgptr); + 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 msglenV25(L9BYTE **ptr) +{ + L9BYTE *ptr2=*ptr; + while (ptr20) + { + a=*ptr++; + if (a<3) return; + + if (a>=0x5e) displaywordV25(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)++; + } + } +} + +L9BOOL amessageV25(L9BYTE *ptr,int msg,long *w,long *c) +{ + int n; + L9BYTE a; + static int depth=0; + + while (msg--) + { + ptr+=msglenV25(&ptr); + } + if (ptr >= startdata+FileSize) return FALSE; + n=msglenV25(&ptr); + + while (--n>0) + { + a=*ptr++; + if (a<3) return TRUE; + + if (a>=0x5e) + { + if (++depth>10 || !amessageV25(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 analyseV25(double *wl) +{ + long words=0,chars=0; + int i; + for (i=0;i<256;i++) + { + long w=0,c=0; + if (amessageV25(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 (V2MsgType==V2M_NORMAL) displaywordV2(startmd,Msg); + else displaywordV25(startmd,Msg); +} + +L9UINT32 filelength(FILE *f) +{ + L9UINT32 pos,FileSize; + + pos=ftell(f); + fseek(f,0,SEEK_END); + FileSize=ftell(f); + fseek(f,pos,SEEK_SET); + return FileSize; +} + +void Allocate(L9BYTE **ptr,L9UINT32 Size) +{ + if (*ptr) free(*ptr); + *ptr=malloc(Size); +} + +void FreeMemory(void) +{ + if (startfile) + { + free(startfile); + startfile=NULL; + } + if (pictureaddress) + { + free(pictureaddress); + pictureaddress=NULL; + } +} + +L9BOOL load(char *filename) +{ + FILE *f=fopen(filename,"rb"); + if (!f) return FALSE; + FileSize=filelength(f); + + Allocate(&startfile,FileSize); + + if (fread(startfile,1,FileSize,f)!=FileSize) + { + fclose(f); + return FALSE; + } + fclose(f); + 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)++]; + 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 ValidateSequence(L9BYTE* Base,L9BYTE* Image,L9UINT32 iPos,L9UINT32 acode,L9UINT32 *Size,L9UINT32 FileSize,L9UINT32 *Min,L9UINT32 *Max,L9BOOL Rts,L9BOOL *JumpKill) +{ + L9UINT32 Pos; + L9BOOL Finished=FALSE,Valid; + L9UINT32 Strange=0; + int ScanCodeMask; + int Code; + *JumpKill=FALSE; + + if (iPos>=FileSize) + 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,FileSize,Min,Max,TRUE /* Rts*/,JumpKill); + Finished=TRUE; + break; + } + case 1: /* intgosub */ + { + L9UINT32 Val=scangetaddr(Code,Base,&Pos,acode,&ScanCodeMask); + Valid=ValidateSequence(Base,Image,Val,acode,Size,FileSize,Min,Max,TRUE,JumpKill); + 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++; + case 1:/* calldriver */ + case 3:/* save */ + case 4:/* restore */ + case 5:/* clearworkspace */ + case 6:/* clear stack */ + break; + case 250: /* printstr */ + while (Base[Pos++]); + break; + + default: +#ifdef _DEBUG + /* 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 _DEBUG + /* 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,FileSize,Min,Max,Rts,JumpKill); + 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,FileSize,Min,Max,Rts,JumpKill); + break; + } + case 28:/* printinput */ + break; + case 12:/* ilins */ + case 13:/* ilins */ + case 29:/* ilins */ + case 30:/* ilins */ + case 31:/* ilins */ +#ifdef _DEBUG + /* printf("scan: illegal instruction"); */ +#endif + Valid=FALSE; + break; + } + if (Valid && (Code & ~ScanCodeMask)) + Strange++; + } while (Valid && !Finished && Pos=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); + free(Image); + return ret; +} +*/ + +long Scan(L9BYTE* StartFile,L9UINT32 FileSize) +{ + L9BYTE *Chk=malloc(FileSize+1); + L9BYTE *Image=calloc(FileSize,1); + L9UINT32 i,num,Size,MaxSize=0; + int j; + L9UINT16 d0,l9,md,ml,dd,dl; + L9UINT32 Min,Max; + long Offset=-1; + L9BOOL JumpKill; + + Chk[0]=0; + for (i=1;i<=FileSize;i++) + Chk[i]=Chk[i-1]+StartFile[i-1]; + + for (i=0;i0x2000 && i+num<=FileSize && 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<=FileSize && dd>0 && dl>0 && i+dd+dl*4<=FileSize) + { + /* 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>FileSize) 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; + if (ValidateSequence(StartFile,Image,i+d0,i+d0,&Size,FileSize,&Min,&Max,FALSE,&JumpKill)) + { +#ifdef _DEBUG + printf("Found valid header at %ld, code size %ld",i,Size); +#endif + if (Size>MaxSize) + { + Offset=i; + MaxSize=Size; + } + /* break; */ + } + } + } + } + free(Chk); + free(Image); + return Offset; +} + +long ScanV2(L9BYTE* StartFile,L9UINT32 FileSize) +{ + L9BYTE *Chk=malloc(FileSize+1); + L9BYTE *Image=calloc(FileSize,1); + L9UINT32 i,Size,MaxSize=0,num; + int j; + L9UINT16 d0,l9; + L9UINT32 Min,Max; + long Offset=-1; + L9BOOL JumpKill; + + Chk[0]=0; + for (i=1;i<=FileSize;i++) + Chk[i]=Chk[i-1]+StartFile[i-1]; + + for (i=0;i=0x8000 && d0<0x9000) + { + if (d0>=0x8000+LISTAREASIZE) break; + } + else if (i+d0>FileSize) 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,FileSize,&Min,&Max,FALSE,&JumpKill)) + { +#ifdef _DEBUG + printf("Found valid V2 header at %ld, code size %ld",i,Size); +#endif + if (Size>MaxSize) + { + Offset=i; + MaxSize=Size; + } + } + } + } + free(Chk); + free(Image); + return Offset; +} + + +long ScanV1(L9BYTE* StartFile,L9UINT32 FileSize) +{ + return -1; + +/* + L9BYTE *Image=calloc(FileSize,1); + L9UINT32 i,Size; + int Replace; + L9BYTE* ImagePtr; + long MaxPos=-1; + L9UINT32 MaxCount=0; + L9UINT32 Min,Max,MaxMin,MaxMax; + L9BOOL JumpKill,MaxJK; + + L9BYTE c; + int maxdict,maxdictlen; + L9BYTE *ptr,*start; + + for (i=0;iMaxCount) + { + 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 _DEBUG + printf("V1scan found code at %ld size %ld",MaxPos,MaxCount); +#endif + + // find dictionary + ptr=StartFile; + maxdictlen=0; + do + { + start=ptr; + do + { + do + { + c=*ptr++; + } while (((c>='A' && c<='Z') || c=='-') && ptr'Z') && (c&0x7f)!='/')) break; + ptr++; + } while (TRUE); + if (ptr-start-1>maxdictlen) + { + maxdict=start-StartFile; + maxdictlen=ptr-start-1; + } + } while (ptr0) printf("V1scan found dictionary at %ld size %ld",maxdict,maxdictlen); +#endif + + MaxPos=-1; + + free(Image); + return MaxPos; +*/ +} + +#ifdef FULLSCAN +void FullScan(L9BYTE* StartFile,L9UINT32 FileSize) +{ + L9BYTE *Image=calloc(FileSize,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;iMaxCount) + { + 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=0x8000 && d0<=0x9000) ? workspace.listarea+d0-0x8000 : startdata+d0; + } + absdatablock=L9Pointers[0]; + list2ptr=L9Pointers[3]; + list3ptr=L9Pointers[4]; + /*list9startptr */ + /* if ((((L9UINT32) L9Pointers[10])&1)==0) L9Pointers[10]++; amiga word access hack */ + list9startptr=L9Pointers[10]; + acodeptr=L9Pointers[11]; + } + + + switch (L9GameType) + { + case L9_V1: + break; + case L9_V2: + { + double a2,a25; + startmd=startdata + L9WORD(startdata+0x0); + startmdV2=startdata + L9WORD(startdata+0x2); + + /* determine message type */ + if (analyseV2(&a2) && a2>2 && a2<10) + { + V2MsgType=V2M_NORMAL; + #ifdef _DEBUG + printf("V2 msg table: normal, wordlen=%.2lf",a2); + #endif + } + else if (analyseV25(&a25) && a25>2 && a25<10) + { + V2MsgType=V2M_ERIK; + #ifdef _DEBUG + printf("V2 msg table: Erik, wordlen=%.2lf",a25); + #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; + } + return TRUE; +} + +L9BOOL checksumgamedata(void) +{ + return calcchecksum(startdata,L9WORD(startdata)+1)==0; +} + +/* instruction codes */ + +L9BYTE* codeptr; +L9BYTE code; + +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) +{ + codeptr=getaddr(); +} + +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 _DEBUG + printf("driver - init"); +#endif +/* Running=FALSE; */ +} +void randomnumber(L9BYTE* a6) +{ +#ifdef _DEBUG + printf("driver - randomnumber"); +#endif +/* Running=FALSE; */ + L9SETWORD(a6,rand()); +} +void driverclg(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - driverclg"); +#endif +/* Running=FALSE; */ +} +void line(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - line"); +#endif +/* Running=FALSE; */ +} +void fill(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - fill"); +#endif +/* Running=FALSE; */ +} +void driverchgcol(L9BYTE* a6) +{ +#ifdef _DEBUG + printf("driver - driverchgcol %d %d", a6[1], a6[0]); +#endif +} +void drivercalcchecksum(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - calcchecksum"); +#endif +/* Running=FALSE; */ +} +void driveroswrch(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - driveroswrch"); +#endif +/* Running=FALSE; */ +} +void driverosrdch(L9BYTE* a6) +{ +#ifdef _DEBUG + printf("driver - driverosrdch"); +#endif + os_flush(); + *a6=os_readchar(); +} +void driversavefile(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - driversavefile"); +#endif +/* Running=FALSE; */ +} +void driverloadfile(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - driverloadfile"); +#endif +/* Running=FALSE; */ +} +void settext(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - settext"); +#endif +/* Running=FALSE; */ +} +void resettask(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - resettask"); +#endif +/* Running=FALSE; */ +} +void driverinputline(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - driverinputline"); +#endif +/* Running=FALSE; */ +} +void returntogem(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - returntogem"); +#endif +/* Running=FALSE; */ +} +void lensdisplay(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - lensdisplay"); +#endif +/* Running=FALSE; */ +} +void allocspace(L9BYTE*a6) +{ +#ifdef _DEBUG + printf("driver - allocspace"); +#endif +/* Running=FALSE; */ +} + +/* v4 */ +void driver14(L9BYTE *a6) +{ +#ifdef _DEBUG + printf("driver - v4 call 14"); +#endif + L9GameType=L9_V4; + *a6=0; +} +void driver32(L9BYTE *a6) +{ +#ifdef _DEBUG + printf("driver - v4 call 32"); +#endif + L9GameType=L9_V4; +} +void driver34(L9BYTE *a6) +{ +#ifdef _DEBUG + printf("driver - v4 call 34"); +#endif + L9GameType=L9_V4; + *a6=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: driver32(a6); break; + case 0x22: driver34(a6); break; + + } +} + +void ramsave(int i) +{ +#ifdef _DEBUG + printf("driver - ramsave %d",i); +#endif + memmove(ramsavearea+i,workspace.vartable,sizeof(SaveStruct)); +} + +void ramload(int i) +{ +#ifdef _DEBUG + 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); + } + else driver(d0,a6); +} + +void Random(void) +{ + randomseed=(((randomseed<<8) + 0x0a - randomseed) <<2) + randomseed + 1; + *getvar()=randomseed & 0xff; +} + +void save(void) +{ + L9UINT16 checksum; + int i; +#ifdef _DEBUG + 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;iId!=L9_ID) return FALSE; + checksum=gs->checksum; + gs->checksum=0; + for (i=0;ifilename,LastGame)) + { + printstring("\rWarning: game filename does not match, you may have loaded this position file into the wrong story file.\r"); + } + return TRUE; +} + +void NormalRestore(void) +{ + GameState temp; + int Bytes; +#ifdef _DEBUG + 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 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: calldriver(); break; + case 2: Random(); 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); + } +} + +L9BYTE* list9ptr; + +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 a2+=getmdlength(&a2); + } while (TRUE); +} + +int unpackd3; + +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); +} + +L9BYTE* ibuffptr; + +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); + } +} + +L9BOOL GetWordV2(char *buff,int Word); +L9BOOL GetWordV3(char *buff,int Word); + +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 (stricmp(ibuff,"#cheat")==0) StartCheat(); + else if (stricmp(ibuff,"#restore")==0) + { + restore(); + return TRUE; + } + else if (stricmp(ibuff,"#quit")==0) + { + StopGame(); + printstring("\rGame Terminated\r"); + return TRUE; + } + else if (stricmp(ibuff,"#dictionary")==0) + { + CheatWord=0; + printstring("\r"); + while ((L9GameType==L9_V2) ? GetWordV2(ibuff,CheatWord++) : GetWordV3(ibuff,CheatWord++)) + { + error("%s ",ibuff); + if (os_readchar() || !Running) break; + } + printstring("\r"); + return TRUE; + } + return FALSE; +} + +L9BOOL corruptinginput(void) +{ + L9BYTE *a0,*a2,*a6; + int d0,d1,d2,keywordnumber,abrevword; + + list9ptr=list9startptr; + + if (ibuffptr==NULL) + { + if (Cheating) NextCheat(); + else + { + /* flush */ + os_flush(); + lastchar='.'; + /* get input */ + if (!os_input(ibuff,IBUFFSIZE)) return FALSE; /* fall through */ + if (CheckHash()) return FALSE; + + /* 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,d0); + L9SETWORD(list9ptr+2,0); + *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 GetWordV2(char *buff,int Word) +{ + L9BYTE *ptr=L9Pointers[1],x; + + while (Word--) + { + do + { + x=*ptr++; + } while (x>0 && x<0x7f); + if (x==0) return FALSE; /* no more words */ + ptr++; + } + do + { + x=*ptr++; + *buff++=x&0x7f; + } while (x>0 && x<0x7f); + *buff=0; + return TRUE; +} + +L9BOOL inputV2(int *wordcount) +{ + L9BYTE a,x; + L9BYTE *ibuffptr,*obuffptr,*ptr,*list0ptr; + + if (Cheating) NextCheat(); + else + { + os_flush(); + lastchar='.'; + /* get input */ + if (!os_input(ibuff,IBUFFSIZE)) return FALSE; /* fall through */ + if (CheckHash()) return FALSE; + + /* force CR but prevent others */ + os_printchar(lastactualchar='\r'); + } + /* add space onto end */ + ibuffptr=(L9BYTE*) strchr(ibuff,0); + *ibuffptr++=32; + *ibuffptr=0; + + *wordcount=0; + ibuffptr=(L9BYTE*) ibuff; + obuffptr=(L9BYTE*) obuff; + /* ibuffptr=76,77 */ + /* obuffptr=84,85 */ + /* list0ptr=7c,7d */ + list0ptr=L9Pointers[1]; + + while (*ibuffptr==32) ++ibuffptr; + + ptr=ibuffptr; + do + { + while (*ptr==32) ++ptr; + if (*ptr==0) break; + (*wordcount)++; + do + { + a=*++ptr; + } while (a!=32 && a!=0); + } while (*ptr>0); + + while (TRUE) + { + ptr=ibuffptr; /* 7a,7b */ + while (*ibuffptr==32) ++ibuffptr; + + while (TRUE) + { + a=*ibuffptr; + x=*list0ptr++; + + if (a==32) break; + if (a==0) + { + *obuffptr++=0; + return TRUE; + } + + ++ibuffptr; + if (tolower(x&0x7f) != tolower(a)) + { + while (x>0 && x<0x7f) x=*list0ptr++; + if (x==0) + { + do + { + a=*ibuffptr++; + if (a==0) + { + *obuffptr=0; + return TRUE; + } + } while (a!=32); + while (*ibuffptr==32) ++ibuffptr; + list0ptr=L9Pointers[1]; + ptr=ibuffptr; + } + else + { + list0ptr++; + ibuffptr=ptr; + } + } + else if (x>=0x7f) break; + } + + a=*ibuffptr; + if (a!=32) + { + ibuffptr=ptr; + list0ptr+=2; + continue; + } + --list0ptr; + while (*list0ptr++<0x7e); + *obuffptr++=*list0ptr; + while (*ibuffptr==32) ++ibuffptr; + list0ptr=L9Pointers[1]; + } +} + +void input(void) +{ + /* 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); +} + +L9BYTE exitreversaltable[16]= {0x00,0x04,0x06,0x07,0x01,0x08,0x02,0x03,0x05,0x0a,0x09,0x0c,0x0b,0xff,0xff,0x0f}; + +/* bug */ +void exit1(L9BYTE *d4,L9BYTE *d5,L9BYTE d6,L9BYTE d7) +{ + L9BYTE* a0=absdatablock; + L9BYTE d1=d7,d0; + if (--d1) + { + do + { + d0=*a0; + a0+=2; + } + while ((d0&0x80)==0 || --d1); + } + + do + { + *d4=*a0++; + if (((*d4)&0xf)==d6) + { + *d5=*a0; + return; + } + a0++; + } + while (((*d4)&0x80)==0); + + /* notfn4 */ + d6=exitreversaltable[d6]; + a0=absdatablock; + *d5=1; + + do + { + *d4=*a0++; + if (((*d4)&0x10)==0 || ((*d4)&0xf)!=d6) a0++; + else if (*a0++==d7) return; + /* exit6noinc */ + if ((*d4)&0x80) (*d5)++; + } while (*d4); + *d5=0; +} + +void Exit(void) +{ + L9BYTE d4,d5; + L9BYTE d7=(L9BYTE) *getvar(); + L9BYTE d6=(L9BYTE) *getvar(); + exit1(&d4,&d5,d6,d7); + + *getvar()=(d4&0x70)>>4; + *getvar()=d5; +} + +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 (d0d1) 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 screen(void) +{ + int textmode=*codeptr++; + if (textmode==0) + { + /* stopgint */ + /* set textmode */ +#ifdef _DEBUG + printf("textmode"); +#endif + } + else + { + codeptr++; +/* stopgint */ +/* gintclearg */ +#ifdef _DEBUG + printf("graphmode"); +#endif + } +} + +void cleartg(void) +{ + int textmode=*codeptr++; + if (textmode==0) + { +#ifdef _DEBUG + printf("clear text screen"); +#endif + } + else + { +/* stopgint */ +/* gintclearg */ +#ifdef _DEBUG + printf("clear graph screen"); +#endif + } +} + +L9BOOL findsub(int d0,L9BYTE** a5) +{ + int d1,d2,d3,d4; + + d1=d0 << 4; + d2=d1 >> 8; + *a5=pictureaddress; +/*findsubloop */ + while (TRUE) + { + d3=*(*a5)++; + if (d3&0x80) return FALSE; + if (d2==d3) + { + if (d1==(*(*a5) & 0xf0)) + { + (*a5)+=2; + return TRUE; + } + } + d3=*(*a5)++ & 0xf0; + d4=**a5; + if ((d3|d4)==0) return 0xff; + + (*a5)+=(d3<<8) + d4 - 2; + } +} + +void getxy1(int d7,int *d5,int *d6) +{ + *d5=(char) (d7<<2)>>5; + *d6=(char) (d7<<5)>>3; +/* if (reflectflag & 2) *d5=-*d5; */ +/* if (reflectflag & 1) *d6=-*d6; */ +} + +void newxy(int x,int y) +{ +/* a2+=(x*scale)&~7; */ +/* a3+=(y*scale)&~7; */ +} + +void sdraw(int d7) +{ + int x,y; + getxy1(d7,&x,&y); + + newxy(x,y); +/* driver(0x11); */ +} +void smove(void) +{ +} +void sgosub(void) +{ +} +void draw(void) +{ +} +void _move(void) +{ +} +void icolour(void) +{ +} +void size(void) +{ +} +void gintfill(void) +{ +} +void gosub(void) +{ +} +void reflect(void) +{ +} + +void notimp(void) +{ +} +void gintchgcol(void) +{ +} +void amove(void) +{ +} +void opt(void) +{ +} +void restorescale(void) +{ +} + +L9BOOL getinstruction(int d0) +{ + if ((d0&0xc0)!=0xc0) + switch ((d0>>6)&3) + { + case 0: sdraw(d0); break; + case 1: smove(); break; + case 2: sgosub(); break; + } + else if ((d0&0x38)!=0x38) + switch ((d0>>3)&7) + { + case 0: draw(); break; + case 1: _move(); break; + case 2: icolour(); break; + case 3: size(); break; + case 4: gintfill(); break; + case 5: gosub(); break; + case 6: reflect(); break; + } + else + switch (d0&7) + { + case 0: notimp(); break; + case 1: gintchgcol(); break; + case 2: notimp(); break; + case 3: amove(); break; + case 4: opt(); break; + case 5: restorescale(); break; + case 6: notimp(); break; + case 7: return FALSE; + } + return TRUE; +} + +void absrunsub(int d0) +{ + L9BYTE* a5; + if (!findsub(d0,&a5)) return; + while (getinstruction(*a5++)); +} + +void picture(void) +{ + int Pic=*getvar(); +#ifdef _DEBUG + printf("picture %d",Pic); +#endif +/* stopgint */ +/* resettask */ +/* driverclg */ + +/* gintinit */ +/* gintcolour=3; */ +/* option=0x80; */ +/* reflectflag=0; */ +/* sizereset(); */ +/* a4=gintstackbase; */ + +/* absrunsub(0); */ +/* absrunsub(Pic); */ +} + +L9UINT16 gnostack[128]; +L9BYTE gnoscratch[32]; +int object,gnosp,numobjectfound,searchdepth,inithisearchpos; + +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 _DEBUG + 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 (d0d1) 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 _DEBUG + 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=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=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=MinAccess && a4=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=MinAccess && a4=MinAccess && a4>8); + #define L9SETDWORD(x,val) *(x)=(L9BYTE)val; *(x+1)=(L9BYTE)(val>>8); *(x+2)=(L9BYTE)(val>>16); *(x+3)=(L9BYTE)(val>>24); + #define FILE_DELIM '/' +#endif + +#if defined(_Windows) && !defined(__WIN32__) +#include +#define malloc farmalloc +#define calloc farcalloc +#define free farfree +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* routines provided by os dependent code */ +void os_printchar(char c); +L9BOOL os_input(char*ibuff,int size); +char os_readchar(void); +void os_flush(void); +L9BOOL os_save_file(L9BYTE *Ptr,int Bytes); +L9BOOL os_load_file(L9BYTE *Ptr,int *Bytes,int Max); +L9BOOL os_get_game_file(char *NewName,int Size); +void os_set_filenumber(char *NewName,int Size,int n); + +/* routines provided by level9 interpreter */ +L9BOOL LoadGame(char *filename); +L9BOOL RunGame(void); +void StopGame(void); +void FreeMemory(void); + +#ifdef __cplusplus +} +#endif + diff --git a/level9.txt b/level9.txt new file mode 100755 index 0000000..219a8ae --- /dev/null +++ b/level9.txt @@ -0,0 +1,164 @@ + + Level 9 Interpreter v2.0 + An interpreter for Level 9 games in any format, + including Spectrum snapshots. + + Written by Glen Summers + + +Introduction +------------ + +During the 1980s a small British company called Level 9, run by two brothers +(Mike and Pete Austin), produced a series of text adventure games for a +variety of computers. These games received considerable critical acclaim and +are probably the best text adventures written for the small cassette based 8 +bit computers common in Britain in the 80s. + +Level 9 wrote their games using a custom designed system known as "A-Code", +which evolved from games on the 8 bit computers such as the Acorn BBC Model +B, the Sinclair Spectrum and the Commodore 64 to the (then new) 16 bit +machines such as the Amiga and the Atari ST. + +From disassembly of Level 9 games there are thought to be four variants of +A-Code, which are detailed below. Thanks go to Paul David Doherty for +analysing the games and producing the table which follows. At present this +interpreter supports v2, v3 and v4 games. + + v1 This was used for the earliest games. Spectrum v1 games had + black text on a grey background. Games known to be released in + this format: + + Colossal Adventure Adventure Quest + Dungeon Adventure Snowball + Lords of Time + + v2 These releases were made between 1984 and 1985 (and usually say + so in the startup copyright message. This version introduced + the yellow text on a black background which became standard. + Games in this format were: + + Return to Eden Lords of Time + Red Moon Erik the Viking + Emerald Isle + + v3 This format, dated 1986, was used by the largest number of + releases. These were: + + Worm in Paradise The Price of Magick + The Growing Pains of Adrian Mole + The Jewels of Darkness Trilogy (Colossal Adventure, + Adventure Quest and Dungeon Adventure released as + one package) + The Silicon Dreams Trilogy + (Snowball, Return to Eden and Worm in Paradise) + + v4 This was used for the Time and Magick Trilogy (Lords of Time, + Red Moon and The Price of Magick), and all Level 9's new games + from 1987 onwards: + + Lancelot Knight Orc + Gnome Ranger Ingrid's Back + Scapeghost + + +Supported Formats +----------------- + +On several machines (such as the Amiga) Level 9 games were distributed as +an interpreter plus a datafile, usually called "gamedata.dat" or something +similar. These games can be played with this interpreter simply by loading +the "gamedata.dat" file. + +For the Amiga (and possibly some other formats) the v4 games were released +in three parts, each in a separate data file: + + gamedat1.dat + gamedat2.dat + gamedat3.dat + +Starting the first game gives a menu from which you can choose which part or +game to play. For this to work the files must have the same basic structure +with a number in it somewhere, e.g. file names + + TimeAndMagick1.dat + TimeAndMagick2.dat + TimeAndMagick3.dat + +will work. + +On other (especially older and smaller) computers the games were distributed +as a single file containing both an interpreter and the game data. Level9 +can cope with these files as well, as it automatically searches files for +valid Level 9 games. This however requires that the file not be compressed +in any way. For example, there are several Spectrum snapshots of Level 9 +games available which this interpreter can play, but these snapshots must +be in an uncompressed format (e.g. SNA). Commonly snapshots are available in +the compressed Z80 format, but these files can be converted to SNA using the +widely available conversion program "SPConv". Version 1.06 or higher of +"SPConv" is recommended. + +This program has been tested on files obtained from Amigas, Spectrums, BBCs, +Commodore 64s, Ataris and IBM PC compatibles. + + +Meta Commands +------------- + +Level9 supports several meta commands, which can be entered on the input +line. These commands are handled by the interpreter rather than being passed +to the game. They are: + + #restore Loads in a saved position directly, bypassing any + protection code withing the game. + + #quit Quits the current game. + + #cheat Tries to bypass the copy protection code which asks for + a specific word. This is done by trying every word in + the game's dictionary. On a slow machine, this can take + a long time. + + #dictionary Lists the game dictionary. Press a key to stop the + listing and return to the input line. Note that the v2 + dictionary appears to have random characters following + on the end: The original interpreter code to detect the + end of dictionary does not appear to agree with the + characters actually at the end. + + +History +------- + + v2.0 Revised the description of v4 games to include all the post-1987 + games, which are now supported. + v2 games are also now supported. + Added meta commands. + + v1.0 First release. + + +Credits +------- + +The Level9 Interpreter was written by Glen Summers, who can be contacted +on email at + + gsummers@physics.ox.ac.uk + +This documentation was written by David Kinder, who can be reached at + + kinder@teaching.physics.ox.ac.uk +or david.kinder@physics.ox.ac.uk + +Help, testing and information on the various Level 9 formats was provided by +Paul David Doherty. + + +The Interactive Fiction Archive +------------------------------- + +If you have access to the Internet and are interested in text adventures, +then you can find all sorts of programs and information at The Interactive +Fiction Archive, at the ftp site ftp.gmd.de, in the /if-archive directory. +The latest version of this program can always be found here. diff --git a/os/amiga.c b/os/amiga.c new file mode 100755 index 0000000..87bfb9d --- /dev/null +++ b/os/amiga.c @@ -0,0 +1,1040 @@ +/* Amiga routines for Level 9 interpreter */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "level9.h" + +#define QUALIFIER_SHIFT (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +struct NewMenu NewMenus[] = +{ + {NM_TITLE, "Project", 0, 0, 0, 0}, + {NM_ITEM, "New Game...", "N", 0, 0, 0}, + {NM_ITEM, NM_BARLABEL, 0, 0, 0, 0}, + {NM_ITEM, "Save...", "S", 0, 0, 0}, + {NM_ITEM, "Restore...", "R", 0, 0, 0}, + {NM_ITEM, NM_BARLABEL, 0, 0, 0, 0}, + {NM_ITEM, "Help...", "H", 0, 0, 0}, + {NM_ITEM, "About...", "?", 0, 0, 0}, + {NM_ITEM, NM_BARLABEL, 0, 0, 0, 0}, + {NM_ITEM, "Quit", "Q", 0, 0, 0}, + {NM_END, 0, 0, 0, 0, 0}}; + +char Version[] = "$VER:Level9 2.0 (13.10.96)"; +char TitleBar[] = "Level 9"; + +#define TEXTBUFFER_SIZE 1024 +char TextBuffer[TEXTBUFFER_SIZE+1]; +int TextBufferPtr; + +#define HISTORY_LINES 20 +unsigned char *History[HISTORY_LINES]; +int HistoryPosition = HISTORY_LINES; + +extern struct Library *IntuitionBase; +struct Library *AmigaGuideBase; +struct Screen *Screen, *DefaultPubScreen; +struct Window *Window, *OldWindowPtr; +struct RastPort *RastPort; +struct Menu *Menus; +struct DiskObject *Icon; +struct TextFont *Font; +struct FileRequester *GameReq, *SaveReq; +struct Process *ThisProcess; +APTR Visual; + +int ScreenWidth, ScreenHeight; +int DisplayHeight, PreviousHeight; +int MoreCount; +int Playing; + +void amiga_init (char *dir); +void screen_ratio (struct Screen *screen); +void reset_cursor (void); +void rect (long xMin, long yMin, long xMax, long yMax, unsigned long pen); +void text (char *s, int n); +int check_len (char *s, int n); +void cursor (int under); +void set_window (int limit, int x); +int get_key (UWORD * qualifier_addr, int c, int wait); +void redraw_line (unsigned char *buffer, int *pos, int max_size); +void move_text (int offset, int max); +void cursor_left (unsigned char *buffer, int *pos); +void cursor_right (unsigned char *buffer, int *pos); +int fit_text (unsigned char *pointer, int new); +void into_buffer (unsigned char *buffer, unsigned char *new, int *pos, int max_size); +void store_hist (unsigned char *line); +int cmp (const char *a, const char *b); +void help (void); +void about (void); +LONG req (UBYTE * text, UBYTE * gadgets,...); +void busy (int busy); +struct FileRequester *alloc_freq (char *initdir); +void filereq (struct FileRequester *freq, char *buffer, char *title, ULONG save); +int newgame (char *fname); + +void os_printchar (char c) +{ + if (Window == 0) + { + if (c == '\r') + c = '\n'; + fputc (c, stdout); + return; + } + + if (c == '\r') + { + os_flush (); + ClipBlit (RastPort, + Window->BorderLeft, + Window->BorderTop + RastPort->TxHeight, + RastPort, + Window->BorderLeft, + Window->BorderTop, + Window->Width - Window->BorderLeft - Window->BorderRight, + DisplayHeight * RastPort->TxHeight, 0xC0); + rect (Window->BorderLeft, + Window->BorderTop + DisplayHeight * RastPort->TxHeight, + Window->Width - Window->BorderRight - 1, + Window->BorderTop + (DisplayHeight + 1) * RastPort->TxHeight - 1, 0); + reset_cursor (); + + if (++MoreCount >= DisplayHeight) + { + MoreCount = 0; + text ("[More]", 6); + cursor (0); + get_key (0, -1, 1); + cursor (0); + rect (Window->BorderLeft, + Window->BorderTop + DisplayHeight * RastPort->TxHeight, + Window->Width - Window->BorderRight - 1, + Window->BorderTop + (DisplayHeight + 1) * RastPort->TxHeight - 1, 0); + reset_cursor (); + } + + return; + } + + if (isprint(c) == 0) return; + if (TextBufferPtr >= TEXTBUFFER_SIZE) + os_flush (); + *(TextBuffer + (TextBufferPtr++)) = c; +} + +L9BOOL os_input (char *ibuff, int size) +{ + int c, pos = 0; + int inputting = 1; + unsigned long saved_pen; + UWORD qualifier; + + *ibuff = '\0'; + cursor (*ibuff); + saved_pen = RastPort->FgPen; + SetAPen (RastPort, 2); + + while (inputting) + { + set_window (1, TextLength (RastPort, ibuff + pos, strlen (ibuff + pos))); + c = get_key (&qualifier, *(ibuff + pos), 1); + set_window (0, 0); + + switch (c) + { + case -1: + cursor (*(ibuff + pos)); + SetAPen (RastPort, 1); + MoreCount = 0; + return 0; + + case -2: + cursor (*(ibuff + pos)); + cursor_right (ibuff, &pos); + strcpy(ibuff,"save"); + SetAPen (RastPort, 1); + MoreCount = 0; + return 1; + + case -3: + cursor (*(ibuff + pos)); + cursor_right (ibuff, &pos); + strcpy(ibuff,"#restore"); + SetAPen (RastPort, 1); + MoreCount = 0; + return 1; + + case 13: + if (strlen (ibuff) > 0) + { + cursor (*(ibuff + pos)); + cursor_right (ibuff, &pos); + SetAPen (RastPort, 1); + store_hist (ibuff); + MoreCount = 0; + return 1; + } + break; + + case 264: + redraw_line (ibuff, &pos, size); + break; + + case 127: + if (pos > 0) + { + int deleted; + + cursor (*(ibuff + pos)); + deleted = *(ibuff + pos - 1); + memmove (ibuff + pos - 1, ibuff + pos, strlen (ibuff) - pos + 1); + move_text (-char_len (deleted), Window->Width - Window->BorderRight); + Move (RastPort, RastPort->cp_x - char_len (deleted), RastPort->cp_y); + pos--; + cursor (*(ibuff + pos)); + } + break; + + case 260: + if (pos < strlen (ibuff)) + { + int deleted; + + cursor (*(ibuff + pos)); + deleted = *(ibuff + pos); + memmove (ibuff + pos, ibuff + pos + 1, strlen (ibuff) - pos); + Move (RastPort, RastPort->cp_x + char_len (deleted), RastPort->cp_y); + move_text (-char_len (deleted), Window->Width - Window->BorderRight); + Move (RastPort, RastPort->cp_x - char_len (deleted), RastPort->cp_y); + cursor (*(ibuff + pos)); + } + break; + + case 131: + if (pos > 0) + { + if (qualifier & QUALIFIER_SHIFT) + { + cursor (*(ibuff + pos)); + cursor_left (ibuff, &pos); + cursor (*(ibuff + pos)); + } + else + { + cursor (*(ibuff + pos--)); + Move (RastPort, RastPort->cp_x - char_len (*(ibuff + pos)), RastPort->cp_y); + cursor (*(ibuff + pos)); + } + } + break; + + case 132: + if (pos < strlen (ibuff)) + { + if (qualifier & QUALIFIER_SHIFT) + { + cursor (*(ibuff + pos)); + cursor_right (ibuff, &pos); + cursor (*(ibuff + pos)); + } + else + { + cursor (*(ibuff + pos)); + Move (RastPort, RastPort->cp_x + char_len (*(ibuff + pos)), RastPort->cp_y); + pos++; + cursor (*(ibuff + pos)); + } + } + break; + + case 129: + if (qualifier & QUALIFIER_SHIFT) + { + HistoryPosition = 0; + while ((*(History + HistoryPosition) == 0) && (HistoryPosition < HISTORY_LINES)) + HistoryPosition++; + into_buffer (ibuff, HistoryPosition == HISTORY_LINES ? + (unsigned char *) "" : *(History + HistoryPosition), &pos, size); + } + else + { + if ((HistoryPosition > 0) && (*(History + HistoryPosition - 1) != 0)) + { + HistoryPosition--; + into_buffer (ibuff, *(History + HistoryPosition), &pos, size); + } + } + break; + + case 130: + if (qualifier & QUALIFIER_SHIFT) + { + HistoryPosition = HISTORY_LINES; + into_buffer (ibuff, "", &pos, size); + } + else + { + if ((HistoryPosition < HISTORY_LINES - 1) && (*(History + HistoryPosition + 1) != 0)) + { + HistoryPosition++; + into_buffer (ibuff, HistoryPosition == HISTORY_LINES ? + (unsigned char *) "" : *(History + HistoryPosition), &pos, size); + } + else + { + HistoryPosition = HISTORY_LINES; + into_buffer (ibuff, "", &pos, size); + } + } + break; + + default: + if (c >= 32 && c <= 127) + { + if ((strlen (ibuff) < size) && (fit_text (ibuff + pos, c))) + { + memmove (ibuff + pos + 1, ibuff + pos, strlen (ibuff) - pos + 1); + *(ibuff + pos) = c; + + pos++; + cursor (*(ibuff + pos)); + move_text (char_len (c), Window->Width - Window->BorderRight); + os_printchar (c); + cursor (*(ibuff + pos)); + } + } + break; + } + } +} + +char os_readchar (void) +{ + MoreCount = 0; + return get_key (0, -1, 0); +} + +void os_flush (void) +{ + static int semaphore = 0; + + if (Window == 0) + return; + if (TextBufferPtr < 1) + return; + + if (semaphore) + return; + semaphore = 1; + + char *ptr, *space, *lastspace; + int searching; + + *(TextBuffer+TextBufferPtr) = ' '; + ptr = TextBuffer; + while (check_len (ptr, TextBufferPtr)) + { + space = ptr; + lastspace = space; + searching = 1; + while (searching) + { + while (*space != ' ') + space++; + if (check_len (ptr, space - ptr)) + { + space = lastspace; + text (ptr, space - ptr); + os_printchar ('\r'); + if (*space == ' ') space++; + TextBufferPtr -= space - ptr; + ptr = space; + searching = 0; + } + else + lastspace = space; + space++; + } + } + text (ptr, TextBufferPtr); + TextBufferPtr = 0; + + semaphore = 0; +} + +L9BOOL os_save_file (L9BYTE * Ptr, int Bytes) +{ + char filename[256]; + FILE *f; + + filereq (SaveReq, filename, "Save Game", 1); + if (strcmp (filename, "") == 0) + return FALSE; + + if (f = fopen (filename, "w")) + { + fwrite (Ptr, 1, Bytes, f); + fclose (f); + return TRUE; + } + return FALSE; +} + +L9BOOL os_load_file(L9BYTE *Ptr,int *Bytes,int Max) +{ + char filename[256]; + FILE *f; + + filereq (SaveReq, filename, "Restore Game", 0); + if (strcmp (filename, "") == 0) + return FALSE; + + if (f = fopen (filename, "r")) + { + *Bytes = fread (Ptr, 1, Max, f); + fclose (f); + return TRUE; + } + return FALSE; +} + +L9BOOL os_get_game_file(char *NewName,int Size) +{ + filereq (GameReq, NewName, "Next Level9 Game File", 0); +} + +void os_set_filenumber(char *NewName,int Size,int n) +{ +char *file; +int i; + + file = FilePart(NewName); + for (i = strlen(file)-1; i >= 0; i--) + { + if (isdigit(*(file+i))) + { + *(file+i) = '0'+n; + return; + } + } +} + +int main (int argc, char **argv) +{ + amiga_init (""); + newgame (argc > 1 ? argv[1] : 0); + exit (0); +} + +wbmain (struct WBStartup *wbmsg) +{ + char startdir[256]; + char *dir; + + strcpy (startdir, ""); + if (Icon = GetDiskObject (wbmsg->sm_ArgList[0].wa_Name)) + if (dir = FindToolType (Icon->do_ToolTypes, "DIR")) + strcpy (startdir, dir); + + amiga_init (startdir); + newgame (0); + exit (0); +} + +void amiga_init (char *dir) +{ + if ((DefaultPubScreen = LockPubScreen (0)) == 0) + exit (1); + screen_ratio (DefaultPubScreen); + + char prog_name[256]; + static char font_name[MAXFONTPATH]; + char *font_desc, *name_ptr; + static WORD pens[] = + {-1}; + static struct TextAttr font = + {NULL, 0, FS_NORMAL, 0}; + int window = 0; + + if (Icon == NULL) + { + if (GetProgramName (prog_name, 256)) + Icon = GetDiskObject (prog_name); + } + + if (Icon) + { + if (FindToolType (Icon->do_ToolTypes, "WINDOW")) + window = 1; + if (font_desc = FindToolType (Icon->do_ToolTypes, "FONT")) + { + strcpy (font_name, font_desc); + if (name_ptr = strrchr (font_name, '/')) + { + font.ta_Name = font_name; + font.ta_YSize = atoi (name_ptr + 1); + strcpy (name_ptr, ".font"); + } + } + if (font.ta_Name) + Font = OpenDiskFont (&font); + } + if (Font == NULL) + { + font.ta_Name = "topaz.font"; + font.ta_YSize = 8; + Font = OpenFont (&font); + } + + if (window == 0) + { + if ((Screen = OpenScreenTags (0, + SA_Pens, pens, + SA_DisplayID, GetVPModeID (&DefaultPubScreen->ViewPort), + SA_Overscan, OSCAN_TEXT, + SA_Depth, 2, + SA_Type, CUSTOMSCREEN | AUTOSCROLL, + SA_Font, &font, + SA_Title, TitleBar, TAG_DONE)) == 0) + exit (1); + } + + if ((Window = OpenWindowTags (0, + WA_Left, 0, + WA_Top, Screen ? 2 : DefaultPubScreen->BarHeight + 1, + WA_Width, Screen ? Screen->Width : ScreenWidth, + WA_Height, Screen ? Screen->Height - 2 : ScreenHeight - DefaultPubScreen->BarHeight - 1, + WA_SmartRefresh, 1, + WA_NewLookMenus, 1, + WA_AutoAdjust, 1, + WA_Borderless, Screen ? 1 : 0, + WA_Backdrop, Screen ? 1 : 0, + WA_Activate, 1, + WA_CloseGadget, Screen ? 0 : 1, + WA_DragBar, Screen ? 0 : 1, + WA_DepthGadget, Screen ? 0 : 1, + WA_SizeGadget, Screen ? 0 : 1, + WA_SizeBBottom, Screen ? 0 : 1, + WA_Title, TitleBar, + WA_ScreenTitle, TitleBar, + WA_IDCMP, IDCMP_RAWKEY | IDCMP_VANILLAKEY | IDCMP_MENUPICK | IDCMP_CLOSEWINDOW | IDCMP_CHANGEWINDOW, + Screen ? WA_CustomScreen : WA_PubScreen, Screen ? Screen : DefaultPubScreen, + TAG_DONE)) == 0) + exit (1); + + ThisProcess = (struct Process *)FindTask(0); + OldWindowPtr = ThisProcess->pr_WindowPtr; + ThisProcess->pr_WindowPtr = Window; + + if ((Visual = GetVisualInfo (Window->WScreen, TAG_DONE)) == 0) + exit (1); + if ((Menus = CreateMenus (NewMenus, GTMN_NewLookMenus, TRUE, TAG_DONE)) == 0) + exit (1); + LayoutMenus (Menus, Visual, GTMN_NewLookMenus, TRUE, TAG_DONE); + SetMenuStrip (Window, Menus); + + if ((GameReq = alloc_freq (dir)) == 0) + exit (1); + if ((SaveReq = alloc_freq (dir)) == 0) + exit (1); + + RastPort = Window->RPort; + SetDrMd (RastPort, JAM2); + SetAPen (RastPort, 1); + SetBPen (RastPort, 0); + SetFont (RastPort, Font); + DisplayHeight = ((Window->Height - Window->BorderTop - Window->BorderBottom) / RastPort->TxHeight) - 1; + PreviousHeight = DisplayHeight; + + reset_cursor (); +} + +__autoexit void + amiga_exit (void) +{ + int i; + + for (i = 0; i < HISTORY_LINES; i++) + { + if (*(History + i)) + FreeVec (*(History + i)); + *(History + i) = 0; + } + + if (GameReq) + FreeAslRequest (GameReq); + if (SaveReq) + FreeAslRequest (SaveReq); + if (Menus) + FreeMenus (Menus); + if (Visual) + FreeVisualInfo (Visual); + if (ThisProcess) + ThisProcess->pr_WindowPtr = OldWindowPtr; + if (Window) + CloseWindow (Window); + if (Screen) + CloseScreen (Screen); + if (Font) + CloseFont (Font); + if (DefaultPubScreen) + UnlockPubScreen (0, DefaultPubScreen); + if (Icon) + FreeDiskObject (Icon); +} + +void + screen_ratio (struct Screen *screen) +{ + struct TagItem vti[] = + {VTAG_VIEWPORTEXTRA_GET, 0, + VTAG_END_CM, 0}; + struct ViewPortExtra *vpe; + + ScreenWidth = screen->Width; + ScreenHeight = screen->Height; + if (screen->ViewPort.ColorMap) + { + if (VideoControl (screen->ViewPort.ColorMap, vti) == 0) + { + vpe = (struct ViewPortExtra *) vti[0].ti_Data; + ScreenWidth = vpe->DisplayClip.MaxX - vpe->DisplayClip.MinX + 1; + ScreenHeight = vpe->DisplayClip.MaxY - vpe->DisplayClip.MinY + 1; + } + } +} + +void + reset_cursor (void) +{ + Move (RastPort, Window->BorderLeft, Window->BorderTop + (DisplayHeight * RastPort->TxHeight)); +} + +void + rect (long xMin, long yMin, long xMax, long yMax, unsigned long pen) +{ + unsigned long saved_pen; + + saved_pen = RastPort->FgPen; + SetAPen (RastPort, pen); + RectFill (RastPort, xMin, yMin, xMax, yMax); + SetAPen (RastPort, saved_pen); +} + +void + text (char *s, int n) +{ + Move (RastPort, RastPort->cp_x, RastPort->cp_y + RastPort->TxBaseline); + Text (RastPort, s, n); + Move (RastPort, RastPort->cp_x, RastPort->cp_y - RastPort->TxBaseline); +} + +int + check_len (char *s, int n) +{ + return TextLength (RastPort, s, n) > Window->Width - Window->BorderRight - RastPort->cp_x; +} + +void + cursor (int under) +{ + int size; + + os_flush (); + size = (under == 0) ? RastPort->TxWidth : char_len (under); + + SetDrMd (RastPort, COMPLEMENT); + rect (RastPort->cp_x, RastPort->cp_y, + RastPort->cp_x + size - 1, RastPort->cp_y + RastPort->TxHeight - 1, 0); + SetDrMd (RastPort, JAM2); +} + +void + set_window (int limit, int x) +{ + if (limit) + { + WindowLimits (Window, + RastPort->cp_x + RastPort->TxWidth + Window->BorderRight + x, + Window->BorderTop + Window->BorderBottom + (RastPort->TxHeight * 3), ~0, ~0); + } + else + { + WindowLimits (Window, Window->Width, Window->Height, Window->Width, + Window->Height); + } +} + +int + get_key (UWORD * qualifier_addr, int c, int wait) +{ + struct IntuiMessage *imsg; + ULONG class; + UWORD code, qualifier; + int old_height; + + os_flush (); + while (1) + { + while (imsg = (struct IntuiMessage *) GetMsg (Window->UserPort)) + { + class = imsg->Class; + code = imsg->Code; + qualifier = imsg->Qualifier; + ReplyMsg ((struct Message *) imsg); + if (qualifier_addr) + *qualifier_addr = qualifier; + switch (class) + { + case IDCMP_RAWKEY: + switch (code) + { + case 0x4C: + return 129; + case 0x4D: + return 130; + case 0x4F: + return 131; + case 0x4E: + return 132; + case 0x5F: + help (); + break; + } + break; + case IDCMP_VANILLAKEY: + switch (code) + { + case 8: + return 127; + case 13: + return 13; + case 127: + return 260; + default: + if (code >= 32 && code <= 126) + return code; + break; + } + break; + case IDCMP_CLOSEWINDOW: + exit (0); + break; + case IDCMP_CHANGEWINDOW: + old_height = DisplayHeight; + DisplayHeight = ((Window->Height - Window->BorderTop - Window->BorderBottom) / RastPort->TxHeight) - 1; + + if (PreviousHeight > DisplayHeight) + { + rect (Window->BorderLeft, + Window->BorderTop + (DisplayHeight * RastPort->TxHeight), + Window->Width - Window->BorderRight - 1, + Window->Height - Window->BorderBottom - 1, 0); + + if (RastPort->cp_y + RastPort->TxHeight > Window->Height - Window->BorderBottom) + { + Move (RastPort, RastPort->cp_x, Window->BorderTop + (DisplayHeight * RastPort->TxHeight)); + rect (Window->BorderLeft, RastPort->cp_y, + Window->Width - Window->BorderRight - 1, + RastPort->cp_y + RastPort->TxHeight - 1, 0); + PreviousHeight = DisplayHeight; + return 264; + } + } + PreviousHeight = DisplayHeight; + break; + case IDCMP_MENUPICK: + if (code != MENUNULL) + { + if (MENUNUM (code) == 0) + { + switch (ITEMNUM (code)) + { + case 0: + if (c != -1) + { + cursor (c); + SetAPen (RastPort, 1); + int r = newgame (0); + SetAPen (RastPort, 2); + cursor (c); + if (r != 0) + return r; + } + break; + case 2: + if (c != -1) return -2; + break; + case 3: + if (c != -1) return -3; + break; + case 5: + help (); + break; + case 6: + about (); + break; + case 8: + exit (0); + break; + } + } + } + break; + } + } + if (wait) + WaitPort (Window->UserPort); + else + return 0; + } +} + +void + redraw_line (unsigned char *buffer, int *pos, int max_size) +{ + char *current_buffer; + + cursor (*(buffer + *pos)); + if ((current_buffer = AllocVec (max_size, MEMF_CLEAR)) == 0) + return; + strcpy (current_buffer, buffer); + into_buffer (buffer, current_buffer, pos, max_size); + FreeVec (current_buffer); +} + +int + char_len (int c) +{ + unsigned char buffer; + + if (c < 0) + c += 256; + buffer = (unsigned char) c; + return TextLength (RastPort, &buffer, 1); +} + +void + move_text (int offset, int max) +{ + int xSource, xDest; + + xSource = RastPort->cp_x; + xDest = RastPort->cp_x + offset; + ClipBlit (RastPort, xSource, RastPort->cp_y, + RastPort, xDest, RastPort->cp_y, + max - MAX (xSource, xDest), RastPort->TxHeight, 0xC0); +} + +void + cursor_left (unsigned char *buffer, int *pos) +{ + while (*pos > 0) + { + (*pos)--; + Move (RastPort, RastPort->cp_x - char_len (*(buffer + *pos)), RastPort->cp_y); + } +} + +void + cursor_right (unsigned char *buffer, int *pos) +{ + while (*pos < strlen (buffer)) + { + Move (RastPort, RastPort->cp_x + char_len (*(buffer + *pos)), RastPort->cp_y); + (*pos)++; + } +} + +int + fit_text (unsigned char *pointer, int new) +{ + int a, b; + + a = TextLength (RastPort, pointer, strlen (pointer)) + char_len (new) + RastPort->cp_x + RastPort->TxWidth; + b = Window->Width - Window->BorderRight; + return (a > b) ? 0 : 1; +} + +void + into_buffer (unsigned char *buffer, unsigned char *new, int *pos, int max_size) +{ + if (new == 0) + return; + cursor (*(buffer + *pos)); + + int saved_x_position; + int saved_colour; + int i; + + cursor_left (buffer, pos); + saved_colour = RastPort->FgPen; + saved_x_position = RastPort->cp_x; + SetAPen (RastPort, RastPort->BgPen); + for (i = 0; i < strlen (buffer); i++) + os_printchar (*(buffer + i)); + os_flush (); + SetAPen (RastPort, saved_colour); + Move (RastPort, saved_x_position, RastPort->cp_y); + + *buffer = 0; + while (1) + { + if ((fit_text (buffer, *(new + *pos)) == 0) || (*(new + *pos) == 0) || (*pos == max_size)) + { + for (i = 0; i < strlen (buffer); i++) + os_printchar (*(buffer + i)); + os_flush (); + cursor (*(buffer + *pos)); + return; + } + else + { + *(buffer + *pos) = *(new + *pos); + (*pos)++; + *(buffer + *pos) = 0; + } + } +} + +void + store_hist (unsigned char *line) +{ + int i; + + if ((*line != 0) && (cmp (*(History + HISTORY_LINES - 1), line) != 0)) + { + if (*History) + FreeVec (*History); + for (i = 0; i < HISTORY_LINES - 1; i++) + *(History + i) = *(History + i + 1); + if (*(History + HISTORY_LINES - 1) = AllocVec (strlen (line) + 1, MEMF_CLEAR)) + strcpy (*(History + HISTORY_LINES - 1), line); + } + HistoryPosition = HISTORY_LINES; +} + +int + cmp (const char *a, const char *b) +{ + if ((a == 0) || (b == 0)) + return 1; + return strcmp (a, b); +} + +void + help (void) +{ + struct NewAmigaGuide guide = + {0, "Level9.guide", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + guide.nag_Screen = Window->WScreen; + if (AmigaGuideBase == 0) + AmigaGuideBase = OpenLibrary ("amigaguide.library", 34); + if (AmigaGuideBase != 0) + CloseAmigaGuide (OpenAmigaGuide (&guide, TAG_DONE)); +} + +void + about (void) +{ + req ("Level 9 Interpreter v2.0\n\n" + "Written by Glen Summers\n" + "Amiga version by David Kinder", "Continue"); +} + +LONG +req (UBYTE * text, UBYTE * gadgets,...) +{ + va_list arguments; + LONG return_value; + static struct EasyStruct requester = + {sizeof (struct EasyStruct), 0, TitleBar, 0, 0}; + + requester.es_TextFormat = text; + requester.es_GadgetFormat = gadgets; + va_start (arguments, gadgets); + busy (1); + return_value = EasyRequestArgs (Window, &requester, 0, arguments); + busy (0); + va_end (arguments); +} + +void + busy (int busy) +{ + if (Window && IntuitionBase->lib_Version >= 39) + SetWindowPointer (Window, WA_BusyPointer, busy, TAG_DONE); +} + +struct FileRequester * + alloc_freq (char *initdir) +{ + return AllocAslRequestTags (ASL_FileRequest, + strcmp (initdir, "") == 0 ? TAG_IGNORE : ASLFR_InitialDrawer, initdir, + ASLFR_SleepWindow, 1, + ASLFR_RejectIcons, 1, TAG_DONE); +} + +void + filereq (struct FileRequester *freq, char *buffer, char *title, ULONG save) +{ + if (AslRequestTags (freq, + ASLFR_Window, Window, + ASLFR_TitleText, title, + ASLFR_DoSaveMode, save, TAG_DONE)) + { + strcpy (buffer, freq->fr_Drawer); + AddPart (buffer, freq->fr_File, 256); + } + else + strcpy (buffer, ""); +} + +int + newgame (char *fname) +{ +char game[256]; +BPTR lock; + + fname ? strcpy (game, fname) : + filereq (GameReq, game, "Select a Level9 Game", 0); + + if (strcmp (game, "") == 0) + return 0; + + if (lock = Lock(game,ACCESS_READ)) + { + NameFromLock(lock,game,256); + UnLock(lock); + } + + if (LoadGame (game)) + { + if (Playing) + return -1; + Playing = TRUE; + while (RunGame ()); + Playing = FALSE; + } + else + req ("Unable to load game", "Cancel"); + return -1; +} diff --git a/os/dos.c b/os/dos.c new file mode 100755 index 0000000..1c97a26 --- /dev/null +++ b/os/dos.c @@ -0,0 +1,347 @@ +/* MS-DOS interface for Level 9 */ +/* Interface by David Kinder */ + +#include +#include +#include +#include +#include +#include +#include "level9.h" + +unsigned _stklen = 16384; + +#define TEXTBUFFER_SIZE 10240 +char TextBuffer[TEXTBUFFER_SIZE+1]; +int TextBufferPtr = 0; + +#define HISTORY_LINES 20 +unsigned char *History[HISTORY_LINES]; +int HistoryPosition = HISTORY_LINES; + +struct text_info TextInfo; +int Hotkey = 1; +int MoreCount = 0; + +#define KEY_BRK 3 +#define KEY_HOME 327 +#define KEY_CRSRU 328 +#define KEY_CRSRL 331 +#define KEY_CRSRR 333 +#define KEY_END 335 +#define KEY_CRSRD 336 +#define KEY_DEL 339 +#define KEY_F12 390 + +L9UINT32 filelength(FILE *f); + +int character(void); +void into_buffer(char *buffer,char *newb,int *x,int *i); +void store_hist(char *line); + +void os_printchar(char c) +{ + if (c == '\r') + { + os_flush(); + cprintf("\r\n"); + + if (++MoreCount >= TextInfo.screenheight) + { + MoreCount = 0; + cprintf("[More]"); + character(); + gotoxy(1,wherey()); + cprintf(" "); + gotoxy(1,wherey()); + } + return; + } + + if (isprint(c) == 0) return; + if (TextBufferPtr >= TEXTBUFFER_SIZE) os_flush(); + *(TextBuffer + (TextBufferPtr++)) = c; +} + +L9BOOL os_input(char *ibuff, int size) +{ +int x,y,c,i = 0; + + x = wherex(); + y = wherey(); + *ibuff = '\0'; + + while (1) + { + switch (c = character()) + { + case '\r': + if (strlen(ibuff) > 0 || Hotkey == 0) + { + os_printchar('\r'); + MoreCount = 0; + store_hist(ibuff); + return TRUE; + } + case '\b': + if (i > 0) + { + memmove(ibuff+i-1,ibuff+i,strlen(ibuff+i)+1); + i--; + gotoxy(x,y); + cprintf("%s ",ibuff); + gotoxy(x+i,y); + } + break; + case KEY_DEL: + if (i < strlen(ibuff)) + { + memmove(ibuff+i,ibuff+i+1,strlen(ibuff+i+1)+1); + gotoxy(x,y); + cprintf("%s ",ibuff); + gotoxy(x+i,y); + } + break; + case KEY_CRSRL: + if (i > 0) i--; + gotoxy(x+i,y); + break; + case KEY_CRSRR: + if (i < strlen(ibuff)) i++; + gotoxy(x+i,y); + break; + case KEY_HOME: + i = 0; + gotoxy(x,y); + break; + case KEY_END: + i = strlen(ibuff); + gotoxy(x+i,y); + break; + case KEY_CRSRU: + if ((HistoryPosition > 0) && (*(History+HistoryPosition-1) != NULL)) + { + HistoryPosition--; + into_buffer(ibuff,*(History+HistoryPosition),&x,&i); + } + break; + case KEY_CRSRD: + if ((HistoryPosition < HISTORY_LINES-1) && (*(History+HistoryPosition+1) != NULL)) + { + HistoryPosition++; + into_buffer(ibuff,HistoryPosition == HISTORY_LINES ? "" : *(History+HistoryPosition),&x,&i); + } + else + { + HistoryPosition = HISTORY_LINES; + into_buffer(ibuff,"",&x,&i); + } + break; + default: + if (c < 256 && i < size && isprint(c) && wherex()+strlen(ibuff+i) < TextInfo.screenwidth) + { + memmove(ibuff+i+1,ibuff+i,strlen(ibuff+i)+1); + *(ibuff+i++) = c; + gotoxy(x,y); + cprintf("%s",ibuff); + gotoxy(x+i,y); + } + break; + } + } +} + +char os_readchar(void) +{ + os_flush(); + if (kbhit() != 0) + { + MoreCount = 0; + return character(); + } + else return 0; +} + +void os_flush(void) +{ +static int semaphore = 0; +int ptr, space, lastspace, searching; + + if (TextBufferPtr < 1) return; + if (semaphore) return; + semaphore = 1; + + *(TextBuffer+TextBufferPtr) = ' '; + ptr = 0; + while (TextBufferPtr+wherex()-1 > TextInfo.screenwidth) + { + space = ptr; + lastspace = space; + searching = 1; + while (searching) + { + while (TextBuffer[space] != ' ') space++; + if (space-ptr+wherex()-1 > TextInfo.screenwidth) + { + space = lastspace; + cprintf("%.*s%s",space-ptr,TextBuffer+ptr,wherex()+space-ptr > TextInfo.screenwidth ? "" : "\r\n"); + if (TextBuffer[space] == ' ') space++; + TextBufferPtr -= (space-ptr); + ptr = space; + searching = 0; + } + else lastspace = space; + space++; + } + } + cprintf("%.*s", TextBufferPtr, TextBuffer+ptr); + TextBufferPtr = 0; + + semaphore = 0; +} + +L9BOOL os_save_file(L9BYTE * Ptr, int Bytes) +{ +char name[256]; +FILE *f; + + os_flush(); + cprintf("Save file: "); + Hotkey = 0; + os_input(name,256); + Hotkey = 1; + + f = fopen(name, "wb"); + if (!f) return FALSE; + fwrite(Ptr, 1, Bytes, f); + fclose(f); + return TRUE; +} + +L9BOOL os_load_file(L9BYTE *Ptr,int *Bytes,int Max) +{ +char name[256]; +FILE *f; + + os_flush(); + cprintf("Load file: "); + Hotkey = 0; + os_input(name,256); + Hotkey = 1; + + f = fopen(name, "rb"); + if (!f) return FALSE; + + *Bytes = filelength(f); + if (*Bytes > Max) + { + fclose(f); + return FALSE; + } + fread(Ptr, 1, *Bytes, f); + fclose(f); + return TRUE; +} + +L9BOOL os_get_game_file(char *NewName,int Size) +{ + os_flush(); + cprintf("Load next game: "); + Hotkey = 0; + os_input(NewName,Size); + Hotkey = 1; + return TRUE; +} + +void os_set_filenumber(char *NewName,int Size,int n) +{ +char *p; +int i; + + p = strrchr(NewName,FILE_DELIM); + if (p == NULL) p = NewName; + for (i = strlen(p)-1; i >= 0; i--) + { + if (isdigit(p[i])) + { + p[i] = '0'+n; + return; + } + } +} + +int main(int argc, char **argv) +{ +int i; + + for (i = 0; i < HISTORY_LINES; i++) *(History+i) = NULL; + gettextinfo(&TextInfo); + + clrscr(); + gotoxy(1,TextInfo.screenheight); + cprintf("Level 9 Interpreter v2.0"); + os_printchar('\r'); + os_printchar('\r'); + + if (argc != 2) + { + cprintf("Syntax: %s \r\n",argv[0]); + return 0; + } + if (!LoadGame(argv[1])) + { + cprintf("Error: Unable to open game file\r\n"); + return 0; + } + while (RunGame()); + StopGame(); + FreeMemory(); + return 0; +} + +int character(void) +{ +int c; + + c = getch(); + if (c == 0) c = getch()+256; + if (c == KEY_F12 || c == KEY_BRK) + { + StopGame(); + FreeMemory(); + clrscr(); + exit(0); + } + return c; +} + +void into_buffer(char *buffer,char *newb,int *x,int *i) +{ + gotoxy(*x,wherey()); + strcpy(buffer,newb); + cprintf("%s",buffer); + clreol(); + *i = strlen(buffer); + gotoxy(*x+*i,wherey()); +} + +int cmp(const char *a,const char *b) +{ + if (a == 0 || b == 0) return 1; + return strcmp(a,b); +} + +void store_hist(char *line) +{ +int i; + + if (*line != NULL && cmp(*(History+HISTORY_LINES-1),line) != 0) + { + if (*History) free(*History); + for (i = 0; i < HISTORY_LINES-1; i++) *(History+i) = *(History+i+1); + *(History+HISTORY_LINES-1) = malloc(strlen(line)+1); + if (*(History+HISTORY_LINES-1)) strcpy(*(History+HISTORY_LINES-1),line); + } + HistoryPosition = HISTORY_LINES; +} + diff --git a/os/level9win.cpp b/os/level9win.cpp new file mode 100755 index 0000000..9509d5c --- /dev/null +++ b/os/level9win.cpp @@ -0,0 +1,907 @@ +#include +#pragma hdrstop + +#include + +#include "..\level9.h" + +// define application name, main window title +#define AppName "Level9" +#define MainWinTitle "Level9" + +// help file name and ini file are set from AppName +char HelpFileName[] = AppName".hlp"; + +#ifdef WIN16 +char Ini[] = AppName".ini"; +#else +char Ini[] = "Software\\CrapolaSoftware\\Level9\\1.00"; +#endif + +#include "level9.rh" + +String Output=""; +int Line=0; +int LineOffset=0; +int LineStart=0; +int LastWordEnd=0; +HWND hWndMain; +int FontHeight,LineSpacing; +LOGFONT lf; +HFONT Font; +COLORREF FontColour; +int PageWidth,PageHeight; +int Margin; +SimpleList InputChars; +int iPos,Input; +String Hash(20); + +void DisplayLine(int Line,char *Str,int Len) +{ + HDC dc=GetDC(hWndMain); + HFONT OldFont=SelectObject(dc,Font); + COLORREF OldCol=SetTextColor(dc,FontColour); + COLORREF OldBk=SetBkColor(dc,GetSysColor(COLOR_WINDOW)); + TextOut(dc,Margin,Line*LineSpacing-LineOffset,Str,Len); + SelectObject(dc,OldFont); + SetTextColor(dc,OldCol); + SetBkColor(dc,OldBk); + ReleaseDC(hWndMain,dc); +} + +void DisplayLineJust(int Line,char *Str,int Len) +{ + HDC dc=GetDC(hWndMain); + HFONT OldFont=SelectObject(dc,Font); + COLORREF OldCol=SetTextColor(dc,FontColour); + COLORREF OldBk=SetBkColor(dc,GetSysColor(COLOR_WINDOW)); + + SIZE Size; +#ifdef WIN32 + GetTextExtentPoint32(dc,Str,Len,&Size); +#else + GetTextExtentPoint(dc,Str,Len,&Size); +#endif + + // count spaces + int nBreaks=0; + char *Ptr=Str; + for (int i=0;i *C; + App::PeekLoop(); + if ((C=InputChars.GetFirst())==NULL) return 0; + char InputChar=C->Item; + delete C; + return InputChar; +} + +SimpleList History; +int HistoryLines=0; +#define NUMHIST 20 + +void Erase(int Start,int End) +{ + RECT rc; + rc.left=Margin+Start; + rc.right=Margin+End; + rc.top=Line*LineSpacing-LineOffset; + rc.bottom=Line*LineSpacing+FontHeight-LineOffset; + HDC dc=GetDC(hWndMain); +#ifdef WIN32 + FillRect(dc,&rc,(HBRUSH) GetClassLong(hWndMain,GCL_HBRBACKGROUND)); +#else + FillRect(dc,&rc,(HBRUSH) GetClassLong(hWndMain,GCW_HBRBACKGROUND)); +#endif + ReleaseDC(hWndMain,dc); +} + +void CancelInput() +{ + if (Caret) InputChars.AddTail(-1); +} + +void HashCommand(char *h) +{ + if (Caret) + { + Hash=h; + InputChars.AddTail(-2); + } +} + +BOOL os_input(char*ibuff,int size) +{ + slIterator HistPtr(History); + if (HistPtr()) HistPtr--; // force out + + Input=Output.Len(); + iPos=0; + Caret=TRUE; + MakeCaret(); + SetCaret(Margin+LineLength(Output+LineStart,Input-LineStart),Line*LineSpacing-LineOffset); + + while (TRUE) + { + SimpleNode *C; + while ((C=InputChars.GetFirst())==NULL) App::PeekLoop(); + int InputChar=C->Item; + delete C; + if (InputChar<0) + { + strcpy(ibuff,Hash); + KillCaret(); + Caret=FALSE; + return InputChar<-1; + } + else if (InputChar=='\r') + { + strcpy(ibuff,Output+Input); // >500 chrs?? + KillCaret(); + Caret=FALSE; + HistPtr.Last(); + if (*ibuff && (!HistPtr() || (String) HistPtr!=ibuff)) + { + History.AddTailRef(String(ibuff)); + if (HistoryLines==NUMHIST) + delete History.GetFirst(); + else HistoryLines++; + } + return TRUE; + } + else switch (InputChar) + { + case 256+VK_DELETE: + if (iPos>=Output.Len()-Input) break; + iPos++; + case 8: + if (iPos>0) + { + int OldLen=LineLength(Output+LineStart,Output.Len()-LineStart); + Output.Remove(Input+--iPos,1); + int Len=LineLength(Output+LineStart,Output.Len()-LineStart); + Erase(Len,OldLen); + } + break; + case 256+VK_UP: + if (!HistPtr()) HistPtr.Last(); + else + { + --HistPtr; + if (!HistPtr()) HistPtr.First(); + } + if (HistPtr()) + { + int OldLen=LineLength(Output+LineStart,Output.Len()-LineStart); + Output.Len(Input); + Output.Insert((String)HistPtr,Input); + iPos=Output.Len()-Input; + int Len=LineLength(Output+LineStart,Output.Len()-LineStart); + Erase(Len,OldLen); + } + break; + case 256+VK_DOWN: + if (HistPtr()) + { + ++HistPtr; + if (!HistPtr()) HistPtr.Last(); + else + { + int OldLen=LineLength(Output+LineStart,Output.Len()-LineStart); + Output.Len(Input); + Output.Insert((String)HistPtr,Input); + iPos=Output.Len()-Input; + int Len=LineLength(Output+LineStart,Output.Len()-LineStart); + Erase(Len,OldLen); + } + } + break; + case 256+VK_LEFT: + if (iPos>0) iPos--; + break; + case 256+VK_RIGHT: + if (iPosPageWidth-2*Margin) return LastWordEnd+1; + else if (c==0) return Pos-1; + else if (c=='\r') return Pos; + LastWordEnd=Pos-LineStart-1; + } + } +} + +#define SCROLLBACK 2000 + +void NewLine() +{ + while (Output.Len()>SCROLLBACK) + { + int Len=FindLineLength(0); + Output.Remove(0,Len); + LineStart-=Len; + LineOffset-=LineSpacing; + Line--; + } + Line++; + + if (Line*LineSpacing+FontHeight-LineOffset>PageHeight) + { + int Shift=Line*LineSpacing+FontHeight-LineOffset-PageHeight; + ScrollWindow(hWndMain,0,-Shift,NULL,NULL); + LineOffset+=Shift; + UpdateWindow(hWndMain); + } + //((Window*)App::MainWindow)->SetVirtualExtent(PageWidth,Line*LineSpacing+FontHeight,1,1); + // scroll +} + +void os_flush() +{ + if (LineLength((char*)Output+LineStart,Output.Len()-LineStart)>PageWidth-2*Margin) + { + DisplayLineJust(Line,(char*) Output+LineStart,LastWordEnd); + LineStart+=LastWordEnd+1; + NewLine(); + } + DisplayLine(Line,(char*) Output+LineStart,Output.Len()-LineStart); + LastWordEnd=Output.Len()-LineStart; +} + +void os_printchar(char c) +{ + Output << c; + if (c=='\r' || c==' ') + { + if (LineLength((char*)Output+LineStart,Output.Len()-LineStart-1)>PageWidth-2*Margin) + { + DisplayLineJust(Line,(char*) Output+LineStart,LastWordEnd); + LineStart+=LastWordEnd+1; + NewLine(); + } + if (c=='\r') + { + DisplayLine(Line,(char*) Output+LineStart,Output.Len()-LineStart-1); + LineStart=Output.Len(); + NewLine(); + } + LastWordEnd=Output.Len()-LineStart-1; + } +} + +void Redraw() +{ + int l=0; + int LineStart=0,LastWordEnd=0; + int Pos=0; + char c; + do + { + c= Output[Pos++]; + if (c=='\r' || c==' ' || c==0) + { + if (LineLength((char*)Output+LineStart,Pos-LineStart-1)>PageWidth-2*Margin) + { + DisplayLineJust(l,(char*) Output+LineStart,LastWordEnd); + LineStart+=LastWordEnd+1; + l++; + } + if (c=='\r' || c==0) + { + DisplayLine(l,(char*) Output+LineStart,Pos-LineStart-1); + if (c=='\r') + { + LineStart=Pos; + l++; + } + } + LastWordEnd=Pos-LineStart-1; + } + } while (c); +} + +void Paginate() +{ + int l=0; + int LineStart=0,LastWordEnd=0; + int Pos=0; + char c; + do + { + c= Output[Pos++]; + if (c=='\r' || c==' ' || c==0) + { + if (LineLength((char*)Output+LineStart,Pos-LineStart-1)>PageWidth-2*Margin) + { + LineStart+=LastWordEnd+1; + l++; + } + if (c=='\r') + { + LineStart=Pos; + l++; + } + LastWordEnd=Pos-LineStart-1; + } + } while (c); + Line=l; + LineOffset=max(0,Line*LineSpacing+FontHeight-PageHeight); + //((Window*)App::MainWindow)->SetVirtualExtent(PageWidth,Line*LineSpacing+FontHeight,1,1); + + if (Caret) SetCaret(Margin+LineLength(Output+LineStart,Input-LineStart+iPos),Line*LineSpacing-LineOffset); +} + +const char Filters[]="Level 9 Game Files (*.dat)\0*.dat\0Spectrum Snapshots (*.sna)\0*.sna\0All Files (*.*)\0*.*\0\0"; +int FiltIndex; +const char GameFilters[]="Saved game file (*.sav)\0*.sav\0All Files (*.*)\0*.*\0\0"; +FName LastGameFile; +int GameFiltIndex; + +void os_set_filenumber(char *NewName,int Size,int n) +{ + FName fn(NewName); + String S; + fn.GetBaseName(S); + while (isdigit(S.Last())) S.Remove(S.Len()-1,1); + fn.NewBaseName(S << n); + strcpy(NewName,fn); +} + +BOOL os_get_game_file(char* Name,int Size) +{ + return CustFileDlg(App::MainWindow,-1,Name,Size,"Open Game File",Filters,&FiltIndex).Execute(); +} + +BOOL os_save_file(BYTE *Ptr,int Bytes) +{ + CancelInput(); + if (CustFileDlg(App::MainWindow,-1,LastGameFile,LastGameFile.Size(),"Save File",GameFilters,&GameFiltIndex,OFN_OVERWRITEPROMPT | OFN_EXPLORER).Execute()) + { + LastGameFile.Update(); + if (!LastGameFile.GetExt()) LastGameFile.NewExt("sav"); + + FILE *f=fopen(LastGameFile,"wb"); + if (f) + { + fwrite(Ptr,1,Bytes,f); + fclose(f); + return TRUE; + } + } + return FALSE; +} + +BOOL os_load_file(BYTE *Ptr,int *Bytes,int Max) +{ + CancelInput(); + if (CustFileDlg(App::MainWindow,-1,LastGameFile,LastGameFile.Size(),"Load File",GameFilters,&GameFiltIndex,OFN_FILEMUSTEXIST | OFN_EXPLORER).Execute()) + { + LastGameFile.Update(); + FILE *f=fopen(LastGameFile,"rb"); + if (f) + { + *Bytes=filelength(f); + if (*Bytes>Max) + MessageBox(App::MainWindow->hWnd,"Not a valid saved game file","Load Error",MB_OK | MB_ICONEXCLAMATION); + else + { + fread(Ptr,1,*Bytes,f); + fclose(f); + return TRUE; + } + } + } + return FALSE; +} + +// About Dialog ****************************************** + +class AboutDialog : public Dialog +{ +public: + AboutDialog(Object *Parent) : Dialog(Parent,IDD_ABOUT,"AboutDialog") {} +}; + +// MainWindow ***************************************** + +class MainWindow : public HashWindow +{ +public: + MainWindow(Object *Parent,char *Title); + ~MainWindow(); + static FName LastFile; + + BOOL HelpState; + BOOL Playing; + + void Destroy(); + BOOL SetupWindow(); + void OpenFile(char *name); + + void CmHelpContents(); + void CmHelpUsing(); + void CmAbout(); + void CmExit(); + void CmOpen(); + void CmSelectFont(); + void CmRestore() { HashCommand("#restore"); } + void CmSave() { HashCommand("save"); } + void CmDictionary() { HashCommand("#dictionary"); } + + void SetFont(); + void DelFonts(); + +// message response functions + + BOOL LButtonDown(TMSG &); + BOOL RButtonDown(TMSG &); + BOOL LButtonUp(TMSG &); + BOOL WMMouseMove(TMSG &); + BOOL WMSize(TMSG &); + + BOOL WMKeyDown(TMSG &); + BOOL WMChar(TMSG &); + BOOL WMSetFocus(TMSG&); + BOOL WMKillFocus(TMSG&); + +// window paint request + void Paint(HDC, BOOL, RECT&); + +// enable message response + HASH_EV_ENABLE(MainWindow) +} ; + +// define response functions +EV_START(MainWindow) +// command messages + EV_COMMAND(CM_ABOUT, CmAbout) + EV_COMMAND(CM_HELPCONTENTS, CmHelpContents) + EV_COMMAND(CM_HELPUSING, CmHelpUsing) + EV_COMMAND(CM_EXIT, CmExit) + EV_COMMAND(CM_OPEN, CmOpen) + EV_COMMAND(CM_FONT, CmSelectFont) + EV_COMMAND(CM_FILELOAD, CmRestore) + EV_COMMAND(CM_FILESAVE, CmSave) + EV_COMMAND(CM_DICTIONARY, CmDictionary) + +// windows messages + EV_MESSAGE(WM_LBUTTONDOWN, LButtonDown) + EV_MESSAGE(WM_RBUTTONDOWN, RButtonDown) + EV_MESSAGE(WM_MOUSEMOVE, WMMouseMove) + EV_MESSAGE(WM_LBUTTONUP, LButtonUp) + EV_MESSAGE(WM_KEYDOWN, WMKeyDown) + EV_MESSAGE(WM_CHAR, WMChar) + EV_MESSAGE(WM_SIZE, WMSize) + EV_MESSAGE(WM_SETFOCUS,WMSetFocus) + EV_MESSAGE(WM_KILLFOCUS,WMKillFocus) + +EV_END + +// main window constructor +MainWindow::MainWindow(Object *Parent,char *Title) : HashWindow(Parent,Title,"MainWindow") +{ + // set window style + Style=WS_OVERLAPPEDWINDOW; + + // give it a menu and icon + AssignMenu(IDM_MENU); + SetIcon(IDI_ICON); + + // set help flag + HelpState = FALSE; + Flags|=W_OVERSCROLL; +} + +// this is called to setup the window +BOOL MainWindow::SetupWindow() +{ +#ifdef WIN32 + SetWindowLong(hWnd,GWL_EXSTYLE,WS_EX_OVERLAPPEDWINDOW); +#endif + + // load window pos from ini file (will also be automatically saved on exit) + GetWindowState(); + SetMru(hWnd,CM_EXIT); + hWndMain=hWnd; + SetFont(); + Playing=FALSE; + + return TRUE; +} + +void MainWindow::CmSelectFont() +{ + CHOOSEFONT cf; + + cf.lStructSize=sizeof(cf); + cf.hwndOwner=hWnd; + cf.lpLogFont=&lf; + cf.rgbColors=FontColour; + + cf.Flags=CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS; + + if (ChooseFont(&cf)) + { + DelFonts(); + SetFont(); + FontColour=cf.rgbColors; + KillCaret(); + Paginate(); + InvalidateRect(hWnd,NULL,TRUE); + MakeCaret(); + } +} + +void MainWindow::SetFont() +{ + Font=CreateFontIndirect(&lf); + HDC dc=GetDC(hWnd); + HFONT OldFont=SelectObject(dc,Font); + TEXTMETRIC tm; + GetTextMetrics(dc,&tm); + FontHeight=tm.tmHeight; + Margin=tm.tmAveCharWidth; + LineSpacing=(int) 1.1*FontHeight; + SelectObject(dc,OldFont); + ReleaseDC(dc,hWnd); +} + +void MainWindow::DelFonts() +{ + DeleteObject(Font); +} + +// this is called when the window is destroyed +void MainWindow::Destroy() +{ + // close help if open + if (HelpState) WinHelp(hWnd,HelpFileName, HELP_QUIT, 0L); + DelFonts(); + StopGame(); + Playing=FALSE; + FreeMemory(); + CancelInput(); +} + +MainWindow::~MainWindow() +{ +} + +FName MainWindow::LastFile; + +void MainWindow::OpenFile(char *name) +{ +// in input routine?, cause fall through + CancelInput(); + // clear buffers ect..., enen if fails as invalidaes game memory + Output=""; + LineStart=LastWordEnd=0; + Paginate(); + InvalidateRect(hWnd,NULL,TRUE); + + if (!LoadGame(name)) MessageBox(hWnd,"Unable to load game file","Load Error",MB_OK | MB_ICONEXCLAMATION); + else + { + LastFile=name; + AddToMru(LastFile); + + if (Playing) return; + Playing=TRUE; + while (Playing && RunGame()) App::PeekLoop(); + Playing=FALSE; + } +} + +void MainWindow::CmOpen() +{ + FName fn=LastFile; + if (os_get_game_file(fn,fn.Size())) + OpenFile(fn); +} + +void MainWindow::CmExit() +{ + DestroyWindow(); +} + +void MainWindow::CmHelpContents() +{ + FName HelpFile; + GetModuleFileName(App::hInstance,HelpFile,HelpFile.Size()); + HelpFile.Update(); + HelpFile.NewName(HelpFileName); + HelpState = WinHelp(hWnd, HelpFile, HELP_CONTENTS, 0L); +} + +void MainWindow::CmHelpUsing() +{ + FName HelpFile; + GetModuleFileName(App::hInstance,HelpFile,HelpFile.Size()); + HelpFile.Update(); + HelpFile.NewName(HelpFileName); + HelpState = WinHelp(hWnd, HelpFile, HELP_HELPONHELP, 0L); +} + +void MainWindow::Paint(HDC, BOOL, RECT&) +{ +// LineOffset=yPos; + Redraw(); +} + +void MainWindow::CmAbout() +{ + AboutDialog(this).Execute(); +} + +BOOL MainWindow::WMKeyDown(TMSG &Msg) +{ + switch ((int) Msg.wParam) // the virtual key code + { + case VK_F1: + CmHelpContents(); + break; + case VK_F2: CmOpen(); break; + case VK_F3: CmRestore(); break; + case VK_F4: CmSave(); break; + case VK_F5: CmDictionary(); break; + + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + case VK_DELETE: + case VK_END: + case VK_HOME: + InputChars.AddTail(256+Msg.wParam); + break; + } + return TRUE; // message handled +} + +BOOL MainWindow::WMChar(TMSG &Msg) +{ + InputChars.AddTail(Msg.wParam); + return TRUE; +} + +BOOL MainWindow::LButtonDown(TMSG &) +{ + // int xPos = LOWORD(Msg.lParam); /* horizontal position of cursor */ + // int yPos = HIWORD(Msg.lParam); /* vertical position of cursor */ + // could instigate drag here + // SetCapture(hWnd); + return TRUE; +} + +BOOL MainWindow::WMMouseMove(TMSG &) +{ + // int xPos = LOWORD(Msg.lParam); /* horizontal position of cursor */ + // int yPos = HIWORD(Msg.lParam); /* vertical position of cursor */ + return TRUE; +} + +BOOL MainWindow::LButtonUp(TMSG &) +{ + // ReleaseCapture(); + return TRUE; +} + +BOOL MainWindow::RButtonDown(TMSG &) +{ + // int xPos = LOWORD(Msg.lParam); /* horizontal position of cursor */ + // int yPos = HIWORD(Msg.lParam); /* vertical position of cursor */ + return TRUE; +} + +BOOL MainWindow::WMSize(TMSG &Msg) +{ + PageWidth = LOWORD(Msg.lParam); + PageHeight = HIWORD(Msg.lParam); + Paginate(); + InvalidateRect(hWnd,NULL,TRUE); + return TRUE; +} + +BOOL MainWindow::WMSetFocus(TMSG&) +{ + MakeCaret(); + return TRUE; +} + +BOOL MainWindow::WMKillFocus(TMSG&) +{ + KillCaret(); + return TRUE; +} + +// App ***************************************** + +class MyApp : public App +{ +public: + MyApp(char *Name); + ~MyApp(); + +private: + void InitMainWindow(); + void SetDefs(); + void ReadIni(); + void WriteIni(); + void FirstIn(); +}; + +void MyApp::SetDefs() +{ + // Set application default settings here + MainWindow::LastFile=""; + FiltIndex=0; + LastGameFile=""; + GameFiltIndex=0; + + memset(&lf,0,sizeof(lf)); + lf.lfHeight=-16; + lf.lfWeight=FW_NORMAL; + lf.lfCharSet=ANSI_CHARSET; + lf.lfOutPrecision=OUT_TT_PRECIS; + lf.lfClipPrecision=CLIP_DEFAULT_PRECIS; + lf.lfQuality=PROOF_QUALITY; + lf.lfPitchAndFamily=4; //FIXED_PITCH | 4 | FF_MODERN; + strcpy(lf.lfFaceName,"Times New Roman"); + + FontColour=RGB(0,0,0); +} + +void MyApp::ReadIni() +{ + // read information from ini file + SetDefs(); + ReadIniString("General","LastFile",(String&)MainWindow::LastFile); + ReadIniInt("General","FiltIndex",FiltIndex); + ReadIniString("General","LastGameFile",(String&)LastGameFile); + ReadIniInt("General","GameFiltIndex",GameFiltIndex); + + ReadIniInt("Font","Size",lf.lfHeight); + String S(LF_FACESIZE); + ReadIniString("Font","Name",S); + if (*S) strcpy(lf.lfFaceName,S); + ReadIniInt("Font","Colour",(long&) FontColour); +} + +void MyApp::WriteIni() +{ + // write information to ini file + WriteIniString("General","LastFile",MainWindow::LastFile); + WriteIniInt("General","FiltIndex",FiltIndex); + WriteIniString("General","LastGameFile",LastGameFile); + WriteIniInt("General","GameFiltIndex",GameFiltIndex); + + long FontHeight=lf.lfHeight; + WriteIniInt("Font","Size",FontHeight); + WriteIniString("Font","Name",lf.lfFaceName); + WriteIniInt("Font","Colour",(long) FontColour); +} + +MyApp::MyApp(char *Name) : App(Name,Ini) +{ + // enable 3d dialogue boxes +#ifdef WIN16 + EnableCtl3d(); +#else + OSVERSIONINFO ovi; + ovi.dwOSVersionInfoSize=sizeof(ovi); + GetVersionEx(&ovi); + if (ovi.dwMajorVersion == 3) EnableCtl3d(); +#endif + + // read from ini file + ReadMru(4); + ReadIni(); +}; + +void MyApp::InitMainWindow() +{ + MainWindow=new ::MainWindow(0,MainWinTitle); +} + +MyApp::~MyApp() +{ + // write information to ini file + WriteMru(); + WriteIni(); +} + + +void MyApp::FirstIn() +{ + ((::MainWindow*) MainWindow)->CmOpen(); +} + +// main function, called from WinMain() +int Main() +{ + // create and run application + return MyApp(AppName).Run(); +} + diff --git a/porting.txt b/porting.txt new file mode 100755 index 0000000..afa30cc --- /dev/null +++ b/porting.txt @@ -0,0 +1,179 @@ + +Level9 Interpreter by Glen Summers (gsummers@physics.ox.ac.uk) +This guide by David Kinder (david.kinder@physics.ox.ac.uk) + + +Level9 has already been compiled on DOS, Windows, Amiga and Acorn systems, +so it should be quite easy to get it working on any other modern computer. + + +The first thing that must be done is to check the typedefs in level9.h. +The typedefs must have the following properties: + + L9BYTE unsigned 8 bit quantity + L9UINT16 unsigned 16 bit quantity + L9UINT32 unsigned 32 bit quantity + L9BOOL quantity capable of holding the values + TRUE (1) and FALSE (0) + +Also the defines FILE_DELIM (the file delimiter character of your file +system, usually '/') and MAX_PATH (the maximum length of the full pathname +of a file) must be set, e.g. + + #define FILE_DELIM '/' + #define MAX_PATH 256 + + +It is required that several os_ functions be written for your system. Given +below is a guide to these functions, and a very simple interface is included +in generic.c. + + +void os_printchar(char c) + + os_printchar() prints a character to the output. The interface + can either buffer this character or print it immediately, but + if buffering is used then the characters must all be sent to the + output when the interpreter calls os_flush(). A paragraph of + text is output as one long stream of characters, without line + breaks, so the interface must provide its own word wrapping, and + any other features that are desired, such as justification or a + [More] prompt. The carriage return character is always '\r', + rather than '\n'. + + +L9BOOL os_input (char *ibuff, int size) + + os_input() reads a line of text from the user, usually to accept + the next command to be sent to the game. The text input must be + stored in ibuff with a terminating zero, and be no longer than + size characters. Normally os_input() should return TRUE, but may + return FALSE to cause the entire input so far to be discarded. + The reason for doing so is discussed in the section at the end + on allowing the interpreter to load a new game without exiting. + + +char os_readchar (void) + + os_readchar() looks to see if a key has been pressed and returns + the character to the interpreter immediately. If no key has been + pressed 0 should be returned. This is most commonly used when a + game is exited, causing it to print "Press SPACE to play again" + and then call os_readchar(). + + +void os_flush (void) + + If the calls to os_printchar() are being buffered by the + interface then the buffered text must be printed when os_flush() + is called. + + +L9BOOL os_save_file (L9BYTE * Ptr, int Bytes) + + os_save_file() should prompt the user in some way (with either + text or a file requester) for a filename to save the area of + memory of size Bytes pointed to by Ptr. TRUE or FALSE should be + returned depending on whether or not the operation was successful. + + +L9BOOL os_load_file (L9BYTE * Ptr, int * Bytes, int Max) + + os_load_file() should prompt the user for the name of a file to + load. At most Max bytes should be loaded into the memory pointed + to by Ptr, and the number of bytes read should be placed into the + variable pointed to by Bytes. + + +L9BOOL os_get_game_file(char *NewName,int Size) + + os_get_game_file() should prompt the user for a new game file, to + be stored in NewName, which can take a maximum name of Size + characters. This is used in the Adrian Mole games (and possibly + others) which load in the next part of the game after the part + currently being played has completed. These games were originally + written for tape-based systems where the call was simply "load + the next game from the tape". + + +void os_set_filenumber(char *NewName,int Size,int n) + + os_set_filename() is for multi-part games originally written for + disk-based systems, which used game filenames such as + + gamedat1.dat gamedat2.dat + + etc. The routine should take the full filename in NewName (of + maximum size Size) and modify it to reflect the number n, e.g. + os_set_filename("gamedat1.dat",2) should leave "gamedat2.dat" + in NewName. + + +int main (int argc, char **argv) + + You must provide your own main() entry point for the program. + The simplest such main() is given in generic.c, which just calls + LoadGame() and then sits in a loop calling RunGame(). These + functions are discussed below. + + +The interpreter provides several functions to be called by the interface +code. These are: + + +L9BOOL LoadGame (char *filename) + + LoadGame() attempts to load filename and then searches it for + a valid Level 9 game. If it is successful TRUE is returned, else + FALSE. The previous game in memory will be overwritten if the + file filename can be loaded, even if it does not contain a Level 9 + game, so even if LoadGame() returns FALSE it must be assumed that + the game memory has changed. + + +L9BOOL RunGame (void) + + If LoadGame() has been successful, RunGame() can be called to run + the Level 9 game. Each call to RunGame() executes a single opcode + of the game. In pre-emptive multitasking systems or systems without + any multitasking it is enough to sit in a loop calling RunGame(), + i.e. + while (RunGame()); + + RunGame() returns TRUE if an opcode code was executed and FALSE if + the game is stopped, either by an error or by a call to StopGame(). + + +void StopGame (void) + + StopGame() stops the current game from playing. + + +void FreeMemory (void) + + FreeMemory() frees memory used to store the game. This routine + should be called when exiting the interpreter. + + +One more complex feature of the interpreter is that a new Level 9 game can +be loaded without exiting and restarting the interpreter. This is of use +in a windowing environment. In this case, both main() and the code that +catches a "New Game" menu item should call a routine such as the example +new_game() below. This ensures that each new game does not use up more and +more of the interpreter's stack. + +int newgame (char *game_name) +{ +static int playing = FALSE; + + if (LoadGame (game)) + { + if (playing) return -1; + playing = TRUE; + while (RunGame ()) + playing = FALSE; + } + else warn ("Unable to load game"); + return -1; +} +