Index: function.c ================================================================== --- function.c +++ function.c @@ -30,10 +30,29 @@ } static void fn_picture_size(sqlite3_context*cxt,int argc,sqlite3_value**argv) { sqlite3_result_int(cxt,picture_size); } + +static void fn_read_lump_at(sqlite3_context*cxt,int argc,sqlite3_value**argv) { + sqlite3_int64 of=sqlite3_value_int64(argv[0]); + FILE*fp=sqlite3_value_pointer(argv[1],"http://zzo38computer.org/fossil/heromesh.ui#FILE_ptr"); + long sz; + Uint8*buf; + if(!fp) return; + rewind(fp); + fseek(fp,of-4,SEEK_SET); + sz=fgetc(fp)<<16; sz|=fgetc(fp)<<24; sz|=fgetc(fp); sz|=fgetc(fp)<<8; + if(sz>0) { + buf=malloc(sz); + if(!buf) fatal("Allocation failed\n"); + if(fread(buf,1,sz,fp)<=0) fatal("I/O error in READ_LUMP_AT() function\n"); + sqlite3_result_blob64(cxt,buf,sz,free); + } else { + sqlite3_result_zeroblob64(cxt,0); + } +} static void fn_resource(sqlite3_context*cxt,int argc,sqlite3_value**argv) { int i; if(argc>14 || argc<1) { sqlite3_result_error(cxt,"Invalid number of XRM resource components",-1); @@ -186,10 +205,11 @@ void init_sql_functions(sqlite3_int64*ptr0,sqlite3_int64*ptr1) { sqlite3_create_function(userdb,"BASENAME",0,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_basename,0,0); sqlite3_create_function(userdb,"LEVEL_CACHEID",0,SQLITE_UTF8|SQLITE_DETERMINISTIC,ptr0,fn_cacheid,0,0); sqlite3_create_function(userdb,"MODSTATE",0,SQLITE_UTF8,0,fn_modstate,0,0); sqlite3_create_function(userdb,"PICTURE_SIZE",0,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_picture_size,0,0); + sqlite3_create_function(userdb,"READ_LUMP_AT",2,SQLITE_UTF8,0,fn_read_lump_at,0,0); sqlite3_create_function(userdb,"RESOURCE",-1,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_resource,0,0); sqlite3_create_function(userdb,"SIGN_EXTEND",1,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_sign_extend,0,0); sqlite3_create_function(userdb,"SOLUTION_CACHEID",0,SQLITE_UTF8|SQLITE_DETERMINISTIC,ptr1,fn_cacheid,0,0); sqlite3_create_module(userdb,"MESSAGES",&vt_messages,"CREATE TABLE `MESSAGES`(`ID` INTEGER PRIMARY KEY, `NAME` TEXT, `TRACE` INT);"); } Index: main.c ================================================================== --- main.c +++ main.c @@ -48,31 +48,12 @@ static const char*globalclassname; static SDL_Cursor*cursor[77]; static FILE*levelfp; static FILE*solutionfp; static sqlite3_int64 leveluc,solutionuc; -static FILE*hamarc_fp; -static long hamarc_pos; static sqlite3_stmt*readusercachest; -static void hamarc_begin(FILE*fp,const char*name) { - while(*name) fputc(*name++,fp); - fwrite("\0\0\0\0",1,5,hamarc_fp=fp); - hamarc_pos=ftell(fp); -} - -static long hamarc_end(void) { - long end=ftell(hamarc_fp); - long len=end-hamarc_pos; - fseek(hamarc_fp,hamarc_pos-4,SEEK_SET); - fputc(len>>16,hamarc_fp); - fputc(len>>24,hamarc_fp); - fputc(len>>0,hamarc_fp); - fputc(len>>8,hamarc_fp); - fseek(hamarc_fp,end,SEEK_SET); -} - static sqlite3_int64 reset_usercache(FILE*fp,const char*nam,struct stat*stats,const char*suffix) { sqlite3_stmt*st; sqlite3_int64 t,id; char buf[128]; int i,z; @@ -188,13 +169,99 @@ sqlite3_bind_blob64(st,4,data,sz,0); while((e=sqlite3_step(st))==SQLITE_ROW); if(e!=SQLITE_DONE) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); sqlite3_finalize(st); } + +static void flush_usercache_1(int sol) { + sqlite3_int64 uc_id=sol?solutionuc:leveluc; + FILE*fp=sol?solutionfp:levelfp; + int fd=fileno(fp); + sqlite3_stmt*st; + sqlite3_int64 of,of2; + sqlite3_int64*ofs; + int e,i,j,c; + if(fd<0) fatal("Unable to flush user cache. Expected file descriptor number; found %d\n",fd); + if(e=sqlite3_prepare_v2(userdb,"SELECT `OFFSET`, `OFFSET`-LENGTH(`NAME`)-5 FROM `USERCACHEDATA` WHERE `FILE` = ?1 AND `DATA` NOT NULL ORDER BY `OFFSET` LIMIT 1;",-1,&st,0)) + fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + sqlite3_bind_int64(st,1,uc_id); + e=sqlite3_step(st); + if(e==SQLITE_ROW) { + of=sqlite3_column_int64(st,0); + of2=sqlite3_column_int64(st,1); + } else if(e==SQLITE_DONE) { + // There is nothing to do; nothing has been changed. + sqlite3_finalize(st); + return; + } else { + fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + } + sqlite3_finalize(st); + if(e=sqlite3_prepare_v2(userdb,"UPDATE `USERCACHEDATA` SET `DATA` = READ_LUMP_AT(`OFFSET`,?2) WHERE `FILE` = ?1 AND `OFFSET` > ?3 AND `DATA` IS NULL;",-1,&st,0)) + fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + sqlite3_bind_int64(st,1,uc_id); + sqlite3_bind_pointer(st,2,fp,"http://zzo38computer.org/fossil/heromesh.ui#FILE_ptr",0); + sqlite3_bind_int64(st,3,of); + while((e=sqlite3_step(st))==SQLITE_ROW); + if(e!=SQLITE_DONE) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + sqlite3_finalize(st); + if(e=sqlite3_prepare_v2(userdb,"SELECT COUNT() FROM `USERCACHEDATA` WHERE `FILE` = ?1 AND `DATA` NOT NULL;",-1,&st,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + sqlite3_bind_int64(st,1,uc_id); + if((e=sqlite3_step(st))!=SQLITE_ROW) fatal("SQL error (%d): %s\n",e,e==SQLITE_DONE?"Expected a result row; got nothing":sqlite3_errmsg(userdb)); + ofs=malloc((c=sqlite3_column_int(st,0)<<1)*sizeof(sqlite3_int64)); + sqlite3_finalize(st); + if(c<=0) fatal("Unexpected error: COUNT() returned zero or negative even though there used to be some data\n"); + if(!ofs) fatal("Allocation failed\n"); + if(e=sqlite3_prepare_v2(userdb,"SELECT `ID`, `NAME`, `DATA` FROM `USERCACHEDATA` WHERE `FILE` = ?1 AND `DATA` NOT NULL ORDER BY `ID`;",-1,&st,0)) + fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + sqlite3_bind_int64(st,1,uc_id); + rewind(fp); + fseek(fp,of2,SEEK_SET); + i=0; + while((e=sqlite3_step(st))==SQLITE_ROW) { + ofs[i++]=sqlite3_column_int64(st,0); + if(sqlite3_column_type(st,1)!=SQLITE_TEXT || sqlite3_column_type(st,2)!=SQLITE_BLOB) fatal("Corrupted user cache database (NAME and DATA columns have wrong types)\n"); + fwrite(sqlite3_column_text(st,1),1,sqlite3_column_bytes(st,1)+1,fp); + j=sqlite3_column_bytes(st,2); + fputc(j>>16,fp); fputc(j>>24,fp); fputc(j,fp); fputc(j>>8,fp); + ofs[i++]=ftell(fp); + fwrite(sqlite3_column_blob(st,2),1,j,fp); + } + if(e!=SQLITE_DONE) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + if(ftruncate(fd,ftell(fp))) fatal("I/O error: %m\n"); + sqlite3_finalize(st); + if(e=sqlite3_prepare_v2(userdb,"UPDATE `USERCACHEDATA` SET `OFFSET` = ?2 WHERE `ID` = ?1;",-1,&st,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb)); + i=0; + while(i