Index: picedit.c ================================================================== --- picedit.c +++ picedit.c @@ -21,10 +21,44 @@ typedef struct { Uint8 size,meth; Uint8 data[0]; // the first row is all 0, since the compression algorithm requires this } Picture; +typedef struct { + Uint8 colors[64]; + Uint8 ncolors; +} ColorFilter; + +typedef struct { + char name[64]; +} OverlayFilter; + +typedef struct { + Uint8 shift[32]; + Uint8 size[32]; + Uint8 nshift; +} ShiftFilter; + +typedef struct { + Uint8 code; + // 0-7 = flip/rotations + // 8-10 = change colours (8 is *->* *->* *->*; 9 is \->*->*->*->/; 10 is *<->* *<->* *<->*) + // 11 = overlay + // 12-15 = shift (up, down, right, left) + union { + ColorFilter color; + OverlayFilter overlay; + ShiftFilter shift; + }; +} Filter; + +typedef struct { + char basename[64]; + Filter filters[64]; + Uint8 nfilters; +} DependentPicture; + static Uint8 cur_type; static void fn_valid_name(sqlite3_context*cxt,int argc,sqlite3_value**argv) { const char*s=sqlite3_value_text(*argv); if(!s || !*s || s[strspn(s,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-0123456789")]) { @@ -256,11 +290,11 @@ static void compress_picture(FILE*out,Picture*pic) { int bm=15; int bs=pic->size*pic->size; FILE*fp=fmemopen(0,bs,"w"); int i,j; - if(!fp) fatal("Error with fmemopen"); + if(!fp) fatal("Error with fmemopen\n"); for(i=0;i<8;i++) { compress_picture_1(fp,pic); if(!ferror(fp)) { j=ftell(fp); if(j>0 && jbasename,s,63); + return; + } + fp=fmemopen((Uint8*)data,size,"r"); + if(!fp) fatal("Error with fmemopen\n"); + i=0; + for(;;) { + j=fgetc(fp); + if(j<32) { + if(j>0) ungetc(j,fp); + break; + } + if(i<63) dp->basename[i++]=j; + } + while(dp->nfilters<64) { + if((j=fgetc(fp))<0) break; + switch(dp->filters[i=dp->nfilters++].code=j) { + case 8 ... 10: + j=dp->filters[i].color.ncolors=fgetc(fp); + if(j<0 || j>64) j=64; + fread(dp->filters[i].color.colors,1,j,fp); + break; + case 11: + k=0; + for(;;) { + j=fgetc(fp); + if(j<32) { + if(j>0) ungetc(j,fp); + break; + } + if(k<63) dp->filters[i].overlay.name[k++]=j; + } + break; + case 12 ... 15: + for(k=0;k<64;k++) { + dp->filters[i].shift.shift[k]=(j=fgetc(fp))&127; + dp->filters[i].shift.size[k]=fgetc(fp); + if(j&128) break; + } + dp->filters[i].shift.nshift=k; + break; + } + } + fclose(fp); +} + +static void save_dependent_picture(FILE*fp,DependentPicture*dp) { + int i,j; + fwrite(dp->basename,1,strlen(dp->basename)+(dp->filters[0].code<32?0:1),fp); + for(i=0;infilters;i++) { + fputc(dp->filters[i].code,fp); + switch(dp->filters[i].code) { + case 8 ... 10: + fputc(dp->filters[i].color.ncolors,fp); + fwrite(dp->filters[i].color.colors,1,dp->filters[i].color.ncolors,fp); + break; + case 11: + fwrite(dp->filters[i].overlay.name,1,strlen(dp->filters[i].overlay.name)+(infilters-1&&dp->filters[i+1].code<32?0:1),fp); + break; + case 12 ... 15: + if(!dp->filters[i].shift.nshift) fwrite("\x80",1,2,fp); + for(j=0;jfilters[i].shift.nshift;j++) { + fputc(dp->filters[i].shift.shift[j]|(j==dp->filters[i].shift.nshift-1?128:0),fp); + fputc(dp->filters[i].shift.size[j],fp); + } + break; + } + } +} + +static void edit_dependent_picture(DependentPicture*dp,const char*name) { + +} static void edit_picture(sqlite3_int64 id) { sqlite3_stmt*st; Picture*pict[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + DependentPicture*dpict; char*name; const unsigned char*data; Uint8*buf=0; size_t size=0; FILE*fp; int i,n; - if(i=sqlite3_prepare_v2(userdb,"SELECT SUBSTR(`NAME`,1,LENGTH(`NAME`)-4),`DATA` FROM `PICEDIT` WHERE `ID`=?1;",-1,&st,0)) { + if(i=sqlite3_prepare_v2(userdb,"SELECT SUBSTR(`NAME`,1,LENGTH(`NAME`)-4),`DATA`,`TYPE` FROM `PICEDIT` WHERE `ID`=?1;",-1,&st,0)) { screen_message(sqlite3_errmsg(userdb)); return; } sqlite3_bind_int64(st,1,id); i=sqlite3_step(st); @@ -891,36 +1006,50 @@ screen_message(i==SQLITE_DONE?"No such ID":sqlite3_errmsg(userdb)); sqlite3_finalize(st); return; } data=sqlite3_column_blob(st,1); - i=sqlite3_column_bytes(st,1); - load_picture_lump(data,i,pict); + n=sqlite3_column_bytes(st,1); + i=sqlite3_column_int(st,2); name=strdup(sqlite3_column_text(st,0)?:(const unsigned char*)"???"); if(!name) fatal("Allocation failed\n"); - sqlite3_finalize(st); - if(!*pict) { - i=picture_size; - *pict=malloc(sizeof(Picture)+(i+1)*i); - if(!*pict) fatal("Allocation failed"); - pict[0]->size=i; - memset(pict[0]->data,0,(i+1)*i); - } - edit_picture_1(pict,name); - free(name); - fp=open_memstream((char**)&buf,&size); - if(!fp) fatal("Cannot open memory stream\n"); - for(i=n=0;i<16;i++) if(pict[i]) n++; - fputc(n,fp); - for(i=0;isize,fp); - for(i=0;i>1;i++) fputc(0,fp); - for(i=0;imeth<<4; - for(i=1;i>1)]|=pict[i]->meth<<(i&1?0:4); - for(i=0;i<16;i++) free(pict[i]); + if(i==1) { + load_picture_lump(data,n,pict); + sqlite3_finalize(st); + if(!*pict) { + i=picture_size; + *pict=malloc(sizeof(Picture)+(i+1)*i); + if(!*pict) fatal("Allocation failed\n"); + pict[0]->size=i; + memset(pict[0]->data,0,(i+1)*i); + } + edit_picture_1(pict,name); + fp=open_memstream((char**)&buf,&size); + if(!fp) fatal("Cannot open memory stream\n"); + for(i=n=0;i<16;i++) if(pict[i]) n++; + fputc(n,fp); + for(i=0;isize,fp); + for(i=0;i>1;i++) fputc(0,fp); + for(i=0;imeth<<4; + for(i=1;i>1)]|=pict[i]->meth<<(i&1?0:4); + for(i=0;i<16;i++) free(pict[i]); + } else if(i==2) { + dpict=malloc(sizeof(DependentPicture)); + if(!dpict) fatal("Allocation failed\n"); + load_dependent_picture(data,n,dpict); + fp=open_memstream((char**)&buf,&size); + if(!fp) fatal("Cannot open memory stream\n"); + edit_dependent_picture(dpict,name); + save_dependent_picture(fp,dpict); + free(dpict); + fclose(fp); + if(!buf || !size) fatal("Allocation failed\n"); + } + free(name); if(i=sqlite3_prepare_v2(userdb,"UPDATE `PICEDIT` SET `DATA`=?2 WHERE ID=?1;",-1,&st,0)) { screen_message(sqlite3_errmsg(userdb)); free(buf); return; } @@ -929,14 +1058,10 @@ i=sqlite3_step(st); if(i!=SQLITE_DONE) screen_message(sqlite3_errmsg(userdb)); sqlite3_finalize(st); } -static void edit_dependent_picture(sqlite3_int64 rowid) { - -} - static int add_picture(int t) { sqlite3_stmt*st; const char*s=screen_prompt("Enter name of new picture:"); int i; if(!s || !*s) return 0; @@ -950,12 +1075,11 @@ sqlite3_finalize(st); if(i!=SQLITE_DONE) { screen_message(sqlite3_errmsg(userdb)); return 0; } - if(t==1) edit_picture(sqlite3_last_insert_rowid(userdb)); - if(t==2) edit_dependent_picture(sqlite3_last_insert_rowid(userdb)); + edit_picture(sqlite3_last_insert_rowid(userdb)); return 1; } static int delete_picture(void) { sqlite3_stmt*st; @@ -1069,14 +1193,11 @@ case SDLK_F2: max-=delete_picture(); goto redraw; case SDLK_F3: *ids=ask_picture_id("Edit:"); - if(*ids) { - if(cur_type==1) edit_picture(*ids); - else if(cur_type==2) edit_dependent_picture(*ids); - } + if(*ids) edit_picture(*ids); goto redraw; case SDLK_F4: rename_picture(); goto redraw; case SDLK_F5: Index: puzzleset.doc ================================================================== --- puzzleset.doc +++ puzzleset.doc @@ -28,17 +28,18 @@ Nothing in this file affects the behaviour of the game, so you can safely replace it with a different set of pictures/sounds if wanted. The IMG lumps are the pictures. Each one may store multiple versions of a picture; the loader will select one based on the user's criteria. + +The DEP lumps are dependent pictures; each one references one or more IMG +lumps, and specifies how to modify them to produce the new picture (e.g. +by rotating, mirroring, changing colours, etc). The WAV lumps are sound effects. Each is a RIFF WAVE file; it must be of a type which can be loaded by SDL. (This is currently not used.) -Although not implemented yet, in future there may also be DIM lumps for -"dependent images", and possibly other lumps. - === Class definition file === This file is a plain text file which defines the classes of objects which are used in the game. See class.doc for details of its format.