Overview
Comment: | More corrections, and more work on implementation, of custom level table definitions. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
702847e4701bfe25ba300ad2cb9b3384 |
User & Date: | user on 2021-12-27 20:33:50 |
Other Links: | manifest | tags |
Context
2021-12-29
| ||
00:04 | Add a entry to the Frequently Asked Questions section about use of Free Hero Mesh with gdb. check-in: b3b7b53f2b user: user tags: trunk | |
2021-12-27
| ||
20:33 | More corrections, and more work on implementation, of custom level table definitions. check-in: 702847e470 user: user tags: trunk | |
2021-12-25
| ||
06:33 | Some corrections of custom level table, including initializing the schema properly; start a few more parts of its implementation too. check-in: 66cf1d39ad user: user tags: trunk | |
Changes
Modified class.c from [64b3d009e0] to [7cb8ff67cc].
︙ | ︙ | |||
2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 | 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; 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 | > | 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 | 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 |
︙ | ︙ | |||
2368 2369 2370 2371 2372 2373 2374 | 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; | < < | | > | 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 | 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); } |
︙ | ︙ |
Modified class.doc from [9f8228ce84] to [e3fcdbff9c].
︙ | ︙ | |||
2583 2584 2585 2586 2587 2588 2589 | === Level table definition === The following blocks are allowed in a level table definition: (<label> <code...>) Define a data column. The SQL name of the data column will be that of the label name, preceded by a underscore, and some characters will be | | > > > > > > > > | > > | > | > | > > > > > > > > > > > | 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 | === Level table definition === The following blocks are allowed in a level table definition: (<label> <code...>) Define a data column. The SQL name of the data column will be that of the label name, preceded by a underscore, and some characters will be stripped out; note that SQL is case-insensitive. (Other implementations or ports of Free Hero Mesh might not implement it as a SQL column, or at all. For best portability, names should contain only ASCII letters, digits, and underscores, and should not start with a digit, and you should not define two or more data columns whose names differ only by their case. Aggregates are not subject to this restriction, though.) The label name must have a colon at front; if a comma is also added then numeric values of this column are treated as signed. (<string> <label> <width> <format> <color>) Defines a display column. The <label> is the name of the data column. The <width> is a number 1 to 255 or it can be * for fill width; only the last column is allowed to be fill width (although it is also OK if no colums are fill width). The <format> is a string of exactly one or two characters; see the below list of possible formats. The <color> is the text colour, and is optional; it can be a number from 1 to 255, or it can be a sequence of parenthesized pairs of a number (-127 to +127) and colour, to mean use those colour if the value of this column is in range. It uses the colour for all values up to and including the specified number, so the numbers should be listed in ascending order, in order to work. The <string> is the heading; it is not allowed to include control characters, nor can it include graphic characters with code number less than 32. (<local> <type> <code...>) Defines a aggregate calculation. The <local> is the name of the aggregate to be referred by other blocks (which must start with a percentage sign, like a local variable does). The <type> is the type of aggregate, which can be + (sum of all values), min (least value), or max (greatest value); any one with a comma at first means it is a signed aggregate. The valid formats are: * "L" or "R" = Left or right aligned text. * "L*" or "R*" replacing * with any character = Left or right aligned displaying the specified character repeated as many times as the column value is. * "d", "d-", "d+", "x", "x0", "X", "X0", "o", "o0", "u" = Display a number (right aligned) as signed decimal (d), hexadecimal (x/X), octal (o), unsigned decimal (u). The ones with "0" will pad by 0 instead of spaces, and the "d-" and "d+" will display a left aligned sign. If a numeric format is specified but it is a text value, then it is treated as "L" instead, unless format is "R*" in which case it is treated as "R". (TODO: Mention valid instructions.) |
Modified game.c from [d6b8ec0435] to [b19cf21fe8].
︙ | ︙ | |||
696 697 698 699 700 701 702 | if(i&~0xFFFF) return; if(replay_size<i) replay_list=realloc(replay_list,replay_size=i); if(!replay_list) fatal("Allocation failed"); replay_count=i; if(i) memcpy(replay_list,sqlite3_column_blob(st,1),i); } | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 | if(i&~0xFFFF) return; if(replay_size<i) replay_list=realloc(replay_list,replay_size=i); if(!replay_list) fatal("Allocation failed"); replay_count=i; if(i) memcpy(replay_list,sqlite3_column_blob(st,1),i); } static int copy_text_to_plain(unsigned char*out,int maxlen,const unsigned char*in) { int at=0; if(!in) { *out=0; return 0; } while(*in && at<maxlen) switch(*in) { case 10: if(at && out[at-1]!=32) out[at++]=32; in++; break; case 14: case 30: in=strchrnul(in,'\\'); if(*in) in++; break; case 31: in++; if(*in) out[at++]=*in++; break; case 32 ... 255: out[at++]=*in++; break; default: in++; } out[at]=0; return at; } static inline void levels_column(int x,int y,int n,int bg,sqlite3_stmt*st,char*buf) { const DisplayColumn*dc=ll_disp+n; Uint8 co=dc->color; int w=dc->width; int nc=dc->data+(dc->flag&4?0:8); int t=sqlite3_column_type(st,nc); int a=0; int i; const char*p; 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; if(dc->flag&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); if(dc->flag&2) { //TODO: colours } switch(dc->form[0]) { case 'L': case 'R': if(dc->form[1]) { if(v<0 || v>w) v=w; memset(buf,dc->form[1],v); buf[v]=0; if(dc->form[0]=='R') a=w-v; } else { goto blob; } break; case 'd': snprintf(buf,w+1,"%*lld",w,(long long)(dc->form[1] && v<0?-v:v)); if(dc->form[1]=='-') *buf=v<0?'-':' '; if(dc->form[1]=='+') *buf=v<0?'-':'+'; break; case 'o': snprintf(buf,w+1,dc->form[1]?"%0*llo":"%*llo",w,(unsigned long long)v); break; case 'u': snprintf(buf,w+1,dc->form[1]?"%0*llu":"%*llu",w,(unsigned long long)v); break; case 'x': snprintf(buf,w+1,dc->form[1]?"%0*llx":"%*llx",w,(unsigned long long)v); break; case 'X': snprintf(buf,w+1,dc->form[1]?"%0*llX":"%*llX",w,(unsigned long long)v); break; default: *buf=0; } } draw_text(a*8+x,y,buf,bg,co); } static int list_levels(void) { static Sint8 mo=-1; static Uint8 columns=0; static int scroll=0; sqlite3_stmt*st; |
︙ | ︙ | |||
784 785 786 787 788 789 790 | draw_text(2*8,y,"\x10",b=0xF8,0xFE); } else { draw_text(2*8,y,"\xB3",b=0x02,0xF8); } snprintf(buf,6,"%5u",mo&2?sqlite3_column_int(st,0):i); draw_text(3*8,y,buf,b,sqlite3_column_int(st,6)?0xFA:0xFC); draw_text(8*8,y,"\xB3",b,b^0xFA); | > > > > > > > > | | | | | | > | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | draw_text(2*8,y,"\x10",b=0xF8,0xFE); } else { draw_text(2*8,y,"\xB3",b=0x02,0xF8); } snprintf(buf,6,"%5u",mo&2?sqlite3_column_int(st,0):i); draw_text(3*8,y,buf,b,sqlite3_column_int(st,6)?0xFA:0xFC); draw_text(8*8,y,"\xB3",b,b^0xFA); if(ll_ndisp) { x=9*8; for(i=0;i<ll_ndisp && x<screen->w;i++) { levels_column(x,y,i,b,st,buf); x+=ll_disp[i].width*8+8; if(x<screen->w && !(ll_disp[i].flag&1)) draw_text(x-8,y,"\xB3",b,b^0xFA); } } else { snprintf(buf,6,"%2u %2u",sqlite3_column_int(st,3),sqlite3_column_int(st,4)); draw_text(9*8,y,buf,b,0xFF); draw_text(11*8,y,"\xB3",b,b^0xFA); draw_text(14*8,y,"\xB3",b,b^0xFA); copy_text_to_plain(buf,255,sqlite3_column_text(st,5)); draw_text(15*8,y,buf,b,0xFF); } } } else { scrmax=(level_nindex+columns-1)/columns; draw_text(0,16,mo&2?"(ID)":"(Ord)",0xF7,0xF1); if(rescroll) { if(sel<scroll*columns) scroll=sel/columns; if(sel>(scroll+screen->h/8-3)*columns) scroll=(sel+3-screen->h/8)/columns; |
︙ | ︙ |
Modified heromesh.h from [39284f6f26] to [641a517ed3].
︙ | ︙ | |||
192 193 194 195 196 197 198 199 200 201 202 203 204 205 | typedef struct { union { char*name; // free when loading table Uint8 ag; // aggregate type }; Uint16 ptr; } DataColumn; extern char*ll_head; extern DisplayColumn*ll_disp; extern Uint8 ll_ndisp; extern DataColumn*ll_data; extern Uint8 ll_ndata; | > | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | typedef struct { union { char*name; // free when loading table Uint8 ag; // aggregate type }; Uint16 ptr; Uint8 sgn; // 0=unsigned, 1=signed } DataColumn; extern char*ll_head; extern DisplayColumn*ll_disp; extern Uint8 ll_ndisp; extern DataColumn*ll_data; extern Uint8 ll_ndata; |
︙ | ︙ |