#if 0 gcc -s -O2 -c -Wno-unused-result class.c `sdl-config --cflags` exit #endif /* This program is part of Free Hero Mesh and is public domain. */ #define HEROMESH_CLASS #include "SDL.h" #include #include #include #include "sqlite3.h" #include "smallxrm.h" #include "heromesh.h" #define TF_COMMA 0x0001 // has a comma modifier #define TF_EQUAL 0x0002 // has an equal sign modifier #define TF_ABNORMAL 0x0004 // with TF_NAME, not directly an opcode #define TF_COMPAT 0x0008 // add 1 to opcode in compatibility mode #define TF_DIR 0x0010 // token is a direction #define TF_DROP 0x0020 // add 0x2000 to opcode if followed by OP_DROP #define TF_NAME 0x0080 // token is a name or opcode #define TF_KEY 0x0100 // token is a Hero Mesh key name #define TF_OPEN 0x0400 // token is ( #define TF_CLOSE 0x0800 // token is ) #define TF_INT 0x1000 // token is a number #define TF_FUNCTION 0x2000 // token is a user function #define TF_MACRO 0x4000 // token is a macro #define TF_EOF 0x8000 // end of file typedef struct { const char*txt; Uint32 num; } Op_Names; #include "instruc.h" #define Tokenf(x) (tokent&(x)) Value globals[0x800]; Value initglobals[0x800]; Class*classes[0x4000]; const char*messages[0x4000]; Uint16 functions[0x4000]; int max_animation=32; Sint32 max_volume=10000; Uint8 back_color=1; char**stringpool; #define HASH_SIZE 8888 #define LOCAL_HASH_SIZE 5555 typedef struct { Uint16 id; char*txt; } Hash; typedef struct InputStack { FILE*classfp; int linenum; struct InputStack*next; } InputStack; typedef struct TokenList { Uint16 t; Uint32 v; char*str; Uint16 ref; struct TokenList*next; } TokenList; typedef struct MacroStack { TokenList*tok; Sint16 n; TokenList**args; struct MacroStack*next; } MacroStack; static FILE*classfp; static Uint16 tokent; static Uint32 tokenv; static int linenum=1; static char tokenstr[0x2000]; static int undef_class=1; static int undef_message=0; static int num_globals=0; static int num_functions=0; static int num_strings=0; static char pushback=0; static Hash*glohash; static InputStack*inpstack; static MacroStack*macstack; static TokenList**macros; #define ParseError(a,...) fatal("On line %d: " a,linenum,##__VA_ARGS__) static const unsigned char chkind[256]={ ['$']=1, ['!']=1, ['\'']=1, ['#']=1, ['@']=1, ['%']=1, ['&']=1, [':']=1, ['0'...'9']=2, ['-']=2, ['+']=2, ['A'...'Z']=3, ['a'...'z']=3, ['_']=3, ['?']=3, ['.']=3, ['*']=3, ['/']=3, }; #define MIN_VERSION 0 #define MAX_VERSION 0 #define MAX_MACRO 0x3FC0 #define MAC_ADD 0xFFC0 #define MAC_SUB 0xFFC1 #define MAC_MUL 0xFFC2 #define MAC_DIV 0xFFC3 #define MAC_MOD 0xFFC4 #define MAC_BAND 0xFFC5 #define MAC_BOR 0xFFC6 #define MAC_BXOR 0xFFC7 #define MAC_BNOT 0xFFC8 #define MAC_CAT 0xFFC9 #define MAC_VERSION 0xFFE0 #define MAC_DEFINE 0xFFE1 #define MAC_INCLUDE 0xFFE2 #define MAC_CALL 0xFFE3 #define MAC_UNDEFINED 0xFFFF static TokenList*add_macro(void) { TokenList*o=malloc(sizeof(TokenList)); if(!o) fatal("Allocation failed\n"); o->t=tokent; o->v=tokenv; o->str=0; o->ref=0; o->next=0; if(*tokenstr) { o->str=strdup(tokenstr); if(!o->str) fatal("Allocation failed\n"); } return o; } static void ref_macro(TokenList*o) { while(o) { if(++o->ref==65000) ParseError("Reference count of macro token list overflowed\n"); o=o->next; } } static void free_macro(TokenList*o) { TokenList*p; while(o && !o->ref--) { free(o->str); o=(p=o)->next; free(p); } } static inline TokenList*step_macro(TokenList*o) { TokenList*p=o->next; if(!o->ref--) { free(o->str); free(o); } return p; } static void read_quoted_string(void) { int c,i,n; int isimg=0; for(i=0;i<0x1FFD;) { c=fgetc(classfp); if(c==EOF) { ParseError("Unterminated string literal\n"); } else if(c=='\\') { if(isimg) { isimg=0; tokenstr[i++]=c; } else switch(c=fgetc(classfp)) { case '"': case '\\': tokenstr[i++]=c; break; case '0' ... '7': tokenstr[i++]=c+1-'0'; break; case 'b': tokenstr[i++]=15; break; case 'c': tokenstr[i++]=12; break; case 'i': tokenstr[i++]=14; isimg=1; break; case 'l': tokenstr[i++]=11; break; case 'n': tokenstr[i++]=10; break; case 'q': tokenstr[i++]=16; break; case 'x': c=fgetc(classfp); if(c>='0' && c<='9') n=c-'0'; else if(c>='A' && c<='F') n=c+10-'A'; else if(c>='a' && c<='f') n=c+10-'a'; else ParseError("Invalid string escape: \\x%c\n",c); n<<=4; c=fgetc(classfp); if(c>='0' && c<='9') n|=c-'0'; else if(c>='A' && c<='F') n|=c+10-'A'; else if(c>='a' && c<='f') n|=c+10-'a'; else ParseError("Invalid string escape: \\x%X%c\n",n>>4,c); if(n<32) tokenstr[i++]=31; tokenstr[i++]=n; break; default: ParseError("Invalid string escape: \\%c\n",c); } } else if(c=='"') { if(isimg) ParseError("Unterminated \\i escape in string literal\n"); tokenstr[i]=0; return; } else if(c!='\r') { tokenstr[i++]=c; if(c=='\n') ++linenum; } } ParseError("Too long string literal\n"); } static int name_compar(const void*a,const void*b) { const Op_Names*x=a; const Op_Names*y=b; return strcmp(x->txt,y->txt); } static Class*initialize_class(int n,int f,const char*name) { Class*cl; if(classes[n]) fatal("Duplicate class number %d\n",n); cl=classes[n]=malloc(sizeof(Class)); if(!cl) fatal("Allocation failed\n"); cl->name=strdup(name); if(!cl->name) fatal("Allocation failed\n"); cl->edithelp=cl->gamehelp=0; cl->codes=cl->messages=cl->images=0; cl->height=cl->weight=cl->climb=cl->density=cl->volume=cl->strength=cl->arrivals=cl->departures=0; cl->temperature=cl->misc4=cl->misc5=cl->misc6=cl->misc7=0; cl->uservars=cl->hard[0]=cl->hard[1]=cl->hard[2]=cl->hard[3]=cl->nmsg=0; cl->oflags=cl->sharp[0]=cl->sharp[1]=cl->sharp[2]=cl->sharp[3]=0; cl->shape=cl->shovable=cl->collisionLayers=cl->nimages=0; cl->cflags=f; if(undef_class<=n) undef_class=n+1; } static int look_class_name(void) { int i; int u=undef_class; for(i=1;iname,tokenstr)) { if(classes[i]->cflags&CF_NOCLASS2) classes[i]->cflags|=CF_NOCLASS1; return i; } } else { u=i; } } if(u==0x4000) ParseError("Too many class names; cannot add $%s\n",tokenstr); initialize_class(u,CF_NOCLASS1,tokenstr); return u; } static int look_message_name(void) { int i; int u=undef_message; for(i=0;i=minv && tbl[h].id<=maxv && !strcmp(tokenstr,tbl[h].txt)) return tbl[h].id; if(!tbl[h].id) { if(newv>maxv) ParseError("Too many %s\n",err); tbl[h].txt=strdup(tokenstr); if(!tbl[h].txt) ParseError("Out of string space in hash table\n"); tbl[h].id=newv; return 0; } h=(h+1)%size; if(h==m) ParseError("Hash table full\n"); } } static Uint16 look_hash_mac(void) { int h,i,m; for(h=i=0;tokenstr[i];i++) h=(13*h+tokenstr[i])%HASH_SIZE; m=h; for(;;) { if(glohash[h].id>=0xC000 && !strcmp(tokenstr,glohash[h].txt)) return h; if(!glohash[h].id) { glohash[h].txt=strdup(tokenstr); if(!glohash[h].txt) ParseError("Out of string space in hash table\n"); glohash[h].id=0xFFFF; return h; } h=(h+1)%HASH_SIZE; if(h==m) ParseError("Hash table full\n"); } } #define ReturnToken(x,y) do{ tokent=x; tokenv=y; return; }while(0) static void nxttok1(void) { int c,i,fl,n,pr; magain: if(macstack) { TokenList*tl=0; pr=0; tokent=macstack->tok->t; if(tokent&TF_EOF) ParseError("Unexpected end of file in macro expansion\n"); tokenv=macstack->tok->v; *tokenstr=0; if(macstack->tok->str) strcpy(tokenstr,macstack->tok->str); if(tokent==TF_MACRO+TF_INT && macstack->n>=0) { if(tokenv&~0xFF) { tokenv-=0x100; } else { if(tokenvn) tl=macstack->args[tokenv]; if(tl) ref_macro(tl); pr=1; } } macstack->tok=step_macro(macstack->tok); if(!macstack->tok) { MacroStack*ms=macstack->next; for(i=0;in;i++) free_macro(macstack->args[i]); free(macstack->args); free(macstack); macstack=ms; } if(pr) { if(tl) { MacroStack*ms=malloc(sizeof(MacroStack)); if(!ms) fatal("Allocation failed\n"); ms->tok=tl; ms->n=-1; ms->args=0; ms->next=macstack; macstack=ms; } goto magain; } return; } fl=n=pr=0; tokent=tokenv=0; *tokenstr=0; again: c=fgetc(classfp); while(c==' ' || c=='\t' || c=='\r' || c=='\n') { if(c=='\n') ++linenum; c=fgetc(classfp); } if(c==EOF) ReturnToken(TF_EOF,0); if(c==';') { while(c!=EOF && c!='\n') c=fgetc(classfp); ++linenum; goto again; } if(c=='(') ReturnToken(TF_OPEN,0); if(c==')') ReturnToken(TF_CLOSE,0); if(c=='"') { tokent=TF_NAME|TF_ABNORMAL; tokenv=OP_STRING; read_quoted_string(); return; } if(c=='=') { fl|=TF_EQUAL; c=fgetc(classfp); } if(c==',') { fl|=TF_COMMA; c=fgetc(classfp); } if(c=='{') { c=fgetc(classfp); while(c==' ' || c=='\t' || c=='\r' || c=='\n') { if(c=='\n') ++linenum; c=fgetc(classfp); } while(c>0 && (chkind[c]&2)) { if(n==256) { tokenstr[256]=0; ParseError("Identifier too long: %s\n",tokenstr); } tokenstr[n++]=c; c=fgetc(classfp); } tokenstr[n]=0; if(!n) fatal("Expected macro name\n"); if(c!=EOF) ungetc(c,classfp); ReturnToken(TF_MACRO|TF_OPEN,look_hash_mac()); } if(c=='}') ReturnToken(TF_MACRO|TF_CLOSE,0); if(c=='|') ReturnToken(TF_MACRO,1); if(c=='\\') { i=0; while((c=fgetc(classfp))=='\\') i+=0x100; tokenv=0; for(;c>='0' && c<='9';c=fgetc(classfp)) tokenv=10*tokenv+c-'0'; --tokenv; if(tokenv&~255) ParseError("Macro parameter reference out of range\n"); if(c!=EOF) ungetc(c,classfp); tokent=TF_MACRO|TF_INT; tokenv|=i; return; } if(c==EOF) ParseError("Unexpected end of file\n"); if(chkind[c]==1) { pr=c; c=fgetc(classfp); if(c==EOF) ParseError("Unexpected end of file\n"); } while(c>0 && (chkind[c]&2)) { if(n==256) { tokenstr[256]=0; ParseError("Identifier too long: %s\n",tokenstr); } tokenstr[n++]=c; c=fgetc(classfp); } tokenstr[n]=0; if(!n) fatal("Expected identifier\n"); if(c!=EOF) ungetc(c,classfp); switch(pr) { case 0: if(chkind[c=*tokenstr]==2) { char*e; if(c=='-' || c=='+') n=1; else n=0; if(n && !tokenstr[1]) goto norm; tokent=TF_INT; if(fl) ParseError("Invalid use of , and = in token\n"); if(c=='0' && tokenstr[1]=='x') { tokenv=strtol(tokenstr+2,&e,16); } else if(c=='0' && tokenstr[1]=='o') { tokenv=strtol(tokenstr+2,&e,8); } else { tokenv=strtol(tokenstr+n,&e,10); } if(e && *e) ParseError("Invalid token: %s\n",tokenstr); if(c=='-') tokenv=-tokenv; } else { static Op_Names key={tokenstr}; const Op_Names*op; norm: op=bsearch(&key,op_names,N_OP_NAMES,sizeof(Op_Names),name_compar); if(!op) ParseError("Invalid token: %s\n",tokenstr); tokenv=op->num&0xFFFF; tokent=op->num>>16; if(fl&~tokent) ParseError("Invalid use of , and = in token: %s\n",tokenstr); if(fl&TF_COMMA) tokenv+=0x0800; if(fl&TF_EQUAL) tokenv+=0x1000; tokent&=fl|~(TF_COMMA|TF_EQUAL); } break; case '$': if(fl) ParseError("Invalid use of , and = in token\n"); tokent=TF_NAME; tokenv=look_class_name()+0x4000; break; case '!': // Just ignore sounds for now if(fl) ParseError("Invalid use of , and = in token\n"); tokent=TF_NAME; tokenv=0x0400; break; case '%': if(fl&TF_COMMA) ParseError("Invalid use of , in token\n"); tokent=TF_NAME|TF_ABNORMAL|fl; tokenv=OP_LOCAL; break; case '@': if(fl&TF_COMMA) ParseError("Invalid use of , in token\n"); tokent=TF_NAME|fl; tokenv=look_hash(glohash,HASH_SIZE,0x2800,0x2FFF,num_globals+0x2800,"user global variables")?:(num_globals++)+0x2800; break; case '#': if(fl) ParseError("Invalid use of , and = in token\n"); tokent=TF_NAME; tokenv=look_message_name()+0xC000; break; case ':': tokent=TF_NAME|TF_ABNORMAL|fl; tokenv=OP_LABEL; break; case '&': if(fl) ParseError("Invalid use of , and = in token\n"); tokent=TF_FUNCTION|TF_ABNORMAL; tokenv=look_hash(glohash,HASH_SIZE,0x8000,0xBFFF,num_functions,"user functions")?:(num_functions++)+0x8000; break; case '\'': if(fl) ParseError("Invalid use of , and = in token\n"); tokent=TF_NAME|TF_KEY; for(i=8;i<256;i++) { if(heromesh_key_names[i] && !strcmp(heromesh_key_names[i],tokenstr)) { tokenv=i; return; } } ParseError("Invalid Hero Mesh key name: %s\n",tokenstr); break; } } static void define_macro(Uint16 name) { int i; TokenList**t; if(glohash[name].id<0xC000) fatal("Confusion\n"); if(glohash[name].id65000) ParseError("Too much macro nesting\n"); if(tokent==TF_MACRO+TF_CLOSE && !--i) break; *t=add_macro(); t=&(*t)->next; } } static void begin_include_file(const char*name) { InputStack*nxt=inpstack; inpstack=malloc(sizeof(InputStack)); if(!inpstack) fatal("Allocation failed\n"); inpstack->classfp=classfp; inpstack->linenum=linenum; inpstack->next=nxt; linenum=1; //TODO: Use the correct directory to load the include file classfp=fopen(name,"r"); if(!classfp) ParseError("Cannot open include file \"%s\": %m\n",name); } static void begin_macro(TokenList*mac) { MacroStack*ms=malloc(sizeof(MacroStack)); TokenList**ap=0; int a=0; int b=0; int c=0; ref_macro(mac); if(!ms) fatal("Allocation failed\n"); ms->tok=mac; ms->n=0; ms->args=0; for(;;) { nxttok1(); if(tokent&TF_EOF) ParseError("Unexpected end of file in macro argument\n"); if(tokent&TF_OPEN) { ++a; if(tokent&TF_MACRO) ++c; } if(tokent&TF_CLOSE) { --a; if(tokent&TF_MACRO) --c; } if(c==-1) { if(a!=-1 && !b) ParseError("Misnested macro argument\n"); ms->next=macstack; macstack=ms; return; } else if(a==-1 && !b) { ParseError("Misnested macro argument\n"); } else if(tokent==TF_MACRO && tokenv==1 && !a && !b) { if(c) ParseError("Misnested macro argument\n"); b=1; ms->args=realloc(ms->args,++ms->n*sizeof(TokenList*)); if(!ms->args) fatal("Allocation failed\n"); ap=ms->args+ms->n-1; *ap=0; } else { if(!b && !(tokent&TF_CLOSE) && a==(tokent&TF_OPEN?1:0)) { ms->args=realloc(ms->args,++ms->n*sizeof(TokenList*)); if(!ms->args) fatal("Allocation failed\n"); ap=ms->args+ms->n-1; *ap=0; } *ap=add_macro(); ap=&((*ap)->next); } } } static void nxttok(void) { if(pushback) { pushback=0; return; } again: nxttok1(); if(tokent&TF_EOF) { if(inpstack) { InputStack s=*inpstack; free(inpstack); fclose(classfp); classfp=s.classfp; linenum=s.linenum; inpstack=s.next; goto again; } } else if(tokent&TF_MACRO) { Uint32 n; char*s; if(tokent&TF_OPEN) { call: switch(glohash[tokenv].id) { case 0xC000 ... MAX_MACRO+0xC000-1: begin_macro(macros[glohash[tokenv].id-0xC000]); goto again; case MAC_ADD: n=0; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) break; if(tokent!=TF_INT) ParseError("Number expected\n"); n+=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_SUB: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); n-=tokenv; nxttok(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); tokent=TF_INT; tokenv=n; break; case MAC_MUL: n=1; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) break; if(tokent!=TF_INT) ParseError("Number expected\n"); n*=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_DIV: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(!tokenv) ParseError("Division by zero\n"); n/=tokenv; nxttok(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); tokent=TF_INT; tokenv=n; break; case MAC_MOD: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(!tokenv) ParseError("Division by zero\n"); n%=tokenv; nxttok(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); tokent=TF_INT; tokenv=n; break; case MAC_BAND: n=-1; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) break; if(tokent!=TF_INT) ParseError("Number expected\n"); n&=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_BOR: n=0; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) break; if(tokent!=TF_INT) ParseError("Number expected\n"); n|=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_BXOR: n=0; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) break; if(tokent!=TF_INT) ParseError("Number expected\n"); n^=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_BNOT: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); n=~tokenv; nxttok(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); tokent=TF_INT; tokenv=n; break; case MAC_CAT: s=malloc(0x2000); if(!s) fatal("Allocation failed\n"); n=0; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) { break; } else if(tokent==TF_INT) { sprintf(tokenstr,"%d",tokenv); goto isname; } else if(tokent&TF_NAME) { isname: if(strlen(tokenstr)+n>=0x1FFE) ParseError("Concatenated string too long\n"); strcpy(s+n,tokenstr); n+=strlen(tokenstr); } else if(tokent!=TF_MACRO || tokenv!=1) { ParseError("Number or string or name expected\n"); } } s[n]=0; strcpy(tokenstr,s); free(s); ReturnToken(TF_NAME|TF_ABNORMAL,OP_STRING); case MAC_VERSION: nxttok1(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenvMAX_VERSION) ParseError("Version number out of range\n"); nxttok1(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); goto again; case MAC_DEFINE: if(!macros) { macros=calloc(MAX_MACRO,sizeof(TokenList*)); if(!macros) fatal("Allocation failed\n"); } nxttok(); if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("String literal expected\n"); define_macro(look_hash_mac()); goto again; case MAC_INCLUDE: if(macstack) ParseError("Cannot use {include} inside of a macro\n"); nxttok1(); if(tokent==TF_MACRO && tokenv==1) ReturnToken(TF_EOF,0); if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("String literal expected\n"); n=look_hash_mac(); nxttok1(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); begin_include_file(glohash[n].txt); goto again; case MAC_CALL: nxttok(); if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("String literal expected\n"); tokenv=look_hash_mac(); goto call; case MAC_UNDEFINED: ParseError("Undefined macro: {%s}\n",tokenstr); break; default: ParseError("Strange macro token: 0x%04X\n",glohash[tokenv].id); } } } } static int pool_string(const char*s) { int i; for(i=0;i32768) ParseError("Too many string literals\n"); stringpool=realloc(stringpool,num_strings*sizeof(stringpool)); if(!stringpool) fatal("Allocation failed\n"); stringpool[i]=strdup(s); if(!stringpool[i]) fatal("Allocation failed\n"); return i; } static Value parse_constant_value(void) { if(tokent==TF_INT) { return NVALUE(tokenv); } else if(Tokenf(TF_NAME)) { switch(tokenv) { case 0x0000 ... 0x00FF: return NVALUE(tokenv); case 0x0100 ... 0x01FF: return NVALUE(tokenv-0x0200); case 0x0200 ... 0x02FF: return MVALUE(tokenv&255); case 0x0300 ... 0x03FF: return UVALUE(0,TY_SOUND); case 0x0400 ... 0x04FF: return UVALUE(0,TY_USOUND); case 0x4000 ... 0x7FFF: return CVALUE(tokenv-0x4000); case OP_STRING: return UVALUE(pool_string(tokenstr),TY_STRING); case OP_BITCONSTANT ... OP_BITCONSTANT_LAST: return NVALUE(1<<(tokenv&31)); case 0xC000 ... 0xFFFF: return MVALUE(tokenv-0xBF00); } } ParseError("Constant value expected\n"); } #define AddInst(x) (cl->codes[ptr++]=(x),prflag=0) #define AddInst2(x,y) (cl->codes[ptr++]=(x),cl->codes[ptr++]=(y),prflag=0,peep=ptr) #define AddInstF(x,y) (cl->codes[ptr++]=(x),prflag=(y)) static int parse_instructions(int cla,int ptr,Hash*hash) { int peep=ptr; int prflag=0; Class*cl=classes[cla]; cl->codes=realloc(cl->codes,0x10000*sizeof(Uint16)); if(!cl->codes) fatal("Allocation failed\n"); for(;;) { nxttok(); if(Tokenf(TF_MACRO)) ParseError("Unexpected macro\n"); if(Tokenf(TF_INT)) { } else if(Tokenf(TF_NAME)) { switch(tokenv) { default: if(Tokenf(TF_ABNORMAL)) ParseError("Invalid instruction token\n"); AddInstF(tokenv,tokent); } } else if(Tokenf(TF_FUNCTION)) { AddInst2(OP_FUNCTION,tokenv&0x3FFF); } else if(tokent==TF_OPEN) { nxttok(); if(Tokenf(TF_MACRO) || !Tokenf(TF_NAME)) ParseError("Invalid parenthesized instruction\n"); switch(tokenv) { default: ParseError("Invalid parenthesized instruction\n"); } } else if(tokent==TF_CLOSE) { if(peepcodes[ptr-1]<0x0100) cl->codes[ptr-1]+=0x1E00; else AddInst(OP_RET); break; } else { ParseError("Invalid instruction token\n"); } if(ptr>=0xFFEF) ParseError("Out of code space\n"); } cl->codes=realloc(cl->codes,ptr*sizeof(Uint16))?:cl->codes; return ptr; } void load_classes(void) { int i; int gloptr=0; Hash*glolocalhash; char*nam=sqlite3_mprintf("%s.class",basefilename); if(!nam) fatal("Allocation failed\n"); classfp=fopen(nam,"r"); sqlite3_free(nam); if(!classfp) fatal("Cannot open class file '%s': %m\n",nam); glohash=calloc(HASH_SIZE,sizeof(Hash)); if(!glohash) fatal("Allocation failed\n"); glolocalhash=calloc(LOCAL_HASH_SIZE,sizeof(Hash)); if(!glolocalhash) fatal("Allocation failed\n"); strcpy(tokenstr,"+"); glohash[look_hash_mac()].id=MAC_ADD; strcpy(tokenstr,"-"); glohash[look_hash_mac()].id=MAC_SUB; strcpy(tokenstr,"*"); glohash[look_hash_mac()].id=MAC_MUL; strcpy(tokenstr,"/"); glohash[look_hash_mac()].id=MAC_DIV; strcpy(tokenstr,"mod"); glohash[look_hash_mac()].id=MAC_MOD; strcpy(tokenstr,"band"); glohash[look_hash_mac()].id=MAC_BAND; strcpy(tokenstr,"bor"); glohash[look_hash_mac()].id=MAC_BOR; strcpy(tokenstr,"bxor"); glohash[look_hash_mac()].id=MAC_BXOR; strcpy(tokenstr,"bnot"); glohash[look_hash_mac()].id=MAC_BNOT; strcpy(tokenstr,"cat"); glohash[look_hash_mac()].id=MAC_CAT; strcpy(tokenstr,"version"); glohash[look_hash_mac()].id=MAC_VERSION; strcpy(tokenstr,"define"); glohash[look_hash_mac()].id=MAC_DEFINE; strcpy(tokenstr,"include"); glohash[look_hash_mac()].id=MAC_INCLUDE; strcpy(tokenstr,"call"); glohash[look_hash_mac()].id=MAC_CALL; if(main_options['L']) { for(;;) { nxttok(); printf("** %5d %04X %08X \"",linenum,tokent,tokenv); for(i=0;tokenstr[i];i++) { if(tokenstr[i]<32 || tokenstr[i]>126) printf("<%02X>",tokenstr[i]&255); else putchar(tokenstr[i]); } printf("\"\n"); if(Tokenf(TF_EOF)) goto done; } } *classes=malloc(sizeof(Class)); if(!*classes) fatal("Allocation failed\n"); classes[0]->name=classes[0]->edithelp=classes[0]->gamehelp=0; classes[0]->codes=classes[0]->messages=classes[0]->images=0; classes[0]->nmsg=0; memset(functions,-1,sizeof(functions)); for(;;) { nxttok(); if(tokent==TF_EOF) goto done; if(tokent!=TF_OPEN) ParseError("Expected open parenthesis\n"); nxttok(); if(Tokenf(TF_FUNCTION)) { } else if(Tokenf(TF_NAME)) { switch(tokenv) { case 0x4000 ... 0x7FFF: // Class definition break; case 0x0200 ... 0x02FF: case 0xC000 ... 0xFFFF: // Default message handler break; case 0x2800 ... 0x2FFF: // Define initial values of global variables i=tokenv-0x2800; nxttok(); if(tokent==TF_CLOSE) break; initglobals[i]=parse_constant_value(); nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_BACKGROUND: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv&~255) ParseError("Background color out of range\n"); back_color=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_ANIMATE: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv<1 || tokenv>256) ParseError("Number of max animation steps out of range\n"); max_animation=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_VOLUME: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); max_volume=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; default: ParseError("Invalid top level definition: %s\n",tokenstr); } } else { ParseError("Invalid top level definition\n"); } } done: fclose(classfp); if(main_options['H']) { for(i=0;icflags&CF_NOCLASS1)) fatal("Class $%s mentioned but not defined\n",classes[i]->name); if(macros) for(i=0;i