#if 0 gcc ${CFLAGS:--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 <stdio.h> #include <stdlib.h> #include <string.h> #include "sqlite3.h" #include "smallxrm.h" #include "heromesh.h" #include "names.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 initglobals[0x800]; Class*classes[0x4000]; const char*messages[0x4000]; Uint16 functions[0x4000]; int max_animation=32; Sint32 max_volume=10000; Uint8 back_color=1; Uint8 inv_back_color=9; char**stringpool; AnimationSlot anim_slot[8]; Uint8 keymask[256/8]; Uint16 array_size; Uint16*orders; Uint8 norders; Uint16 control_class; Uint8 has_xy_input; Uint8 has_mbcs=0; char*ll_head; DisplayColumn*ll_disp; Uint8 ll_ndisp; DataColumn*ll_data; Uint8 ll_ndata; Uint8 ll_naggregate; Uint16*ll_code; #ifdef CONFIG_GLOBAL_HASH_SIZE #define HASH_SIZE CONFIG_GLOBAL_HASH_SIZE #else #define HASH_SIZE 8888 #endif #ifdef CONFIG_LOCAL_HASH_SIZE #define LOCAL_HASH_SIZE CONFIG_LOCAL_HASH_SIZE #else #define LOCAL_HASH_SIZE 5555 #endif typedef struct { Uint16 id; char*txt; } Hash; /* Global hash: 1000-10FF = User flags 2800-2FFF = Variables 8000-BFFF = Functions C000-FFFF = Macros Local hash: 2000-27FF = Variables 8000-FFFF = Labels */ 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; typedef struct LabelStack { Uint16 id; Uint16 addr; struct LabelStack*next; } LabelStack; 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; static LabelStack*labelstack; static Uint16*labelptr; static Uint8 dense[8]; #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, ['^']=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_BIT 0xFFCA #define MAC_MAKE 0xFFCB #define MAC_VERSION 0xFFE0 #define MAC_DEFINE 0xFFE1 #define MAC_INCLUDE 0xFFE2 #define MAC_CALL 0xFFE3 #define MAC_APPEND 0xFFE4 #define MAC_EDIT 0xFFE5 #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; } #ifndef CONFIG_OMIT_MBCS static inline void valid_part_of_code(Uint8 b) { if(b<0x21 || b>0xFD || b==0x7F) ParseError("Improper TRON code\n"); } static Uint32 read_euc_tron_1(const Uint8*s,int*p) { Uint32 r=0; Uint8 h=0; int i=*p; int c=s[i++]; if(c==0x90 || c==0x91) h=c,c=s[i++]; while(c==0x80) r+=0x1000000,c=s[i++]; if(c<0xA1) ParseError("Improper TRON code\n"); if(h) { if(h==0x90) c-=0x40; r|=c<<16; } else { r|=(c-0x80)<<16; } c=s[i++]; if(c<0xA0) ParseError("Improper TRON code\n"); r|=(c<<8)|s[i++]; *p=i; return r; } static void convert_euctron_to_tron8(void) { static const Uint8 jisx[0x80]={ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xA1, 0xA3,0xA4,0xA5,0xA8, 0xAC,0xAD,0xAE,0xAF, 0xEE,0xEF,0xF0,0xF1, 0xF2,0xF3,0xF4,0xF5, 0xF6,0xF7,0xF8,0xF9, 0xFA,0xFB,0xFC,0xFD, 0xFE,0x87,0x00,0x88, 0x89,0x8A,0x00,0x00, 0x8B,0x00,0x00,0x00, 0x8C,0x8D,0x8E,0x8F, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x90,0x91, 0x92,0x93,0x94,0x95, 0x96,0x97,0x98,0x99, 0x9A,0x9B,0x9C,0x9D, 0x9E,0x9F,0xA0,0x00, }; Uint8 s[0x2000]; int i,o,p,x,y; Uint32 c; strcpy(s,tokenstr); i=o=p=0; if(*s<=0x20) tokenstr[0]=0xFE,tokenstr[1]=p=0x21,o=2; while(i<0x1FFF && s[i] && o<0x1FFD) { if(s[i]==27) { i++; c=0; for(;;) switch(x=s[i++]) { case '0' ... '9': c=16*c+x-'0'; break; case 'A' ... 'F': c=16*c+x+10-'A'; break; case 'a' ... 'f': c=16*c+x+10-'a'; break; case ';': goto code; default: ParseError("Invalid \\T escape\n"); } } else if(s[i]<=32) { tokenstr[o++]=c=s[i++]; if(c==16 || c==31) { if(!s[i]) ParseError("Invalid string\n"); tokenstr[o++]=s[i++]; } else if(c==14 || c==30) { x=strcspn(s+i,"\\"); if(!s[i+x]) ParseError("Invalid string\n"); if(o+x+1>=0x1FFC) ParseError("Converted string too long\n"); memcpy(tokenstr+o,s+i,x+1); o+=x+1; } } else if(s[i]=='%') { tokenstr[o++]=0x7F; i++; x=s[i++]; if(!x) break; tokenstr[o++]=x; p=0; // ensure not being shifted to the wrong code set after a substitution } else if(s[i]<127) { // Note that the non-official plane 0x70 is used, rather than conversion to JIS or Unicode. // This might be changed in future to convert to JIS. c=0x702200+s[i++]; goto code; } else if(s[i]>=0x88 && s[i]<=0x8D) { x=s[i++]; c=read_euc_tron_1(s,&i); y=(c>>8)&0xFF; switch(x) { case 0x88: c-=0x8080; break; case 0x89: c-=0x0080; break; case 0x8A: c-=0x8000; break; case 0x8B: break; case 0x8C: c=(y<0xC0?c-0x2080:(c&~0xFFFF)+((c>>8)&0xFF)+(y<<8)-0x8040); break; case 0x8D: c=(y<0xC0?c-0x2000:y<0xE0?(c&~0xFFFF)+((c>>8)&0xFF)+(y<<8)-0x0040:c-0x6060); break; } goto code; } else if(s[i]==0x8E) { // Half width katakana; see comments in the (s[i]<127) block c=0x702300+s[i++]; goto code; } else if(s[i]==0x8F) { i++; if(!s[i] || !s[i+1]) break; x=s[i++]; if(!x) ParseError("Invalid TRON code\n"); c=s[i++]^((jisx[x&0x7F]?:x)<<8)^0x210080; goto code; } else { c=0x210000+(s[i++]<<8); if(s[i]<0x80) ParseError("Invalid EUC-JP character\n"); c+=s[i++]-0x8080; code: valid_part_of_code(c&0xFF); valid_part_of_code((c>>8)&0xFF); valid_part_of_code((c>>16)&0xFF); if((c>>16)!=p) { if(o+(c>>24)>=0x1FFB) ParseError("Converted string too long\n"); memset(tokenstr+o,0xFE,x=(c>>24)+1); o+=x; tokenstr[o++]=c>>16; } tokenstr[o++]=c>>8; tokenstr[o++]=c; } } tokenstr[o]=0; } #endif // CONFIG_OMIT_MBCS static void read_quoted_string(void) { char y=0; 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 'd': tokenstr[i++]=30; isimg=1; 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?:255; break; #ifndef CONFIG_OMIT_MBCS case 'T': if(has_mbcs) { y=1; tokenstr[i++]=27; break; } // else fall through #endif default: ParseError("Invalid string escape: \\%c\n",c); } } else if(c=='"') { if(isimg) ParseError("Unterminated \\i escape in string literal\n"); tokenstr[i]=0; #ifndef CONFIG_OMIT_MBCS if(y) convert_euctron_to_tron8(); #endif return; } else if(c!='\r') { tokenstr[i++]=c; if(c=='\n') ++linenum; #ifndef CONFIG_OMIT_MBCS if((c&0x80) && has_mbcs) y=1; #endif } } 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=cl->order=0; cl->cflags=f; if(undef_class<=n) undef_class=n+1; } Uint16 get_message_ptr(int c,int m) { if(!classes[c]) return 0xFFFF; if(classes[c]->nmsg<=m) return 0xFFFF; return classes[c]->messages[m]; } static void set_message_ptr(int c,int m,int p) { if(m>=classes[c]->nmsg) { classes[c]->messages=realloc(classes[c]->messages,(m+1)*sizeof(Uint16)); if(!classes[c]->messages) fatal("Allocation failed\n"); while(m>=classes[c]->nmsg) classes[c]->messages[classes[c]->nmsg++]=0xFFFF; } classes[c]->messages[m]=p; } static int look_class_name(void) { int i; int u=undef_class; for(i=1;i<undef_class;i++) { if(classes[i]) { if(!strcmp(classes[i]->name,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<undef_message;i++) { if(messages[i]) { if(!strcmp(messages[i],tokenstr)) return i; } else { u=i; } } if(u==0x4000) ParseError("Too many message names; cannot add #%s",tokenstr); if(u==undef_message) ++undef_message; messages[u]=strdup(tokenstr); if(!messages[u]) fatal("Allocation failed\n"); return u; } static Uint16 look_hash(Hash*tbl,int size,Uint16 minv,Uint16 maxv,Uint16 newv,const char*err) { int h,i,m; for(h=i=0;tokenstr[i];i++) h=(13*h+tokenstr[i])%size; m=h; for(;;) { if(tbl[h].id>=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"); } } static int check_improper_name(void) { unsigned char*p=tokenstr; while(*p) if(*p++<=32) return 1; return 0; } #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; if(tokent!=(TF_MACRO|TF_CLOSE)) *tokenstr=0; if(macstack->tok->str) strcpy(tokenstr,macstack->tok->str); if(main_options['M']) { printf("M* Macro %04X %08X \"",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(tokent==TF_MACRO+TF_INT && macstack->n>=0) { if(tokenv&~0xFF) { tokenv-=0x100; } else { if(tokenv<macstack->n) 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;i<macstack->n;i++) free_macro(macstack->args[i]); free(macstack->args); free(macstack); macstack=ms; if(main_options['M']) printf("M.\n"); } 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; } if(main_options['M']) printf("M^ %d\n",tl?1:0); goto magain; } if(tokent==TF_MACRO+TF_ABNORMAL) goto magain; // denotes a empty macro 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) goto norm; 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 '!': if(fl) ParseError("Invalid use of , and = in token\n"); tokent=TF_NAME; tokenv=find_user_sound(tokenstr); 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; if(fl&TF_EQUAL) tokenv+=0x1000; 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+0x8000,"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; case '^': tokent=TF_NAME|TF_ABNORMAL|fl; tokenv=OP_USERFLAG; break; } } static void define_macro(Uint16 name,Uint8 q) { int i; TokenList**t; if(main_options['M']) printf("M< %04X %04X \"%s\" %d\n",name,glohash[name].id,glohash[name].txt,q); if(glohash[name].id<0xC000) fatal("Confusion\n"); if(glohash[name].id<MAX_MACRO+0xC000) { i=glohash[name].id-0xC000; if(q) { free_macro(macros[i]); macros[i]=0; } } else { q=1; for(i=0;i<MAX_MACRO;i++) if(!macros[i]) break; if(i==MAX_MACRO) ParseError("Too many macro definitions\n"); } glohash[name].id=i+0xC000; t=macros+i; if(!q) while(*t) t=&(*t)->next; i=1; for(;;) { nxttok1(); if(main_options['L'] && main_options['M']) { int j; printf("*: %5d %04X %08X \"",i,tokent,tokenv); for(j=0;tokenstr[j];j++) { if(tokenstr[j]<32 || tokenstr[j]>126) printf("<%02X>",tokenstr[j]&255); else putchar(tokenstr[j]); } printf("\"\n"); } if(tokent==TF_MACRO+TF_OPEN && ++i>65000) ParseError("Too much macro nesting\n"); if(tokent==TF_MACRO+TF_CLOSE && !--i) break; *t=add_macro(); t=&(*t)->next; } if(main_options['M']) printf("M> %04X %04X %p\n",name,glohash[name].id,macros[glohash[name].id-0xC000]); if(!macros[glohash[name].id-0xC000]) { // The macro is empty, so add a token which denotes a empty macro. // This token will be skipped during macro expansion. tokent=TF_MACRO+TF_ABNORMAL; *tokenstr=0; macros[glohash[name].id-0xC000]=add_macro(); } } #ifndef CONFIG_OMIT_INCLUDE 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; if(main_options['S']) fatal("Cannot use {include} with level hash calculation mode\n"); if(*name=='.' || *name=='_' || *name=='-' || !*name || name[strspn(name,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-abcdefghijklmnopqrstuvwxyz.")]) ParseError("Improper name of include file \"%s\"\n",name); if(main_options['z']) { if(strlen(name)<9 || memcmp(name-strlen(name),".include",8)) ParseError("Include file name doesn't end with .include\n"); classfp=composite_slice(name,0)?:fopen(name,"r"); } else { classfp=fopen(name,"r"); } if(!classfp) ParseError("Cannot open include file \"%s\": %m\n",name); } #endif static void begin_macro(TokenList*mac) { MacroStack*ms=malloc(sizeof(MacroStack)); TokenList**ap=0; int a=0; int b=0; int c=0; if(StackProtection()) fatal("Stack overflow\n"); 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(main_options['M']) { int i; printf("M= %c%4d %04X %08X \"",b?'|':a?'^':' ',ms->n,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(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; } if(StackProtection()) fatal("Stack overflow\n"); 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: if(main_options['M']) printf("M+ {%s}\n",glohash[tokenv].txt); 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 && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); n+=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_SUB: nxttok(); if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) 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 && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); n*=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_DIV: nxttok(); if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) 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 && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) 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 && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) 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 && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) 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 && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); n^=tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_BNOT: nxttok(); if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) 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_BIT: n=0; for(;;) { nxttok(); if(tokent==TF_MACRO+TF_CLOSE) break; if(tokent!=TF_INT && tokent!=TF_NAME+TF_DIR && tokent!=TF_NAME+TF_KEY) ParseError("Number expected\n"); if(tokenv<32) n|=1<<tokenv; } tokent=TF_INT; tokenv=n; break; case MAC_MAKE: nxttok(); if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("String literal expected\n"); s=tokenstr; n=0; while(*s) switch(*s++) { case '=': n|=0x0001; break; case ',': n|=0x0002; break; case ':': n|=0x0004; break; case '@': n|=0x0008; break; case '%': n|=0x0010; break; case '#': n|=0x0020; break; case '$': n|=0x0040; break; case '&': n|=0x0080; break; case '^': n|=0x0100; break; case '\'': n|=0x0400; break; case 'D': n|=0x0800; break; case '+': n|=0x1000; break; case 'C': n|=0x2000; break; case 'A': n|=0x4000; break; case '!': n|=0x8000; break; default: ParseError("Incorrect mode of {make}\n"); } nxttok(); if(tokent==TF_INT || ((tokent&TF_NAME) && !(tokenv&~255))) { n|=0x10000; snprintf(tokenstr,32,"%llu",(long long)tokenv); } else if(!(tokent&TF_NAME) || tokenv!=OP_STRING) { ParseError("Numeric or string literal expected\n"); } s=strdup(tokenstr); if(!s) fatal("Allocation failed\n"); nxttok(); strcpy(tokenstr,s); free(s); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); if(n&0x10000) { n&=0xFFFF; tokent=TF_INT; tokenv=strtol(tokenstr,0,10); } switch(n) { case 0x0004: // : ReturnToken(TF_NAME|TF_ABNORMAL,OP_LABEL); case 0x0005: // =: ReturnToken(TF_NAME|TF_ABNORMAL|TF_EQUAL,OP_LABEL); case 0x0006: // ,: ReturnToken(TF_NAME|TF_ABNORMAL|TF_COMMA,OP_LABEL); case 0x0008: // @ tokent=TF_NAME; tokenv=look_hash(glohash,HASH_SIZE,0x2800,0x2FFF,num_globals+0x2800,"user global variables")?:(num_globals++)+0x2800; return; case 0x0009: // =@ tokent=TF_NAME|TF_EQUAL; tokenv=look_hash(glohash,HASH_SIZE,0x2800,0x2FFF,num_globals+0x2800,"user global variables")?:(num_globals++)+0x2800; return; case 0x0010: // % ReturnToken(TF_NAME|TF_ABNORMAL,OP_LOCAL); case 0x0011: // =% ReturnToken(TF_NAME|TF_ABNORMAL|TF_EQUAL,OP_LOCAL); case 0x0020: // # if(check_improper_name()) ParseError("Improper message name in {make}\n"); tokent=TF_NAME; tokenv=look_message_name()+0xC000; return; case 0x0040: // $ if(!*tokenstr || *tokenstr=='(' || check_improper_name()) ParseError("Improper class name in {make}\n"); tokent=TF_NAME; tokenv=look_class_name()+0x4000; return; case 0x0080: // & tokent=TF_FUNCTION|TF_ABNORMAL; tokenv=look_hash(glohash,HASH_SIZE,0x8000,0xBFFF,num_functions+0x8000,"user functions")?:(num_functions++)+0x8000; return; case 0x0100: // ^ ReturnToken(TF_NAME|TF_ABNORMAL,OP_USERFLAG); case 0x0101: // =^ ReturnToken(TF_NAME|TF_ABNORMAL|TF_EQUAL,OP_USERFLAG); case 0x0102: // ,^ ReturnToken(TF_NAME|TF_ABNORMAL|TF_COMMA,OP_USERFLAG); case 0x0103: // =,^ ReturnToken(TF_NAME|TF_ABNORMAL|TF_EQUAL|TF_COMMA,OP_USERFLAG); case 0x0400: // ' if(tokent==TF_INT) { n=tokenv; } else { for(n=8;n<256;n++) if(heromesh_key_names[n] && !strcmp(heromesh_key_names[n],tokenstr)) break; } if(!heromesh_key_names[n&=255]) ParseError("Invalid key token (%d)\n",n); ReturnToken(TF_NAME|TF_KEY,n); case 0x0800: // D if(tokent==TF_INT) ReturnToken(TF_NAME|TF_DIR,tokenv&15); else ReturnToken(TF_NAME|TF_DIR,strtol(tokenstr,0,10)&15); case 0x1000: // + ReturnToken(TF_INT,strtol(tokenstr,0,10)); case 0x1001: // =+ ReturnToken(TF_INT,strtol(tokenstr,0,2)); case 0x1002: // ,+ ReturnToken(TF_INT,strtol(tokenstr,0,16)); case 0x1003: // =,+ ReturnToken(TF_INT,strtol(tokenstr,0,8)); case 0x2000: // C if(tokent!=TF_INT) tokenv=strtol(tokenstr,0,10); *tokenstr=n=tokenv&255; if(n<32) tokenstr[0]=31,tokenstr[1]=n,tokenstr[2]=0; else tokenstr[1]=0; ReturnToken(TF_NAME|TF_ABNORMAL,OP_STRING); case 0x2002: // ,C if(tokent!=TF_INT) tokenv=strtol(tokenstr,0,10); if(n<32) { *tokenstr=0; } else { tokenstr[0]=16; tokenstr[1]=n; tokenstr[2]=0; } ReturnToken(TF_NAME|TF_ABNORMAL,OP_STRING); case 0x4000: // A ReturnToken(TF_INT,*tokenstr&255); default: ParseError("Incorrect mode of {make}\n"); } break; case MAC_VERSION: nxttok1(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv<MIN_VERSION || tokenv>MAX_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(),1); goto again; case MAC_INCLUDE: #ifdef CONFIG_OMIT_INCLUDE ParseError("The {include} macro has been omitted from this version\n"); #else 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; #endif case MAC_CALL: nxttok(); if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("String literal expected\n"); tokenv=look_hash_mac(); goto call; case MAC_APPEND: 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(),0); goto again; case MAC_EDIT: if(!macros) ParseError("Cannot edit nonexistent macro\n"); nxttok(); if(tokent==TF_INT) { if(!macstack) ParseError("Empty macro stack\n"); n=tokenv-1; nxttok(); if(n<0 || macstack->n<=n || !macstack->args[n]) ParseError("Cannot edit nonexistent argument %u\n",n+1); if(macstack->args[n]->t&TF_OPEN) { free_macro(macstack->args[n]->next); macstack->args[n]->next=0; } else if(macstack->args[n]->t&TF_EOF) { ParseError("Invalid edit token\n"); } free(macstack->args[n]->str); macstack->args[n]->t=tokent; macstack->args[n]->v=tokenv; macstack->args[n]->str=0; if(*tokenstr) { macstack->args[n]->str=strdup(tokenstr); if(!macstack->args[n]->str) fatal("Allocation failed\n"); } } else { if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("Numeric or string literal expected\n"); n=glohash[look_hash_mac()].id; if(n<0xC000 || n>MAX_MACRO+0xC000-1 || !macros[n-0xC000]) ParseError("Undefined macro: {%s}\n",tokenstr); nxttok(); n-=0xC000; if(macros[n]->t&(TF_MACRO|TF_EOF)) ParseError("Invalid edit token\n"); free(macros[n]->str); macros[n]->t=tokent; macros[n]->v=tokenv; macros[n]->str=0; if(*tokenstr) { macros[n]->str=strdup(tokenstr); if(!macros[n]->str) fatal("Allocation failed\n"); } } if(main_options['M']) { int i; printf("M= @%4d %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"); } nxttok(); if(tokent!=TF_MACRO+TF_CLOSE) ParseError("Too many macro arguments\n"); goto again; case MAC_UNDEFINED: ParseError("Undefined macro: {%s}\n",tokenstr); break; default: ParseError("Strange macro token: 0x%04X\n",glohash[tokenv].id); } if(main_options['M']) printf("M- %5d %04X %08X\n",linenum,tokent,tokenv); } } } static int pool_string(const char*s) { int i; for(i=0;i<num_strings;i++) if(!strcmp(s,stringpool[i])) return i; i=num_strings++; if(i>32768) 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(tokenv&255,TY_SOUND); case 0x0400 ... 0x04FF: return UVALUE(tokenv&255,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"); } static Uint32 parse_array(void) { Uint32 x,y,z; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); x=tokenv; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); y=tokenv; if(x<1 || x>64 || y<1 || y>1024) ParseError("Array dimension out of range\n"); z=array_size; if(z+x*y>0xFFFE) ParseError("Out of array memory\n"); array_size+=x*y; return z|((y-1)<<16)|((x-1)<<26); } static void define_user_flags(Uint16 ca,Uint16 cb) { for(;;) { nxttok(); if(tokent==TF_CLOSE) break; if(!Tokenf(TF_NAME) || tokenv!=OP_USERFLAG) ParseError("User flag or close parenthesis expected\n"); if(ca>cb) ParseError("Too many user flags of this kind\n"); if(look_hash(glohash,HASH_SIZE,0x1000,0x10FF,ca++,"user flags")) ParseError("Duplicate definition of ^%s\n",tokenstr); } } static void begin_label_stack(void) { labelstack=0; labelptr=malloc(0x8000*sizeof(Uint16)); if(!labelptr) fatal("Allocation failed\n"); memset(labelptr,255,0x8000*sizeof(Uint16)); *labelptr=0x8001; } static void end_label_stack(Uint16*codes,Hash*hash) { LabelStack*s; while(labelstack) { s=labelstack; if(labelptr[s->id-0x8000]==0xFFFF) { int i; for(i=0;i<LOCAL_HASH_SIZE;i++) { if(hash[i].id==s->id) ParseError("Label :%s mentioned but never defined\n",hash[i].txt); } ParseError("Label mentioned but never defined\n"); } codes[s->addr]=labelptr[s->id-0x8000]; labelstack=s->next; free(s); } free(labelptr); labelstack=0; labelptr=0; } static int parse_pattern(int cla,int ptr,Hash*hash) { Class*cl=classes[cla]; Uint8 depth=0; Uint16 nest[32]; Uint16 nest0[32]; int x,y; for(;;) { nxttok(); if(Tokenf(TF_MACRO)) ParseError("Unexpected macro\n"); if(Tokenf(TF_DIR)) { cl->codes[ptr++]=tokenv&15; } else if(Tokenf(TF_NAME)) { switch(tokenv) { case OP_ADD: case OP_CLIMB: case OP_HEIGHT: case OP_LOC: case OP_MARK: case OP_SUB: case OP_QUEEN: case OP_ROOK: case OP_BISHOP: case OP_DIR: case OP_DIR_C: case OP_DIR_E: case OP_DIR_EC: case OP_OBJTOPAT: case OP_OBJBOTTOMAT: case OP_CUT: case OP_MUL: case OP_OBJABOVE: case OP_OBJBELOW: case OP_TRACE: case OP_NEXT: case OP_IMAGE: case OP_IMAGE_C: case 0x0200 ... 0x02FF: // message case 0x4000 ... 0x7FFF: // class case 0xC000 ... 0xFFFF: // message cl->codes[ptr++]=tokenv; break; case OP_BEGIN: case OP_IF: if(depth==31) ParseError("Too much pattern nesting\n"); nest0[depth]=nest[depth]=ptr; cl->codes[ptr++]=tokenv; cl->codes[ptr++]=0; depth++; break; case OP_ELSE: if(!depth) ParseError("Premature end of subpattern\n"); cl->codes[nest[depth-1]+1]=ptr+1; cl->codes[ptr++]=OP_ELSE; cl->codes[ptr++]=cl->codes[nest[depth-1]]; cl->codes[nest[depth-1]]=OP_IF; cl->codes[ptr++]=0; nest[depth-1]=ptr-2; break; case OP_THEN: if(!depth) ParseError("Premature end of subpattern\n"); cl->codes[nest[--depth]+1]=ptr; cl->codes[ptr++]=OP_THEN; break; case OP_AGAIN: if(!depth) ParseError("Premature end of subpattern\n"); cl->codes[ptr++]=OP_AGAIN; cl->codes[ptr++]=nest0[depth-1]; cl->codes[nest[--depth]+1]=ptr; cl->codes[ptr++]=OP_THEN; break; case OP_USERFLAG: x=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!x) ParseError("User flag ^%s not defined\n",tokenstr); if(Tokenf(TF_COMMA)) x+=0x100; if(Tokenf(TF_EQUAL)) ParseError("Improper token in pattern\n"); cl->codes[ptr++]=x; break; default: ParseError("Improper token in pattern\n"); } } else if(Tokenf(TF_FUNCTION)) { cl->codes[ptr++]=OP_FUNCTION; cl->codes[ptr++]=tokenv&0x3FFF; } else if(tokent==TF_OPEN) { nxttok(); if(Tokenf(TF_MACRO) || !Tokenf(TF_NAME)) ParseError("Improper token in pattern\n"); switch(tokenv) { case OP_HEIGHT: case OP_CLIMB: cl->codes[ptr++]=tokenv+0x0800; // OP_HEIGHT_C or OP_CLIMB_C nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv&~0xFFFF) ParseError("Number out of range\n"); cl->codes[ptr++]=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parentheses expected\n"); break; default: ParseError("Improper token in pattern\n"); } } else if(tokent==TF_CLOSE) { if(depth) ParseError("Premature end of pattern\n"); cl->codes[ptr++]=OP_RET; return ptr; } else if(Tokenf(TF_EOF)) { ParseError("Unexpected end of file\n"); } else { ParseError("Improper token in pattern\n"); } if(ptr>=0xFFEF) ParseError("Out of code space\n"); } } #define CaseT0(t) do{ if(t0!=-1 && t0!=t) ParseError("Type mismatch in case block\n"); t0=t; }while(0) #define CaseT1(t) do{ if(t1!=-1 && t1!=t) ParseError("Type mismatch in case block\n"); t1=t; }while(0) static int case_block(int cla,int ptr,Hash*hash) { Class*cl=classes[cla]; int sptr=ptr++; Sint8 t0=-1; Sint8 t1=-1; Uint16 n=0; Uint8 e=1; Uint8 z=0; for(;;) { nxttok(); if(Tokenf(TF_MACRO)) ParseError("Unexpected macro\n"); if(tokent==TF_OPEN) { nxttok(); if(Tokenf(TF_MACRO)) ParseError("Unexpected macro\n"); if(n>=256 && tokenv!=OP_ELSE && !Tokenf(TF_NAME)) ParseError("Too many cases\n"); if(!e) ParseError("Cannot add more cases after the default block\n"); n++; if(Tokenf(TF_INT) || Tokenf(TF_DIR)) { if(tokenv) CaseT0(TY_NUMBER); cl->codes[ptr++]=tokenv; } else if(Tokenf(TF_NAME)) { switch(tokenv) { case OP_ELSE: e=0; n--; break; case 0x0200 ... 0x02FF: CaseT0(TY_MESSAGE); cl->codes[ptr++]=tokenv+1-0x0200; break; case 0x4000 ... 0x7FFF: CaseT0(TY_CLASS); cl->codes[ptr++]=tokenv-0x4000; break; case 0xC000 ... 0xFFFF: CaseT0(TY_MESSAGE); cl->codes[ptr++]=(tokenv&0x3FFF)+257; break; default: ParseError("Improper token in case block\n"); break; } } else if(Tokenf(TF_EOF)) { ParseError("Unexpected end of file\n"); } else { ParseError("Improper token in case block\n"); } nxttok(); if(Tokenf(TF_MACRO)) ParseError("Unexpected macro\n"); if(Tokenf(TF_INT) || Tokenf(TF_DIR)) { if(tokenv) CaseT1(TY_NUMBER); else z=1; if(tokenv&~0xFFFF) ParseError("Number in case block out of range\n"); cl->codes[ptr++]=tokenv; } else if(Tokenf(TF_NAME)) { switch(tokenv) { case OP_LABEL: CaseT1(TY_CODE); if(Tokenf(TF_COMMA|TF_EQUAL)) ParseError("Improper token in case block\n"); tokenv=look_hash(hash,LOCAL_HASH_SIZE,0x8000,0xFFFF,*labelptr,"labels"); if(!tokenv) tokenv=*labelptr,++*labelptr; tokenv-=0x8000; cl->codes[ptr++]=labelptr[tokenv]; if(labelptr[tokenv]==0xFFFF) { LabelStack*s=malloc(sizeof(LabelStack)); if(!s) fatal("Allocation failed\n"); s->id=tokenv|0x8000; s->addr=ptr-1; s->next=labelstack; labelstack=s; } break; case OP_STRING: CaseT1(TY_STRING); cl->codes[ptr++]=pool_string(tokenstr)+1; break; case 0x0200 ... 0x02FF: CaseT1(TY_MESSAGE); cl->codes[ptr++]=tokenv+1-0x0200; break; case 0x4000 ... 0x7FFF: CaseT1(TY_CLASS); cl->codes[ptr++]=tokenv-0x4000; break; case 0xC000 ... 0xFFFF: CaseT1(TY_MESSAGE); cl->codes[ptr++]=(tokenv&0x3FFF)+257; break; default: ParseError("Improper token in case block\n"); break; } } else if(Tokenf(TF_EOF)) { ParseError("Unexpected end of file\n"); } else { ParseError("Improper token in case block\n"); } nxttok(); } else if(tokent==TF_CLOSE) { if(!n) ParseError("Empty case block\n"); if(z && t1==TY_CODE) ParseError("Type mismatch in case block\n"); if(e) { cl->codes[ptr]=(t1==TY_CODE?ptr+1:0); ptr++; } if(t0==-1) t0=TY_NUMBER; if(t1==-1) t1=TY_NUMBER; cl->codes[sptr]=(t0<<12)|(t1<<8)|(n-1); return ptr; } else if(Tokenf(TF_EOF)) { ParseError("Unexpected end of file\n"); } else { ParseError("Improper token in case block\n"); } if(ptr>=0xFFEF) ParseError("Out of code space\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)) #define ChangeInst(x) (cl->codes[ptr-1]x,prflag=0) #define InstFlag(x) (peep<ptr && (prflag&(x))) #define Inst7bit() (peep<ptr && cl->codes[ptr-1]<0x0080) #define Inst8bit() (peep<ptr && cl->codes[ptr-1]<0x0100) #define AbbrevOp(x,y) case x: if(Inst7bit()) ChangeInst(+=0x1000|((y)<<4)); else AddInstF(x,tokent); break #define FlowPush(x) do{ if(flowdepth==64) ParseError("Too much flow control nesting\n"); flowop[flowdepth]=x; flowptr[flowdepth++]=ptr; }while(0) #define FlowPop(x) do{ if(!flowdepth || flowop[--flowdepth]!=(x)) ParseError("Flow control mismatch\n"); prflag=0; }while(0) static int parse_instructions(int cla,int ptr,Hash*hash,int compat) { int peep=ptr; int prflag=0; Class*cl=classes[cla]; int flowdepth=0; Uint16 flowop[64]; Uint16 flowptr[64]; int x,y; 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)) { numeric: if(!(tokenv&~0xFFL)) AddInst(tokenv); else if(!((tokenv-1)&tokenv)) AddInst(0x87E0+__builtin_ctz(tokenv)); else if(!(tokenv&~0xFFFFL)) AddInst2(OP_INT16,tokenv); else if((tokenv&0x80000000L) && -256<(Sint32)tokenv) AddInst(tokenv&0x01FF); else AddInst(OP_INT32),AddInst2(tokenv>>16,tokenv); } else if(Tokenf(TF_NAME)) { switch(tokenv) { AbbrevOp(OP_ADD,0x00); AbbrevOp(OP_SUB,0x08); AbbrevOp(OP_MUL,0x10); AbbrevOp(OP_DIV,0x18); AbbrevOp(OP_MOD,0x20); AbbrevOp(OP_MUL_C,0x28); AbbrevOp(OP_DIV_C,0x30); AbbrevOp(OP_MOD_C,0x38); AbbrevOp(OP_BAND,0x40); AbbrevOp(OP_BOR,0x48); AbbrevOp(OP_BXOR,0x50); AbbrevOp(OP_LSH,0x58); AbbrevOp(OP_RSH,0x60); AbbrevOp(OP_RSH_C,0x68); AbbrevOp(OP_EQ,0x70); AbbrevOp(OP_NE,0x78); AbbrevOp(OP_LT,0x80); AbbrevOp(OP_GT,0x88); AbbrevOp(OP_LE,0x90); AbbrevOp(OP_GE,0x98); AbbrevOp(OP_LT_C,0xA0); AbbrevOp(OP_GT_C,0xA8); AbbrevOp(OP_LE_C,0xB0); AbbrevOp(OP_GE_C,0xB8); AbbrevOp(OP_RET,0xE0); case OP_DROP: if(InstFlag(TF_DROP)) ChangeInst(+=0x2000); else AddInst(OP_DROP); break; case OP_LOCAL: if(!cla) ParseError("Cannot use local variable in a global definition\n"); tokenv=look_hash(hash,LOCAL_HASH_SIZE,0x2000,0x27FF,cl->uservars+0x2000,"user local variables")?:(cl->uservars++)+0x2000; if(Tokenf(TF_EQUAL)) tokenv+=0x1000; AddInst(tokenv); break; case OP_LABEL: tokenv=look_hash(hash,LOCAL_HASH_SIZE,0x8000,0xFFFF,*labelptr,"labels"); if(!tokenv) tokenv=*labelptr,++*labelptr; tokenv-=0x8000; if(Tokenf(TF_COMMA)) { AddInst2(OP_CALLSUB,labelptr[tokenv]); } else if(Tokenf(TF_EQUAL)) { AddInst2(OP_GOTO,labelptr[tokenv]); } else { if(labelptr[tokenv]!=0xFFFF) ParseError("Duplicate definition of label :%s\n",tokenstr); labelptr[tokenv]=ptr; peep=ptr; break; } if(labelptr[tokenv]==0xFFFF) { LabelStack*s=malloc(sizeof(LabelStack)); if(!s) fatal("Allocation failed\n"); s->id=tokenv|0x8000; s->addr=ptr-1; s->next=labelstack; labelstack=s; } break; case OP_IF: case OP_OR: case OP_AND: case OP_FORK: AddInst(tokenv); FlowPush(OP_IF); peep=++ptr; break; case OP_THEN: FlowPop(OP_IF); cl->codes[flowptr[flowdepth]]=peep=ptr; break; case OP_ELSE: FlowPop(OP_IF); x=flowptr[flowdepth]; AddInst(OP_GOTO); FlowPush(OP_IF); cl->codes[x]=peep=++ptr; break; case OP_BEGIN: FlowPush(OP_BEGIN); peep=ptr; break; case OP_AGAIN: FlowPop(OP_BEGIN); AddInst2(OP_GOTO,flowptr[flowdepth]); break; case OP_UNTIL: FlowPop(OP_BEGIN); AddInst2(OP_IF,flowptr[flowdepth]); break; case OP_WHILE: AddInst(OP_IF); FlowPush(OP_WHILE); peep=++ptr; break; case OP_REPEAT: FlowPop(OP_WHILE); x=flowptr[flowdepth]; FlowPop(OP_BEGIN); AddInst2(OP_GOTO,flowptr[flowdepth]); cl->codes[x]=ptr; break; case OP_FOR: if(num_globals>=0x07FF) ParseError("Too many user variables\n"); x=num_globals++; AddInst2(OP_FOR,x); FlowPush(OP_FOR); peep=++ptr; break; case OP_NEXT: FlowPop(OP_FOR); AddInst2(OP_NEXT,flowptr[flowdepth]); cl->codes[flowptr[flowdepth]]=peep=ptr; break; case OP_STRING: AddInst2(OP_STRING,pool_string(tokenstr)); break; case OP_MBEGIN: FlowPush(OP_BEGIN); AddInst(OP_TMARK); AddInst(OP_IF); FlowPush(OP_WHILE); peep=++ptr; break; case OP_USERFLAG: // Opcodes 0x1F00-0x1F1F read a bit; opcodes 0x1F20-0x1F3F set/clear a bit x=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!x) ParseError("User flag ^%s not defined\n",tokenstr); if(x<0x1020) y=OP_MISC4; else if(x<0x1040) y=OP_MISC5; else if(x<0x1060) y=OP_MISC6; else if(x<0x1080) y=OP_MISC7; else y=OP_COLLISIONLAYERS; if(Tokenf(TF_EQUAL)) { if(x>=0x1080) ParseError("Flag ^%s is read-only\n",tokenstr); if(Tokenf(TF_COMMA)) { AddInst(OP_OVER); AddInst(y+0x0800); AddInst(0x1F20|(x&0x1F)); AddInst(y+0x1800); } else { AddInst(y); AddInst(0x1F20|(x&0x1F)); AddInst(y+0x1000); } } else if(Tokenf(TF_COMMA)) { AddInst(y+0x0800); AddInst(0x1F00|(x&0x1F)); } else { AddInst(y); AddInst(0x1F00|(x&0x1F)); } break; case OP_SUPER: case OP_SUPER_C: if(!ptr || cl->codes[0]!=OP_SUPER) ParseError("Use of Super in a class with no parent class\n"); AddInst(tokenv); break; case OP_LINK: AddInst(OP_LINK); FlowPush(OP_IF); cl->codes[++ptr]=cla; peep=++ptr; break; case OP_RTN: AddInst(OP_RET); FlowPop(OP_IF); cl->codes[flowptr[flowdepth]]=peep=ptr; break; default: if(Tokenf(TF_ABNORMAL)) ParseError("Invalid instruction token\n"); if(compat && Tokenf(TF_COMPAT) && Tokenf(TF_EQUAL)) ++tokenv; 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) { case OP_POPUP: nxttok(); if(tokent!=TF_INT || tokenv<0 || tokenv>32) ParseError("Expected number from 0 to 32"); if(tokenv) AddInst2(OP_POPUPARGS,tokenv); else AddInst(OP_POPUP); nxttok(); if(tokent!=TF_CLOSE) ParseError("Unterminated (PopUp)\n"); break; case OP_PATTERN: case OP_PATTERNS: case OP_PATTERN_C: case OP_PATTERNS_C: case OP_PATTERN_E: case OP_PATTERNS_E: AddInst(tokenv); cl->codes[ptr]=peep=parse_pattern(cla,ptr+1,hash); ptr=peep; break; case OP_BROADCAST: nxttok(); if(Tokenf(TF_MACRO) || !Tokenf(TF_NAME) || tokenv<0x4000 || tokenv>0x7FFF) ParseError("Class name expected\n"); AddInst2(OP_BROADCASTCLASS,tokenv-0x4000); nxttok(); if(tokent!=TF_CLOSE) ParseError("Unterminated (Broadcast)\n"); break; case OP_BIT: x=0; for(;;) { nxttok(); if(tokent==TF_CLOSE) break; if(Tokenf(TF_MACRO) || !Tokenf(TF_INT)) ParseError("Number or close parenthesis expected\n"); x|=1<<tokenv; } tokenv=x; goto numeric; case OP_CASE: cl->codes[ptr++]=OP_CASE; ptr=peep=case_block(cla,ptr,hash); break; case OP_MISC4: y=0x000; goto uflags; case OP_MISC4_C: y=0x100; goto uflags; case OP_MISC5: y=0x020; goto uflags; case OP_MISC5_C: y=0x120; goto uflags; case OP_MISC6: y=0x040; goto uflags; case OP_MISC6_C: y=0x140; goto uflags; case OP_MISC7: y=0x060; goto uflags; case OP_MISC7_C: y=0x160; goto uflags; case OP_COLLISIONLAYERS: y=0x080; goto uflags; case OP_COLLISIONLAYERS_C: y=0x180; goto uflags; uflags: x=0; for(;;) { nxttok(); if(tokent==TF_CLOSE) break; if(Tokenf(TF_MACRO) || !Tokenf(TF_NAME) || tokenv!=OP_USERFLAG) ParseError("User flag or close parenthesis expected\n"); tokenv=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!tokenv) ParseError("User flag ^%s not defined\n",tokenstr); uflags1: if((tokenv^y)&0xE0) { if(y&0x100) ParseError("User flag ^%s belongs to the wrong attribute\n",tokenstr); } else { if(Tokenf(TF_COMMA)) x&=~(1<<(tokenv&31)); else x|=1<<(tokenv&31); } } tokenv=x; goto numeric; case OP_USERFLAG: tokenv=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!tokenv) ParseError("User flag ^%s not defined\n",tokenstr); x=0; y=(tokenv&0xE0)|0x100; goto uflags1; default: ParseError("Invalid parenthesized instruction\n"); } } else if(tokent==TF_CLOSE) { if(flowdepth) ParseError("Unterminated flow control structure\n"); if(peep<ptr && cl->codes[ptr-1]==OP_RET) break; if(peep<ptr && (cl->codes[ptr-1]&0xFF00)==0x1E00) break; if(Inst8bit()) ChangeInst(+=0x1E00); else AddInst(OP_RET); break; } else if(Tokenf(TF_EOF)) { ParseError("Unexpected end of file\n"); } else { ParseError("Invalid instruction token\n"); } if(ptr>=0xFFEF) ParseError("Out of code space\n"); } if(flowdepth) ParseError("Unterminated flow control blocks (%d levels)\n",flowdepth); cl->codes=realloc(cl->codes,ptr*sizeof(Uint16))?:cl->codes; if(!ptr) cl->codes=0; return ptr; } static void dump_class(int cla,int endptr,const Hash*hash) { const Class*cl=classes[cla]; int i,j; if(!cl) return; printf("<<<Class %d>>>\n",cla); printf(" Name: %c%s\n",cla?'$':'(',cla?cl->name:"Global)"); printf(" Flags: 0x%02X 0x%04X\n",cl->cflags,cl->oflags); printf(" Ht=%d Wt=%d Clm=%d Den=%d Vol=%d Str=%d Arr=%d Dep=%d\n",cl->height,cl->weight,cl->climb,cl->density,cl->volume,cl->strength,cl->arrivals,cl->departures); printf(" Temp=%d M4=%d M5=%d M6=%d M7=%d Shape=%d Shov=%d Coll=%d Img=%d\n",cl->temperature,cl->misc4,cl->misc5,cl->misc6,cl->misc7,cl->shape,cl->shovable,cl->collisionLayers,cl->nimages); printf(" Hard: %d %d %d %d\n",cl->hard[0],cl->hard[1],cl->hard[2],cl->hard[3]); printf(" Sharp: %d %d %d %d\n",cl->sharp[0],cl->sharp[1],cl->sharp[2],cl->sharp[3]); if(cl->nimages && cl->images) { printf(" Images:"); for(i=0;i<cl->nimages;i++) printf(" %d%c",cl->images[i]&0x7FFF,cl->images[i]&0x8000?'*':'.'); putchar('\n'); } if(cl->nmsg && cl->messages) { printf(" Messages:\n"); for(i=0;i<cl->nmsg;i++) { if(cl->messages[i]!=0xFFFF) { if(i<256) printf(" %s: 0x%04X\n",standard_message_names[i],cl->messages[i]); else printf(" #%s: 0x%04X\n",messages[i-256],cl->messages[i]); } } } if(hash && cl->uservars) { printf(" Variables:\n"); for(i=0;i<LOCAL_HASH_SIZE;i++) { if(hash[i].id>=0x2000 && hash[i].id<0x3000) printf(" %%%s = 0x%04X\n",hash[i].txt,hash[i].id); } } if(endptr && cl->codes) { printf(" Codes:"); for(i=0;i<endptr;i++) { if(!(i&15)) printf("\n [%04X]",i); printf(" %04X",cl->codes[i]); } putchar('\n'); } printf("---\n\n"); } static inline Uint32 class_def_number(void) { Uint32 n; nxttok(); if(!Tokenf(TF_INT)) fatal("Number expected\n"); n=tokenv; nxttok(); if(tokent!=TF_CLOSE) fatal("Close parentheses expected\n"); return n; } static Uint32 class_def_misc(void) { Uint32 n=0; for(;;) { nxttok(); if(tokent==TF_CLOSE) return n; if(Tokenf(TF_INT)) { n|=tokenv; } else if(Tokenf(TF_NAME) && !(tokenv&~255)) { n|=tokenv; } else if(Tokenf(TF_NAME) && tokenv>=OP_BITCONSTANT && tokenv<=OP_BITCONSTANT_LAST) { n|=1L<<(tokenv&31); } else { fatal("Number expected"); } } } static Uint32 class_def_arrivals(void) { Uint32 n=0; int i; nxttok(); if(Tokenf(TF_NAME) && tokenv==OP_INPLACE) { nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parentheses expected\n"); return 1<<12; } for(i=0;i<25;i++) { if(!Tokenf(TF_INT) || (tokenv&~1)) ParseError("Expected 0 or 1\n"); if(tokenv) n|=1<<(i+4-2*(i%5)); nxttok(); } if(tokent!=TF_CLOSE) ParseError("Close parentheses expected\n"); return n; } static void class_def_hard(Uint16*data) { int i; for(;;) { nxttok(); if(tokent==TF_CLOSE) { return; } else if(tokent==TF_OPEN) { nxttok(); if(!Tokenf(TF_DIR) || tokenv>7 || (tokenv&1)) ParseError("Expected even absolute direction\n"); i=tokenv>>1; nxttok(); if(tokent!=TF_INT || (tokenv&~0xFFFF)) ParseError("Hardness/sharpness must be a 16-bit number\n"); data[i]=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parentheses expected\n"); } else if(tokent==TF_INT) { if(tokenv&~0xFFFF) ParseError("Hardness/sharpness must be a 16-bit number\n"); data[0]=data[1]=data[2]=data[3]=tokenv; } else { ParseError("Expected ( or ) or number\n"); } } } static inline Uint8 class_def_shovable(void) { Uint8 n=0; nxttok(); if(tokent==TF_INT) { n=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parentheses expected\n"); return n; } for(;;) { if(tokent==TF_CLOSE) return n; if(!Tokenf(TF_DIR) || tokenv>7) ParseError("Expected absolute direction\n"); n|=1<<tokenv; nxttok(); } } static inline Uint8 class_def_shape(void) { Uint8 n=0; int i; for(;;) { nxttok(); if(tokent==TF_CLOSE) { return n; } else if(tokent==TF_OPEN) { nxttok(); if(!Tokenf(TF_DIR) || tokenv>7 || (tokenv&1)) ParseError("Expected even absolute direction\n"); i=tokenv; n&=~(3<<i); nxttok(); if(tokent!=TF_INT || (tokenv&~3)) ParseError("Shape must be 0, 1, 2, or 3\n"); n|=tokenv<<i; nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parentheses expected\n"); } else if(tokent==TF_INT) { if(tokenv&~3) ParseError("Shape must be 0, 1, 2, or 3\n"); n=tokenv*0x55; } else { ParseError("Expected ( or ) or number\n"); } } } static char*class_def_help(void) { char*txt=malloc(0x3000); int n=0; int i; if(!txt) fatal("Allocation failed\n"); for(;;) { nxttok(); if(tokent==TF_CLOSE) break; if(Tokenf(TF_NAME) && tokenv==OP_MISC1 && !n) { txt[0]=16; n=1; nxttok(); if(tokent==TF_CLOSE) break; ParseError("Close parenthesis expected\n"); } if(!Tokenf(TF_NAME) || tokenv!=OP_STRING) ParseError("String expected\n"); i=strlen(tokenstr); if(i+n>=0x2FFA) ParseError("Help text is too long\n"); strcpy(txt+n,tokenstr); n+=i; txt[n++]=10; txt[n]=0; } if(!n) { free(txt); return 0; } txt[n]=0; return realloc(txt,n+1)?:txt; } static void class_def_image(int cla) { Class*cl=classes[cla]; Uint16*img=malloc(256*sizeof(Uint16)); sqlite3_stmt*st; if(cl->nimages || cl->images) ParseError("Duplicate (Image) block\n"); if(!img) fatal("Allocation failed\n"); if(userdb && sqlite3_prepare_v2(userdb,"SELECT `ID` FROM `PICTURES` WHERE `NAME` = ?1;",-1,&st,0)) fatal("SQL error: %s\n",sqlite3_errmsg(userdb)); for(;;) { nxttok(); if(tokent==TF_CLOSE) break; if(cl->nimages==255) ParseError("Too many images in class\n"); if(!Tokenf(TF_NAME) || tokenv!=OP_STRING) ParseError("String expected\n"); if(userdb) { sqlite3_bind_text(st,1,tokenstr,-1,0); if(sqlite3_step(st)==SQLITE_ROW) { img[cl->nimages++]=sqlite3_column_int(st,0)|0x8000; } else { ParseError("A picture named \"%s\" does not exist\n",tokenstr); } sqlite3_reset(st); sqlite3_clear_bindings(st); } else { img[cl->nimages++]=0; } } if(userdb) sqlite3_finalize(st); if(!cl->nimages) { free(img); return; } cl->images=realloc(img,cl->nimages*sizeof(Uint16))?:img; } static void class_def_defaultimage(int cla) { Class*cl=classes[cla]; int i; for(i=0;i<cl->nimages;i++) cl->images[i]&=0x7FFF; for(;;) { nxttok(); if(tokent==TF_OPEN) { nxttok(); if(tokent==TF_INT) { if(tokenv<0 || tokenv>=cl->nimages) ParseError("Image number out of range\n"); i=tokenv; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv<i) ParseError("Backward range in (DefaultImage) block\n"); if(tokenv>=cl->nimages) ParseError("Image number out of range\n"); while(i<=tokenv) cl->images[i++]|=0x8000; nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parenthesis expected\n"); } else if(tokent!=TF_CLOSE) { ParseError("Number expected\n"); } } else if(tokent==TF_CLOSE) { break; } else if(tokent==TF_INT) { if(tokenv<0 || tokenv>=cl->nimages) ParseError("Image number out of range\n"); cl->images[tokenv]|=0x8000; } else if(Tokenf(TF_NAME) && !Tokenf(TF_MACRO) && tokenv==OP_MOD) { nxttok(); if(tokent!=TF_INT) ParseError("Expected ( or ) or number\n"); if(tokenv<=0) ParseError("Zero or negative modulus is not allowed\n"); for(i=0;i<cl->nimages-tokenv;i++) if(cl->images[i]&0x8000) cl->images[i+tokenv]|=0x8000; } else { ParseError("Expected ( or ) or number\n"); } } } static void class_user_flag(Class*cl) { int x=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!x) ParseError("User flag ^%s not defined\n",tokenstr); if(x<0x1020) cl->misc4|=1L<<(x&0x1F); else if(x<0x1040) cl->misc5|=1L<<(x&0x1F); else if(x<0x1060) cl->misc6|=1L<<(x&0x1F); else if(x<0x1080) cl->misc7|=1L<<(x&0x1F); else cl->collisionLayers|=1<<(x&0x1F); } static void set_super_class(Class*cl,int ptr) { Class*su=classes[tokenv&0x3FFF]; int i; if(ptr || cl->nmsg) ParseError("Cannot specify superclasses before program blocks\n"); if(!su || !(su->cflags&CF_GROUP)) ParseError("Cannot inherit from non-abstract class\n"); cl->codes=malloc(8); if(!cl->codes) fatal("Allocation failed\n"); cl->codes[0]=OP_SUPER; cl->codes[1]=tokenv&0x3FFF; cl->height=su->height; cl->weight=su->weight; cl->climb=su->climb; cl->density=su->density; cl->volume=su->volume; cl->strength=su->strength; cl->arrivals|=su->arrivals; cl->departures|=su->departures; cl->temperature=su->temperature; cl->misc4|=su->misc4; cl->misc5|=su->misc5; cl->misc6|=su->misc6; cl->misc7|=su->misc7; cl->uservars+=su->uservars; cl->oflags|=su->oflags; for(i=0;i<4;i++) { cl->sharp[i]=su->sharp[i]; cl->hard[i]=su->hard[i]; } cl->cflags|=su->cflags&(CF_PLAYER|CF_INPUT|CF_COMPATIBLE|CF_QUIZ); cl->shape=su->shape; cl->shovable=su->shovable; cl->collisionLayers|=su->collisionLayers; if(cl->nmsg=su->nmsg) { cl->messages=malloc(cl->nmsg*sizeof(Uint16)); if(!cl->messages) fatal("Allocation failed\n"); for(i=0;i<cl->nmsg;i++) cl->messages[i]=(su->messages[i]==0xFFFF)?0xFFFF:0x0000; } } static int make_dispatch_block(int cla,Class*cl,int ptr) { int i; cl->codes=realloc(cl->codes,0x10000*sizeof(Uint16)); if(!cl->codes) fatal("Allocation failed\n"); if(get_message_ptr(cla,MSG_KEY)!=0xFFFF) ParseError("Class $%s has a KEY message already\n",cl->name); if(ptr>0xFDFE) ParseError("Out of code space\n"); set_message_ptr(cla,MSG_KEY,ptr); cl->codes[ptr]=OP_DISPATCH; for(i=1;i<257;i++) cl->codes[ptr+i]=0; return ptr+257; } static void class_definition(int cla,sqlite3_stmt*vst) { Hash*hash=calloc(LOCAL_HASH_SIZE,sizeof(Hash)); Class*cl=classes[cla]; int ptr=0; int compat=0; int i,j; char disp=0; if(!hash) fatal("Allocation failed\n"); if(!cl) fatal("Confusion of class definition somehow\n"); if(cl->cflags&(CF_NOCLASS1|CF_NOCLASS2)) { cl->cflags=0; } else { ParseError("Duplicate definition of class $%s\n",cl->name); } begin_label_stack(); for(;;) { nxttok(); if(Tokenf(TF_EOF)) { ParseError("Unexpected end of file\n"); } else if(Tokenf(TF_MACRO)) { ParseError("Unexpected macro token\n"); } else if(Tokenf(TF_OPEN)) { nxttok(); if(Tokenf(TF_KEY)) { if(!disp) disp=1,ptr=make_dispatch_block(cla,cl,ptr); i=tokenv&255; cl->codes[cl->messages[MSG_KEY]+i]=ptr; if(cl->cflags&CF_INPUT) { nxttok(); if(tokent!=TF_NAME || tokenv!=OP_IGNOREKEY) keymask[i>>3]|=1<<(i&7); pushback=1; } ptr=parse_instructions(cla,ptr,hash,compat); } else if(Tokenf(TF_NAME)) { switch(tokenv) { case OP_IMAGE: class_def_image(cla); break; case OP_DEFAULTIMAGE: class_def_defaultimage(cla); break; case OP_HELP: if(cl->gamehelp) ParseError("Duplicate (Help) block\n"); cl->gamehelp=class_def_help(); break; case OP_EDITORHELP: if(cl->edithelp) ParseError("Duplicate (EditorHelp) block\n"); cl->edithelp=class_def_help(); break; case OP_HEIGHT: cl->height=class_def_number(); break; case OP_WEIGHT: cl->weight=class_def_number(); break; case OP_CLIMB: cl->climb=class_def_number(); break; case OP_DENSITY: cl->density=class_def_number(); break; case OP_VOLUME: cl->volume=class_def_number(); break; case OP_STRENGTH: cl->strength=class_def_number(); break; case OP_TEMPERATURE: cl->temperature=class_def_number(); break; case OP_MISC4: cl->misc4=class_def_misc(); break; case OP_MISC5: cl->misc5=class_def_misc(); break; case OP_MISC6: cl->misc6=class_def_misc(); break; case OP_MISC7: cl->misc7=class_def_misc(); break; case OP_ARRIVALS: cl->arrivals=class_def_arrivals(); break; case OP_DEPARTURES: cl->departures=class_def_arrivals(); break; case OP_HARD: class_def_hard(cl->hard); break; case OP_SHARP: class_def_hard(cl->sharp); break; case OP_SHAPE: cl->shape=class_def_shape(); break; case OP_SHOVABLE: cl->shovable=class_def_shovable(); break; case OP_SUBS: ptr=parse_instructions(cla,ptr,hash,compat); break; case OP_LABEL: pushback=1; ptr=parse_instructions(cla,ptr,hash,compat); break; case OP_COLLISIONLAYERS: cl->collisionLayers=i=class_def_misc(); if(i&~255) ParseError("CollisionLayers out of range\n"); break; case OP_OTHERS: if(!disp) disp=1,ptr=make_dispatch_block(cla,cl,ptr); if(!(cl->cflags&CF_INPUT)) ParseError("Others block without Input flag\n"); cl->codes[cl->messages[MSG_KEY]+256]=ptr; ptr=parse_instructions(cla,ptr,hash,compat); break; case OP_ROOK: if(!disp) disp=1,ptr=make_dispatch_block(cla,cl,ptr); if(ptr>=0xFFED) ParseError("Out of code space\n"); for(i=37;i<=40;i++) { cl->codes[cl->messages[MSG_KEY]+i]=ptr; if(cl->cflags&CF_INPUT) keymask[i>>3]|=1<<(i&7); } cl->codes[ptr++]=OP_QUEEN; ptr=parse_instructions(cla,ptr,hash,compat); break; case OP_BISHOP: if(!disp) disp=1,ptr=make_dispatch_block(cla,cl,ptr); if(ptr>=0xFFED) ParseError("Out of code space\n"); for(i=33;i<=36;i++) { cl->codes[cl->messages[MSG_KEY]+i]=ptr; if(cl->cflags&CF_INPUT) keymask[i>>3]|=1<<(i&7); } cl->codes[ptr++]=OP_QUEEN; ptr=parse_instructions(cla,ptr,hash,compat); break; case OP_QUEEN: if(!disp) disp=1,ptr=make_dispatch_block(cla,cl,ptr); if(ptr>=0xFFED) ParseError("Out of code space\n"); for(i=33;i<=40;i++) { cl->codes[cl->messages[MSG_KEY]+i]=ptr; if(cl->cflags&CF_INPUT) keymask[i>>3]|=1<<(i&7); } cl->codes[ptr++]=OP_QUEEN; ptr=parse_instructions(cla,ptr,hash,compat); break; case 0x0200 ... 0x02FF: i=tokenv&255; goto message; case 0xC000 ... 0xFFFF: i=tokenv+256-0xC000; message: set_message_ptr(cla,i,ptr); nxttok(); pushback=1; if(tokenv==OP_LABEL && tokent==(TF_NAME|TF_ABNORMAL|TF_EQUAL)) { j=look_hash(hash,LOCAL_HASH_SIZE,0x8000,0xFFFF,*labelptr,"labels"); if(!j) j=*labelptr,++*labelptr; if(labelptr[j-0x8000]!=0xFFFF) { set_message_ptr(cla,i,labelptr[j-0x8000]); pushback=0; } } ptr=parse_instructions(cla,ptr,hash,compat); break; default: ParseError("Invalid directly inside of a class definition\n"); } } else { ParseError("Invalid directly inside of a class definition\n"); } } else if(Tokenf(TF_NAME)) { switch(tokenv) { case OP_PLAYER: cl->cflags|=CF_PLAYER; break; case OP_INPUT: cl->cflags|=CF_INPUT; break; case OP_COMPATIBLE: cl->cflags|=CF_COMPATIBLE; compat=1; break; case OP_COMPATIBLE_C: cl->cflags|=CF_COMPATIBLE; break; case OP_QUIZ: cl->cflags|=CF_QUIZ; break; case OP_INVISIBLE: cl->oflags|=OF_INVISIBLE; break; case OP_VISUALONLY: cl->oflags|=OF_VISUALONLY; break; case OP_STEALTHY: cl->oflags|=OF_STEALTHY; break; case OP_USERSTATE: cl->oflags|=OF_USERSTATE; break; case OP_BIZARRO: cl->oflags|=OF_BIZARRO; break; case OP_SHOVABLE: cl->shovable=0x55; break; case OP_USERFLAG: class_user_flag(cl); break; case OP_ABSTRACT: cl->cflags|=CF_GROUP; break; case OP_CONNECTION: cl->oflags|=OF_CONNECTION; break; case OP_CRUSH: cl->oflags|=OF_CRUSH; break; case 0x4000 ... 0x7FFF: set_super_class(cl,ptr); ptr=2; break; default: ParseError("Invalid directly inside of a class definition\n"); } } else if(Tokenf(TF_CLOSE)) { break; } else { ParseError("Invalid directly inside of a class definition\n"); } } end_label_stack(cl->codes,hash); if(!cl->nimages && !(cl->cflags&CF_GROUP)) cl->oflags|=OF_INVISIBLE; if(cl->collisionLayers && *dense && !cl->density) { for(i=0;i<8;i++) if(dense[i] && ((1<<i)&cl->collisionLayers)) { cl->density=dense[i]; break; } } if(main_options['C']) dump_class(cla,ptr,hash); if(main_options['H']) { for(i=0;i<LOCAL_HASH_SIZE;i++) if(hash[i].id) printf(" \"%s\": %04X\n",hash[i].txt,hash[i].id); } for(i=0;i<LOCAL_HASH_SIZE;i++) { if(vst && hash[i].id>=0x2000 && hash[i].id<0x2800) { sqlite3_reset(vst); sqlite3_bind_int(vst,1,(hash[i].id&0x07FF)|(cla<<16)); sqlite3_bind_text(vst,2,hash[i].txt,-1,free); while(sqlite3_step(vst)==SQLITE_ROW); } else { free(hash[i].txt); } } free(hash); if(cl->cflags&CF_GROUP) { if(cl->edithelp || cl->gamehelp || cl->nimages) ParseError("Invalid in abstract classes"); } } static void load_class_numbers(void) { int i,n; long size=0; unsigned char*data=read_lump(FIL_LEVEL,LUMP_CLASS_DEF,&size); unsigned char*p; if(!data) return; for(i=0;i<size-3;) { n=data[i]|(data[i+1]<<8); if(!n) break; if(n>=0x4000) fatal("Malformed CLASS.DEF lump (invalid class number %d)\n",n); i+=2; p=data+i; while(i<size && data[i++]); if(i==size && data[i-1]) fatal("Malformed CLASS.DEF lump\n"); initialize_class(n,CF_NOCLASS2,p); } i+=2; for(;i<size-3;) { n=data[i]|(data[i+1]<<8); if(n<256 || n>=0x4100) fatal("Malformed CLASS.DEF lump (invalid message number %d)\n",n); n-=256; i+=2; p=data+i; while(i<size && data[i++]); if(i==size && data[i-1]) fatal("Malformed CLASS.DEF lump\n"); if(messages[n]) fatal("Duplicate message number %d\n",n+256); messages[n]=strdup(p); if(!messages[n]) fatal("Allocation failed\n"); if(n>=undef_message) undef_message=n+1; } free(data); } static void parse_order_block(void) { // OP_MISC1, OP_MISC1_C, etc = properties (_C=reverse) // 0x1000...0x10FF = Have flag // OP_RET = end of block Uint16 beg,ptr; orders=malloc(0x4000*sizeof(Uint16)); if(!orders) fatal("Allocation failed\n"); nxttok(); if(tokent==TF_INT) { if(tokenv<1 || tokenv>254) ParseError("Order number out of range\n"); beg=ptr=tokenv; nxttok(); } else { beg=ptr=128; } while(tokent!=TF_CLOSE) { if(tokent!=TF_OPEN) ParseError("Open or close parenthesis expected\n"); if(ptr>=0x3FFD) ParseError("Out of order memory\n"); nxttok(); if(Tokenf(TF_MACRO|TF_COMMA|TF_EQUAL) || !Tokenf(TF_NAME)) ParseError("Unexpected token in (Order) block\n"); orders[++norders]=ptr; if(norders==beg) ParseError("Too many orders\n"); switch(tokenv) { case OP_INPUT: case OP_PLAYER: case OP_CONTROL: case 0x4000 ... 0x7FFF: orders[ptr++]=tokenv; break; case OP_USERFLAG: tokenv=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!tokenv) ParseError("User flag ^%s not defined\n",tokenstr); orders[ptr++]=tokenv; break; default: ParseError("Unexpected token in (Order) block\n"); } while(nxttok(),tokent!=TF_CLOSE) { if(Tokenf(TF_MACRO|TF_EQUAL) || !Tokenf(TF_NAME)) ParseError("Unexpected token in (Order) block\n"); if(ptr>=0x3FFE) ParseError("Out of order memory\n"); switch(tokenv) { case OP_MISC1: case OP_MISC1_C: case OP_MISC2: case OP_MISC2_C: case OP_MISC3: case OP_MISC3_C: case OP_MISC4: case OP_MISC4_C: case OP_MISC5: case OP_MISC5_C: case OP_MISC6: case OP_MISC6_C: case OP_MISC7: case OP_MISC7_C: case OP_TEMPERATURE: case OP_TEMPERATURE_C: case OP_DENSITY: case OP_DENSITY_C: case OP_XLOC: case OP_XLOC_C: case OP_YLOC: case OP_YLOC_C: case OP_IMAGE: case OP_IMAGE_C: case 0xC000 ... 0xFFFF: orders[ptr++]=tokenv; break; default: ParseError("Unexpected token in (Order) block\n"); } } orders[ptr++]=OP_RET; nxttok(); } if(!norders) ParseError("Empty (Order) block\n"); orders=realloc(orders,ptr*sizeof(Uint16))?:orders; } static void set_class_orders(void) { int i,j,k,m; if(main_options['C']) { for(j=1;j<=norders;j++) { printf("Order %d =",j); k=orders[j]; while(orders[k]!=OP_RET) printf(" %04X",orders[k++]); putchar('\n'); } } for(i=1;i<undef_class;i++) if(classes[i] && (classes[i]->nmsg || classes[0]->nmsg) && !(classes[i]->cflags&(CF_GROUP|CF_NOCLASS2))) { for(j=1;j<=norders;j++) { k=orders[orders[j]]; switch(k) { case 0x1000 ... 0x101F: if(classes[i]->misc4&(1UL<<(k&0x1F))) goto found; break; case 0x1020 ... 0x103F: if(classes[i]->misc5&(1UL<<(k&0x1F))) goto found; break; case 0x1040 ... 0x105F: if(classes[i]->misc6&(1UL<<(k&0x1F))) goto found; break; case 0x1060 ... 0x107F: if(classes[i]->misc7&(1UL<<(k&0x1F))) goto found; break; case 0x1080 ... 0x1087: if(classes[i]->collisionLayers&(1L<<(k&0x07))) goto found; break; case 0x4000 ... 0x7FFF: if(i+0x4000==k) goto found; m=i; while(m && classes[m]->codes && classes[m]->codes[0]==OP_SUPER) { if(classes[m]->codes[1]+0x4000==k) goto found; m=classes[m]->codes[1]; } break; case OP_PLAYER: if(classes[i]->cflags&CF_PLAYER) goto found; break; case OP_INPUT: if(classes[i]->cflags&CF_INPUT) goto found; break; case OP_CONTROL: if(i==control_class) goto found; break; } continue; found: classes[i]->order=j; if(main_options['C']) printf("Order %d : Class %d\n",j,i); break; } } } static int level_table_code(int ptr,Hash*hash) { int flowdepth=0; Uint16 flowptr[64]; int x; for(;;) { if(ptr>=0xFFFA) ParseError("Out of memory\n"); nxttok(); if(Tokenf(TF_MACRO)) ParseError("Unexpected macro\n"); if(tokent==TF_CLOSE) { ll_code[ptr++]=OP_RET; return ptr; } else if(Tokenf(TF_INT)) { if(!(tokenv&~0xFFL)) ll_code[ptr++]=tokenv; else if(!(tokenv&~0xFFFFL)) ll_code[ptr++]=OP_INT16,ll_code[ptr++]=tokenv; else ll_code[ptr++]=OP_INT32,ll_code[ptr++]=OP_INT32,ll_code[ptr++]=tokenv>>16,ll_code[ptr++]=tokenv; } else if(Tokenf(TF_NAME)) { switch(tokenv) { case OP_IF: if(flowdepth==64) ParseError("Too much flow control nesting\n"); ll_code[ptr++]=OP_IF; flowptr[flowdepth++]=ptr++; break; case OP_ELSE: if(!flowdepth) ParseError("Flow control mismatch\n"); ll_code[flowptr[flowdepth-1]]=ptr+2; ll_code[ptr++]=OP_GOTO; flowptr[flowdepth-1]=ptr++; break; case OP_THEN: if(!flowdepth) ParseError("Flow control mismatch\n"); ll_code[flowptr[--flowdepth]]=ptr; break; case OP_STRING: ll_code[ptr++]=OP_STRING; ll_code[ptr++]=pool_string(tokenstr); break; case OP_LOCAL: x=look_hash(hash,LOCAL_HASH_SIZE,0x200,0x23F,ll_naggregate+0x200,"aggregates")?:(ll_naggregate++)+0x200; ll_code[ptr++]=OP_LOCAL; ll_code[ptr++]=x-0x200; break; case OP_USERFLAG: if(Tokenf(TF_COMMA|TF_EQUAL)) ParseError("Invalid instruction token\n"); x=look_hash(glohash,HASH_SIZE,0x1000,0x10FF,0,"user flags"); if(!x) ParseError("User flag ^%s not defined\n",tokenstr); ll_code[ptr++]=OP_USERFLAG; ll_code[ptr++]=x; break; default: if(Tokenf(TF_ABNORMAL)) ParseError("Invalid instruction token\n"); ll_code[ptr++]=tokenv; } } else { ParseError("Invalid instruction token\n"); } } } static void level_table_definition(void) { sqlite3_str*str=sqlite3_str_new(0); unsigned char buf[0x2000]; Hash*hash=calloc(LOCAL_HASH_SIZE,sizeof(Hash)); DataColumn*datac=calloc(0x41,sizeof(DataColumn)); DataColumn*aggrc=calloc(0x41,sizeof(DataColumn)); DisplayColumn*dispc=calloc(0x41,sizeof(DisplayColumn)); int ptr=0; int last=0; int i; if(ll_naggregate || ll_ndata || ll_ndisp || ll_head || ll_code) ParseError("LevelTable block is already defined\n"); ll_code=malloc(0x10000*sizeof(Uint16)); if(!hash || !ll_code || !datac || !aggrc || !dispc) fatal("Allocation failed\n"); sqlite3_str_appendchar(str,1,0xB3); for(;;) { nxttok(); if(tokent==TF_EOF) ParseError("Unexpected end of file\n"); if(tokent==TF_CLOSE) break; if(tokent!=TF_OPEN) ParseError("Expected ( or )\n"); nxttok(); if(!Tokenf(TF_NAME)) ParseError("Unexpected token in (LevelTable) block\n"); switch(tokenv) { case OP_LABEL: i=look_hash(hash,LOCAL_HASH_SIZE,0x100,0x13F,ll_ndata+0x100,"data columns")?:(ll_ndata++)+0x100; i-=0x100; if(datac[i].name) ParseError("Duplicate definition\n"); datac[i].name=strdup(tokenstr); if(!datac[i].name) fatal("Allocation failed\n"); datac[i].ptr=ptr; if(Tokenf(TF_COMMA)) datac[i].sgn=1; ptr=level_table_code(ptr,hash); break; case OP_STRING: if(last) ParseError("Extra columns after fill column\n"); for(i=0;tokenstr[i];i++) if(!(tokenstr[i]&~31)) ParseError("Improper column heading\n"); strcpy(buf,tokenstr); // Data nxttok(); if(Tokenf(TF_NAME) && tokenv==OP_LABEL) { i=look_hash(hash,LOCAL_HASH_SIZE,0x100,0x13F,ll_ndata+0x100,"display columns")?:(ll_ndata++)+0x100; dispc[ll_ndisp].data=i-0x100; } else { ParseError("Syntax error\n"); } // Width nxttok(); if(tokent==TF_INT && tokenv>0 && tokenv<=255) { dispc[ll_ndisp].width=tokenv; } else if(Tokenf(TF_NAME) && tokenv==OP_MUL) { dispc[ll_ndisp].width=255; dispc[ll_ndisp].flag|=1; } else { ParseError("Syntax error\n"); } // Format nxttok(); if(!Tokenf(TF_NAME) || tokenv!=OP_STRING || !*tokenstr) ParseError("Syntax error\n"); if(*tokenstr<32 || *tokenstr>126) ParseError("Syntax error\n"); if(tokenstr[1]>0 && tokenstr[1]<31) ParseError("Syntax error\n"); if(tokenstr[1]==31) tokenstr[1]=tokenstr[2],tokenstr[2]=tokenstr[3]; if(tokenstr[1] && tokenstr[2]) ParseError("Syntax error\n"); memcpy(dispc[ll_ndisp].form,tokenstr,2); // Colour nxttok(); if(tokent==TF_INT) { if(tokenv&~255) ParseError("Color out of range\n"); dispc[ll_ndisp].color=tokenv; nxttok(); if(tokent!=TF_CLOSE) ParseError("Syntax error\n"); } else if(tokent==TF_OPEN) { dispc[ll_ndisp].flag|=2; dispc[ll_ndisp].ptr=ptr; for(;;) { if(++dispc[ll_ndisp].color==255) ParseError("Too many colors\n"); nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); i=tokenv; if(i<-127 || i>127) ParseError("Number out of range\n"); nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(ptr>=0xFFFE) ParseError("Out of memory\n"); if(tokenv&~255) ParseError("Color out of range\n"); ll_code[ptr++]=tokenv|((i+128)<<8); nxttok(); if(tokent!=TF_CLOSE) ParseError("Close parenthesis expected\n"); nxttok(); if(tokent==TF_CLOSE) break; if(tokent!=TF_OPEN) ParseError("Syntax error\n"); }; } else if(tokent==TF_CLOSE) { dispc[ll_ndisp].color=0xFF; } // End if(dispc[ll_ndisp].flag&1) { last=1; sqlite3_str_appendall(str,buf); } else { i=dispc[ll_ndisp].width; sqlite3_str_appendf(str,"%-*.*s\xB3",i,i,buf); } ll_ndisp++; break; case OP_LOCAL: i=look_hash(hash,LOCAL_HASH_SIZE,0x200,0x23F,ll_naggregate+0x200,"aggregates")?:(ll_naggregate++)+0x200; i-=0x200; if(aggrc[i].ag) ParseError("Duplicate definition\n"); nxttok(); if(!Tokenf(TF_NAME)) ParseError("Improper aggregate\n"); switch(tokenv) { case OP_ADD: aggrc[i].ag=1; break; case OP_MIN_C: aggrc[i].sgn=1; case OP_MIN: aggrc[i].ag=2; break; case OP_MAX_C: aggrc[i].sgn=1; case OP_MAX: aggrc[i].ag=3; break; default: ParseError("Improper aggregate\n"); } aggrc[i].ptr=ptr; ptr=level_table_code(ptr,hash); break; default: ParseError("Unexpected token in (LevelTable) block\n"); } } ll_head=sqlite3_str_finish(str); if(!ll_head) fatal("Allocation failed\n"); for(i=0;i<ll_ndata;i++) if(!datac[i].name) ParseError("Undefined data column\n"); for(i=0;i<ll_naggregate;i++) if(!aggrc[i].ag) ParseError("Undefined aggregate\n"); ll_data=realloc(datac,(ll_ndata+ll_naggregate)*sizeof(DataColumn)); if(!ll_data) fatal("Allocation failed\n"); for(i=0;i<ll_naggregate;i++) ll_data[i+ll_ndata]=aggrc[i]; free(aggrc); ll_disp=realloc(dispc,(ll_ndisp)*sizeof(DisplayColumn))?:dispc; // The next line will result in an invalid pointer if ptr is zero, // but this is harmless, since ll_code is never accessed if ptr is zero. ll_code=realloc(ll_code,ptr*sizeof(Uint16))?:ll_code; for(i=0;i<LOCAL_HASH_SIZE;i++) free(hash[i].txt); free(hash); if(main_options['C']) { printf("<<<LevelTable>>>\n Data/Aggregate:\n"); for(i=0;i<ll_ndata+ll_naggregate;i++) { printf(" %d: ",i); if(i<ll_ndata) printf(" \"%s\"",ll_data[i].name); else printf(" (%d)",ll_data[i].ag); printf(" 0x%04X %c\n",ll_data[i].ptr,ll_data[i].sgn?'s':'u'); } printf(" Display:\n"); for(i=0;i<ll_ndisp;i++) printf(" %d: data=%d width=%d format=%c%c color=%d flag=%d ptr=0x%04X\n",i ,ll_disp[i].data,ll_disp[i].width,ll_disp[i].form[0]?:' ',ll_disp[i].form[1]?:' ',ll_disp[i].color,ll_disp[i].flag,ll_disp[i].ptr); printf(" Codes:"); for(i=0;i<ptr;i++) { if(!(i&15)) printf("\n [%04X]",i); printf(" %04X",ll_code[i]); } putchar('\n'); printf("---\n\n"); } } static void parse_density_block(void) { int i; for(i=0;i<8;i++) { nxttok(); if(tokent==TF_CLOSE) return; if(tokent!=TF_INT || tokenv<1 || tokenv>255) ParseError("Number 1-255 or close parenthesis expected\n"); dense[i]=tokenv; } ParseError("Too many global density numbers are specified\n"); } void load_classes(void) { int i; int gloptr=0; Hash*glolocalhash; char*nam=sqlite3_mprintf("%s.class",basefilename); sqlite3_stmt*vst=0; printStatus("Loading class definitions...\n"); if(!nam) fatal("Allocation failed\n"); classfp=main_options['z']?composite_slice(".class",1):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,"bit"); glohash[look_hash_mac()].id=MAC_BIT; strcpy(tokenstr,"make"); glohash[look_hash_mac()].id=MAC_MAKE; 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; strcpy(tokenstr,"append"); glohash[look_hash_mac()].id=MAC_APPEND; strcpy(tokenstr,"edit"); glohash[look_hash_mac()].id=MAC_EDIT; 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)); load_class_numbers(); if(userdb && (i=sqlite3_prepare_v2(userdb,"INSERT INTO `VARIABLES`(`ID`,`NAME`) VALUES(?1,?2);",-1,&vst,0))) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb)); for(;;) { nxttok(); if(tokent==TF_EOF) goto done; if(tokent!=TF_OPEN) ParseError("Expected open parenthesis\n"); nxttok(); if(Tokenf(TF_FUNCTION)) { functions[tokenv&0x3FFF]=gloptr; begin_label_stack(); gloptr=parse_instructions(0,gloptr,glolocalhash,0); end_label_stack(classes[0]->codes,glolocalhash); } else if(Tokenf(TF_NAME)) { switch(tokenv) { case 0x4000 ... 0x7FFF: // Class definition class_definition(tokenv-0x4000,vst); break; case 0x0200 ... 0x02FF: case 0xC000 ... 0xFFFF: // Default message handler begin_label_stack(); set_message_ptr(0,tokenv&0x8000?(tokenv&0x3FFF)+256:tokenv-0x0200,gloptr); gloptr=parse_instructions(0,gloptr,glolocalhash,0); end_label_stack(classes[0]->codes,glolocalhash); break; case 0x2800 ... 0x2FFF: // Define initial values of global variables i=tokenv-0x2800; nxttok(); if(tokent==TF_CLOSE) break; if(Tokenf(TF_NAME) && tokenv==OP_ARRAY) { initglobals[i].t=TY_ARRAY; initglobals[i].u=parse_array(); } else { 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=inv_back_color=tokenv; nxttok(); if(tokent==TF_INT) { if(tokenv&~255) ParseError("Background color out of range\n"); inv_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_SYNCHRONIZE: nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); i=tokenv; if(i&~7) ParseError("Animation slot number out of range\n"); nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv<1 || tokenv>255) ParseError("Length of synchronized animation out of range\n"); anim_slot[i].length=tokenv; nxttok(); if(tokent!=TF_INT) ParseError("Number expected\n"); if(tokenv<1 || tokenv>255) ParseError("Synchronized animation speed out of range\n"); anim_slot[i].speed=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; case OP_MISC4: define_user_flags(0x1000,0x101F); break; case OP_MISC5: define_user_flags(0x1020,0x103F); break; case OP_MISC6: define_user_flags(0x1040,0x105F); break; case OP_MISC7: define_user_flags(0x1060,0x107F); break; case OP_COLLISIONLAYERS: define_user_flags(0x1080,0x1087); break; case OP_CODEPAGE: nxttok(); if(tokent!=TF_INT || tokenv<1 || tokenv>0x7FFFFF) ParseError("Number from 1 to 8388607 expected\n"); if(tokenv==460800 || tokenv==954) has_mbcs=1; set_code_page(tokenv); nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_ORDER: if(norders) ParseError("Extra (Order) block\n"); parse_order_block(); break; case OP_CONTROL: if(control_class) ParseError("Extra (Control) block\n"); strcpy(tokenstr,"(Control)"); control_class=look_class_name(); if(!(classes[control_class]->cflags&CF_NOCLASS1)) ParseError("Conflicting definition of (Control) class\n"); class_definition(control_class,vst); break; case OP_CONNECTION: nxttok(); if(!(tokent&TF_NAME) || tokenv!=OP_STRING) ParseError("String literal expected\n"); for(i=0;tokenstr[i];i++) switch(tokenstr[i]) { case 't': conn_option|=0x02; break; case 'w': conn_option|=0x01; break; default: ParseError("Unrecognized (Connection) option\n"); } nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_LEVELTABLE: level_table_definition(); break; case OP_INPUTXY: has_xy_input=1; nxttok(); if(tokent!=TF_CLOSE) ParseError("Expected close parenthesis\n"); break; case OP_DENSITY: parse_density_block(); break; default: ParseError("Invalid top level definition: %s\n",tokenstr); } } else { ParseError("Invalid top level definition\n"); } } done: fclose(classfp); if(main_options['C']) { dump_class(0,gloptr,glolocalhash); printf("<<<User-defined Messages>>>\n"); for(i=0;i<0x4000;i++) if(messages[i]) printf(" %d = #%s\n",i+256,messages[i]); printf("---\n\n"); } if(main_options['H']) { for(i=0;i<HASH_SIZE;i++) if(glohash[i].id) printf("\"%s\": %04X\n",glohash[i].txt,glohash[i].id); for(i=0;i<LOCAL_HASH_SIZE;i++) if(glolocalhash[i].id) printf(" \"%s\": %04X\n",glolocalhash[i].txt,glolocalhash[i].id); } for(i=0;i<LOCAL_HASH_SIZE;i++) free(glolocalhash[i].txt); free(glolocalhash); for(i=0;i<num_functions;i++) if(functions[i]==0xFFFF) { int j; for(j=0;j<HASH_SIZE;j++) if(glohash[j].id==i+0x8000) fatal("Function &%s mentioned but not defined\n",glohash[j].txt); fatal("Function mentioned but not defined\n"); } for(i=0;i<HASH_SIZE;i++) { if(vst && glohash[i].id>=0x2800 && glohash[i].id<0x3000) { sqlite3_reset(vst); sqlite3_bind_int(vst,1,glohash[i].id&0x07FF); sqlite3_bind_text(vst,2,glohash[i].txt,-1,free); while(sqlite3_step(vst)==SQLITE_ROW); } else { free(glohash[i].txt); } } if(vst) sqlite3_finalize(vst); free(glohash); for(i=1;i<undef_class;i++) if(classes[i] && (classes[i]->cflags&CF_NOCLASS1)) fatal("Class $%s mentioned but not defined\n",classes[i]->name); if(control_class) { if(classes[control_class]->nimages) fatal("Images are not allowed in Control class"); if(classes[control_class]->cflags&CF_GROUP) fatal("Control class is not allowed to be Abstract"); } if(macros) for(i=0;i<MAX_MACRO;i++) if(macros[i]) free_macro(macros[i]); free(macros); if(array_size) { array_data=malloc(array_size*sizeof(Value)); if(!array_data) fatal("Array allocation failed\n"); } if(norders) set_class_orders(); printStatus("Done\n"); }