Index: TODO ================================================================== --- TODO +++ TODO @@ -15,11 +15,10 @@ * Possibility to define auto-generation levels mode * Editor * Mouse dragging * Level index editor * Table of contents for levels (partially implemented) - * Can define your own columns * Can be divisions by sections * Deal better with allowing to skip past corrupted levels * Picture editor/loading * Allowing more altimages * Batch insert multiple dependent image lumps Index: class.doc ================================================================== --- class.doc +++ class.doc @@ -2643,11 +2643,11 @@ Logical: land lor lxor lnot Compare: eq ne lt ,lt gt ,gt le ,le ge ,ge - Misc: Level _ in nin ret if else then + Misc: Level _ in nin ret if else then c? m? n? . dup Trace Object: Xloc Yloc Class Dir Misc1 Misc2 Misc3 Misc4 Misc5 Misc6 Misc7 Temperature Density Player Input Bizarro CollisionLayers Additionally, you can include numbers, strings (see below for their @@ -2654,7 +2654,53 @@ meaning, which is different from the meaning in class codes), aggregate names (with % at first), user flags (with ^ at first), class names, and message names (both built-in messages and user messages are possible). Accessing values of objects other than the current one is not possible. -(TODO: Usage of strings as instructions in this block.) +The Trace instruction in the (LevelTable) block is not the same as in the +class codes. In this block, it doesn't consume anything from the stack; +instead, if both -t and -v switches are enabled, then it will display data +relating to the level table calculation on stdout, at the time when the +level table is loaded. (If the table is dropped, it will automatically be +reloaded the next time it is accessed.) + +If a string is found where an instruction is expected in this block, then +unlike class codes it is not simply pushing the string to the stack. +Instead, if it starts with @ then the rest of it is a literal string, but +otherwise it represents a pattern to match. If the match is successful, +it pushes the matched substring (or sometimes, a number) to the stack, +and then pushes 1; if unsuccessful, only pushes 0. These patterns will +match the title string of the level. Pattern codes are: + + = Follow by a delimited string; match that string at the current + position and skip past it, or if it doesn't match, fail. + / Follow by a delimited string; match that string at the current or + any later position (at the first place where it is found), or if + it doesn't match, fail. + [ Set beginning of slice. + ] Set end of slice. + ( Set beginning of slice (same as the [ command); + ) Set end of slice, but use the position just before the matched item + instead of just after it. + \n Skip until a line break of horizontal bar, and past it. If it is + followed by any more consecutive line breaks without anything else + in between, skips those ones too. + \d Follow by some text and then : and \ or just \ to skip to and find + the data in the string that has the same text. If it contains a + question mark then any single character is matched. Between : and \ + you can have usual other matching commands such as [ and ]. + + Follow by one more character, skip past any number of them. + - Skip past any number of digits. + _ Skip past any number of spaces and line breaks. + < Rewind back to the beginning. + > Skip to the end. + # Successful match; the result is a number within the slice, converted + from a string (parsed as decimal) to a number. + $ Successful match; the result is the matched slice. + % Successful match; the result is the length of the matched slice. + +A "delimited string" in this block is delimited by any printable +character on both sides (except a character with code less than 32). + +(The above list of pattern codes maybe it isn't explained very well; +hopefully someone else know how to improve it better.) Index: function.c ================================================================== --- function.c +++ function.c @@ -1211,21 +1211,159 @@ Value misc1,misc2,misc3; Uint8 x,y,dir,bizarro; Uint16 class; } ObjInfo; -#define ArithOp1(aa) ({ if(sp<1 || stack[sp-1].t) goto bad; Value t1=stack[sp-1]; stack[sp-1]=NVALUE(aa); }) -#define ArithOp2(aa) ({ if(sp<2 || stack[sp-1].t || stack[sp-2].t) goto bad; Value t1=stack[sp-2]; Value t2=stack[sp-1]; sp--; stack[sp-1]=NVALUE(aa); }) -#define ArithDivOp2(aa) ({ if(sp<2 || stack[sp-1].t || stack[sp-2].t || !stack[sp-1].u) goto bad; Value t1=stack[sp-2]; Value t2=stack[sp-1]; sp--; stack[sp-1]=NVALUE(aa); }) -#define OtherOp1(aa) ({ if(sp<1) goto bad; Value t1=stack[sp-1]; stack[sp-1]=(aa); }) -#define OtherOp2(aa) ({ if(sp<2) goto bad; Value t1=stack[sp-2]; Value t2=stack[sp-1]; sp--; stack[sp-1]=(aa); }) +static const unsigned char*ll_find(const unsigned char*p,Uint8 c) { + int j; + for(j=0;*p;p++) { + if(j) { + if(*p=='\\') j=0; + } else { + if(*p==c) return p; + if(*p==14 || *p==30) j=1; + if(*p==31 && p[1]) p++; + } + } + return 0; +} + +static inline Sint16 ll_string(const unsigned char*str,const unsigned char*title,Value*stack,Sint16 sp) { + const unsigned char*ps=str; // pointer of string + const unsigned char*pt=title; // pointer of title + const unsigned char*so=0; // start offset + const unsigned char*eo=0; // end offset + const unsigned char*pp=pt; + const unsigned char*q; + int i,j,k; + while(*ps) { + switch(*ps++) { + case '=': + k=*ps++; + if(k<32) return 64; + while(*ps && *ps!=k && *pt==*ps) ps++,pt++; + if(*ps!=k) goto notfound; + ps++; + break; + case '/': + k=*ps++; + if(k<32) return 64; + if(*ps==k) goto bad; + q=strchr(ps,k); + if(!q) goto bad; + j=q-ps; + while(*pt) { + pt=ll_find(pt,*ps); + if(!pt) goto notfound; + for(i=1;i='0' && *pt<='9') pt++; + break; + case '_': + while(*pt==' ' || *pt==10) pt++; + break; + case '\\': + pt=ll_find(pt,'\\'); + if(!pt) goto notfound; + pp=pt++; + break; + case '.': + if(*pt==31 && pt[1]) pt+=2; + else if(*pt==14 || *pt==30) pt=strchr(pt,'\\'),pt+=pt?1:0; + else if(*pt) pt++; + if(!pt) return 64; + break; + case '<': pt=title; break; + case '>': pt+=strlen(pt); break; + case '#': + if(!so || !eo || so>eo) break; + k=0; + while(*so>='0' && *so<='9') k=10*k+*so++-'0'; + stack[sp++]=NVALUE(k); + stack[sp++]=NVALUE(1); + return sp; + case '$': + if(!so || !eo || so>eo) break; + stack[sp++]=UVALUE(((so-title)<<16)|(eo-title),TY_FOR); + stack[sp++]=NVALUE(1); + return sp; + case '%': + if(!so || !eo || so>eo) break; + stack[sp++]=NVALUE(eo-so); + stack[sp++]=NVALUE(1); + return sp; + default: bad: + if(main_options['v']) fprintf(stderr,"Invalid character (0x%02X) in string in LevelTable definition\n",ps[-1]); + return 64; + } + } + notfound: stack[sp++]=NVALUE(0); return sp; +} static inline Uint32 ll_in(Value*stack,Sint16 top,Sint16 m) { while(--top>m) if(stack[top].t==stack[m-1].t && stack[top].u==stack[m-1].u) return 1; return 0; } +#define ArithOp1(aa) ({ if(sp<1 || stack[sp-1].t) goto bad; Value t1=stack[sp-1]; stack[sp-1]=NVALUE(aa); }) +#define ArithOp2(aa) ({ if(sp<2 || stack[sp-1].t || stack[sp-2].t) goto bad; Value t1=stack[sp-2]; Value t2=stack[sp-1]; sp--; stack[sp-1]=NVALUE(aa); }) +#define ArithDivOp2(aa) ({ if(sp<2 || stack[sp-1].t || stack[sp-2].t || !stack[sp-1].u) goto bad; Value t1=stack[sp-2]; Value t2=stack[sp-1]; sp--; stack[sp-1]=NVALUE(aa); }) +#define OtherOp1(aa) ({ if(sp<1) goto bad; Value t1=stack[sp-1]; stack[sp-1]=(aa); }) +#define OtherOp2(aa) ({ if(sp<2) goto bad; Value t1=stack[sp-2]; Value t2=stack[sp-1]; sp--; stack[sp-1]=(aa); }) + static Sint16 level_table_exec(Uint16 ptr,const unsigned char*lvl,long sz,ObjInfo*ob,Value*stack,Value*agg) { Sint16 sp=0; Uint32 k,m; while(sp<62) switch(ll_code[ptr++]) { case 0 ... 255: stack[sp++]=NVALUE(ll_code[ptr-1]); break; @@ -1243,10 +1381,12 @@ case OP_DELTA: ArithOp2(t1.u>t2.u?t1.u-t2.u:t2.u-t1.u); break; case OP_DENSITY: if(!ob) goto bad; stack[sp++]=NVALUE(classes[ob->class]->density); break; case OP_DIR: if(!ob) goto bad; stack[sp++]=NVALUE(ob->dir); break; case OP_DIV: ArithDivOp2(t1.u/t2.u); break; case OP_DIV_C: ArithDivOp2(t1.s/t2.s); break; + case OP_DROP: if(sp) --sp; break; + case OP_DUP: if(!sp) goto bad; stack[sp]=stack[sp-1]; sp++; break; case OP_EQ: OtherOp2(NVALUE((t1.u==t2.u && t1.t==t2.t)?1:0)); break; case OP_GE: ArithOp2(t1.u>=t2.u?1:0); break; case OP_GE_C: ArithOp2(t1.s>=t2.s?1:0); break; case OP_GOTO: ptr=ll_code[ptr]; break; case OP_GT: ArithOp2(t1.u>t2.u?1:0); break; @@ -1296,19 +1436,28 @@ case OP_MUL: ArithOp2(t1.u*t2.u); break; case OP_MUL_C: ArithOp2(t1.s*t2.s); break; case OP_NE: OtherOp2(NVALUE((t1.u==t2.u && t1.t==t2.t)?0:1)); break; case OP_NEG: ArithOp1(-t1.s); break; case OP_PLAYER: if(!ob) goto bad; stack[sp++]=NVALUE(classes[ob->class]->cflags&CF_PLAYER?1:0); break; + case OP_QC: OtherOp1(NVALUE(t1.t==TY_CLASS?1:0)); break; + case OP_QM: OtherOp1(NVALUE(t1.t==TY_MESSAGE?1:0)); break; + case OP_QN: OtherOp1(NVALUE(t1.t==TY_NUMBER?1:0)); break; case OP_RET: return sp; case OP_RSH: ArithOp2(t2.u&~31?0:t1.u>>t2.u); break; case OP_RSH_C: ArithOp2(t2.u&~31?(t1.s<0?-1:0):t1.s>>t2.u); break; case OP_STRING: - // If starting with @ then a literal string, otherwise match a part of the level title. - //TODO + k=ll_code[ptr++]; + if(stringpool[k][0]=='@') stack[sp++]=UVALUE(k,TY_STRING); else sp=ll_string(stringpool[k],lvl+6,stack,sp); break; case OP_SUB: ArithOp2(t1.u-t2.u); break; case OP_TEMPERATURE: if(!ob) goto bad; stack[sp++]=NVALUE(classes[ob->class]->temperature); break; + case OP_TRACE: + if(main_options['t'] && main_options['v']) { + printf("'' ptr=0x%04X sp=%d ll_ndata=%d ll_naggregate=%d\n",ptr,sp,ll_ndata,ll_naggregate); + for(k=0;kclass]->misc4; else if(ll_code[ptr]<0x1040) k=classes[ob->class]->misc5; else if(ll_code[ptr]<0x1060) k=classes[ob->class]->misc6; @@ -1324,10 +1473,31 @@ } bad: if(main_options['v']) fprintf(stderr,"Error in LevelTable definition (at 0x%04X)\n",ptr); return -1; } + +static void ll_append_str(sqlite3_str*str,const unsigned char*p,long n) { + const unsigned char*end=p+n; + while(p=32) { + sqlite3_str_appendchar(str,1,*p++); + } else if(*p==31) { + p++; + if(pt==TY_MARK) { sqlite3_bind_null(st,i+8); - } else if(j>1 && (stack->t==TY_STRING || stack->t==TY_FOR)) { - //TODO - } else if(stack->t==TY_FOR) { - sqlite3_bind_blob(st,i+8,lvl+(stack->u>>16)+6,stack->u&0xFFFF,SQLITE_TRANSIENT); - } else if(stack->t==TY_STRING) { - sqlite3_bind_blob(st,i+8,stringpool[stack->u]+1,strlen(stringpool[stack->u]+1),0); + } else if(stack->t==TY_STRING || stack->t==TY_FOR || stack->t==TY_LEVELSTRING) { + sqlite3_str*str=sqlite3_str_new(userdb); + for(n=0;nname) sqlite3_str_appendf(str,"%s",classes[z]->name); + break; + case TY_LEVELSTRING: + if(stack[n].u>=nlevelstrings) break; + ll_append_str(str,levelstrings[stack[n].u],strlen(levelstrings[stack[n].u])); + break; + case TY_STRING: + sqlite3_str_appendall(str,stringpool[stack[n].u]+1); + break; + case TY_FOR: + ll_append_str(str,lvl+(stack[n].u>>16)+6,stack[n].u&0xFFFF); + break; + // Otherwise, do nothing + } + if(z=sqlite3_str_length(str)) { + sqlite3_bind_blob(st,i+8,sqlite3_str_finish(str),z,sqlite3_free); + } else { + sqlite3_free(sqlite3_str_finish(str)); + sqlite3_bind_zeroblob(st,i+8,0); + } } else { - if(ll_data[i].sgn) sqlite3_bind_int64(st,i+8,stack->s); else sqlite3_bind_int64(st,i+8,stack->u); break; + if(ll_data[i].sgn) sqlite3_bind_int64(st,i+8,stack->s); else sqlite3_bind_int64(st,i+8,stack->u); } } } static int vt1_levels_open(sqlite3_vtab*vt,sqlite3_vtab_cursor**cur) { Index: game.c ================================================================== --- game.c +++ game.c @@ -729,12 +729,12 @@ sqlite3_int64 v; if(t==SQLITE_NULL) return; if(dc->flag&1) w=255; if(t==SQLITE_BLOB || t==SQLITE_TEXT) { blob: - i=copy_text_to_plain(buf,w,sqlite3_column_text(st,nc)); - if(dc->form[0]=='R') a=w-i; + i=snprintf(buf,w,"%s",sqlite3_column_text(st,nc)); + if(dc->form[0]=='R' && iflag&2) co=0xFF; } else { // This implementation does not check that the format is necessarily valid. // You should not rely on the use of any undocumented format. v=sqlite3_column_int64(st,nc);