126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
fseek(fp,t,SEEK_CUR);
}
done:
sqlite3_finalize(st);
return id;
}
unsigned char*read_lump(int sol,int lvl,long*sz,sqlite3_value**us) {
// Returns a pointer to the data; must be freed using free().
// If there is no data, returns null and sets *sz and *us to zero.
// Third argument is a pointer to a variable to store the data size (must be not null).
// Fourth argument may be null, and is user state (use sqlite3_value_free() to free it).
unsigned char*buf=0;
sqlite3_reset(readusercachest);
sqlite3_bind_int64(readusercachest,1,sol?solutionuc:leveluc);
sqlite3_bind_int(readusercachest,2,lvl);
if(sqlite3_step(readusercachest)==SQLITE_ROW) {
if(us) *us=sqlite3_value_dup(sqlite3_column_value(readusercachest,6));
if(sqlite3_column_type(readusercachest,5)==SQLITE_BLOB) {
const unsigned char*con=sqlite3_column_blob(readusercachest,5);
*sz=sqlite3_column_bytes(readusercachest,5);
buf=malloc(*sz);
if(*sz && !buf) fatal("Allocation failed\n");
memcpy(buf,con,*sz);
} else {
FILE*fp=sol?solutionfp:levelfp;
rewind(fp);
fseek(fp,sqlite3_column_int64(readusercachest,4)-4,SEEK_SET);
*sz=fgetc(fp)<<16; *sz|=fgetc(fp)<<24; *sz|=fgetc(fp); *sz|=fgetc(fp)<<8;
if(feof(fp) || *sz<0) fatal("Invalid Hamster archive\n");
buf=malloc(*sz);
if(!buf) fatal("Allocation failed\n");
if(!fread(buf,1,*sz,fp)) fatal("Unable to read data\n");
rewind(fp);
}
} else {
*sz=0;
if(us) *us=0;
}
sqlite3_reset(readusercachest);
return buf;
}
void write_lump(int sol,int lvl,long sz,const unsigned char*data) {
// Writes a lump to the user cache.
// The actual Hamster archive files will be updated when the program terminates.
sqlite3_stmt*st;
int e;
if(e=sqlite3_prepare_v2(userdb,"INSERT INTO `USERCACHEDATA`(`FILE`,`LEVEL`,`NAME`,`DATA`) VALUES(?1,?2,CASE WHEN ?2 < 0 THEN ?3 ELSE ?2 || ?3 END,?4)"
" ON CONFLICT(`FILE`,`LEVEL`) DO UPDATE SET `DATA` = ?4;",-1,&st,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
sqlite3_bind_int64(st,1,sol?solutionuc:leveluc);
sqlite3_bind_int(st,2,lvl);
sqlite3_bind_text(st,3,lvl==LUMP_CLASS_DEF?"CLASS.DEF":lvl==LUMP_LEVEL_IDX?"LEVEL.IDX":sol?".SOL":".LVL",-1,SQLITE_STATIC);
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 load_level_index(void) {
long sz;
int i;
unsigned char*data=read_lump(0,LUMP_LEVEL_IDX,&sz,0);
if(!data) return;
if(sz>65536) fatal("Too many levels\n");
level_index=malloc((level_nindex=sz>>1)*sizeof(Uint16));
if(!level_index) fatal("Allocation failed\n");
for(i=0;i<level_nindex;i++) level_index[i]=data[i+i]|(data[i+i+1]<<8);
free(data);
}
const char*load_level(int lvl) {
// Load level by ID. Returns null pointer if successful, or an error message if it failed.
long sz=0;
unsigned char*buf=lvl>=0?read_lump(FIL_LEVEL,lvl,&sz,0):0;
unsigned char*p=buf;
unsigned char*end=buf+sz;
unsigned char*q;
int i,n,x,y,z;
Uint16 lo=0;
Uint32 o;
Uint32 mru[2];
if(lvl<0 && level_index && -lvl<=level_nindex) {
lo=-lvl;
lvl=level_index[~lvl];
p=buf=read_lump(FIL_LEVEL,lvl,&sz,0);
end=buf+sz;
}
if(lvl<0) return "Invalid level ID";
if(!buf) return "Cannot find level";
free(level_title);
level_title=0;
annihilate();
|
|
|
>
<
|
|
|
>
>
|
<
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
|
fseek(fp,t,SEEK_CUR);
}
done:
sqlite3_finalize(st);
return id;
}
unsigned char*read_lump_or_userstate(int sol,int lvl,long*sz,char us) {
// Returns a pointer to the data; must be freed using free().
// If there is no data, returns null and sets *sz and *us to zero.
// Third argument is a pointer to a variable to store the data size (must be not null).
// Fourth argument is 1 for user state or 0 for lump data.
unsigned char*buf=0;
sqlite3_reset(readusercachest);
sqlite3_bind_int64(readusercachest,1,sol?solutionuc:leveluc);
sqlite3_bind_int(readusercachest,2,lvl);
sqlite3_bind_int(readusercachest,3,us);
if(sqlite3_step(readusercachest)==SQLITE_ROW) {
if(sqlite3_column_type(readusercachest,1)==SQLITE_BLOB) {
const unsigned char*con=sqlite3_column_blob(readusercachest,1);
*sz=sqlite3_column_bytes(readusercachest,1);
buf=malloc(*sz);
if(*sz && !buf) fatal("Allocation failed\n");
memcpy(buf,con,*sz);
} else if(us) {
*sz=0;
} else {
FILE*fp=sol?solutionfp:levelfp;
rewind(fp);
fseek(fp,sqlite3_column_int64(readusercachest,0)-4,SEEK_SET);
*sz=fgetc(fp)<<16; *sz|=fgetc(fp)<<24; *sz|=fgetc(fp); *sz|=fgetc(fp)<<8;
if(feof(fp) || *sz<0) fatal("Invalid Hamster archive\n");
buf=malloc(*sz);
if(!buf) fatal("Allocation failed\n");
if(!fread(buf,1,*sz,fp)) fatal("Unable to read data\n");
rewind(fp);
}
} else {
*sz=0;
}
sqlite3_reset(readusercachest);
return buf;
}
void write_lump(int sol,int lvl,long sz,const unsigned char*data) {
// Writes a lump to the user cache.
// The actual Hamster archive files will be updated when the program terminates.
sqlite3_stmt*st;
int e;
if(e=sqlite3_prepare_v2(userdb,"INSERT INTO `USERCACHEDATA`(`FILE`,`LEVEL`,`NAME`,`DATA`) VALUES(?1,?2,CASE WHEN ?2 < 0 THEN ?3 ELSE ?2 || ?3 END,?4)"
" ON CONFLICT(`FILE`,`LEVEL`) DO UPDATE SET `DATA` = ?4;",-1,&st,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
sqlite3_bind_int64(st,1,sol?solutionuc:leveluc);
sqlite3_bind_int(st,2,lvl);
sqlite3_bind_text(st,3,lvl==LUMP_CLASS_DEF?"CLASS.DEF":lvl==LUMP_LEVEL_IDX?"LEVEL.IDX":sol?".SOL":".LVL",-1,SQLITE_STATIC);
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);
}
void write_userstate(int sol,int lvl,long sz,const unsigned char*data) {
sqlite3_stmt*st;
int e;
if(e=sqlite3_prepare_v2(userdb,"UPDATE `USERCACHEDATA` SET `USERSTATE` = ?3"
" WHERE `FILE` = ?1 AND `LEVEL` = ?2;",-1,&st,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
sqlite3_bind_int64(st,1,sol?solutionuc:leveluc);
sqlite3_bind_int(st,2,lvl);
sqlite3_bind_blob64(st,3,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 load_level_index(void) {
long sz;
int i;
unsigned char*data=read_lump(FIL_LEVEL,LUMP_LEVEL_IDX,&sz);
if(!data) return;
if(sz>65536) fatal("Too many levels\n");
level_index=malloc((level_nindex=sz>>1)*sizeof(Uint16));
if(!level_index) fatal("Allocation failed\n");
for(i=0;i<level_nindex;i++) level_index[i]=data[i+i]|(data[i+i+1]<<8);
free(data);
}
const char*load_level(int lvl) {
// Load level by ID. Returns null pointer if successful, or an error message if it failed.
long sz=0;
unsigned char*buf=lvl>=0?read_lump(FIL_LEVEL,lvl,&sz):0;
unsigned char*p=buf;
unsigned char*end=buf+sz;
unsigned char*q;
int i,n,x,y,z;
Uint16 lo=0;
Uint32 o;
Uint32 mru[2];
if(lvl<0 && level_index && -lvl<=level_nindex) {
lo=-lvl;
lvl=level_index[~lvl];
p=buf=read_lump(FIL_LEVEL,lvl,&sz);
end=buf+sz;
}
if(lvl<0) return "Invalid level ID";
if(!buf) return "Cannot find level";
free(level_title);
level_title=0;
annihilate();
|
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
|
if(stat(nam2,&fst)) fatal("Unable to stat '%s': %m\n",nam2);
if(!fst.st_size) fatal("File '%s' has zero size\n",nam2);
if(fst.st_mtime>t1 || fst.st_ctime>t1) leveluc=reset_usercache(levelfp,nam2,&fst,".LVL");
if(stat(nam3,&fst)) fatal("Unable to stat '%s': %m\n",nam3);
if(fst.st_mtime>t2 || fst.st_ctime>t2) solutionuc=reset_usercache(solutionfp,nam3,&fst,".SOL");
free(nam2);
free(nam3);
if(z=sqlite3_prepare_v3(userdb,"SELECT * FROM `USERCACHEDATA` WHERE `FILE` = ?1 AND `LEVEL` = ?2;",-1,SQLITE_PREPARE_PERSISTENT,&readusercachest,0)) {
fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
}
if(z=sqlite3_exec(userdb,"COMMIT;",0,0,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
fprintf(stderr,"Done\n");
}
static void init_sql(void) {
|
>
|
|
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
|
if(stat(nam2,&fst)) fatal("Unable to stat '%s': %m\n",nam2);
if(!fst.st_size) fatal("File '%s' has zero size\n",nam2);
if(fst.st_mtime>t1 || fst.st_ctime>t1) leveluc=reset_usercache(levelfp,nam2,&fst,".LVL");
if(stat(nam3,&fst)) fatal("Unable to stat '%s': %m\n",nam3);
if(fst.st_mtime>t2 || fst.st_ctime>t2) solutionuc=reset_usercache(solutionfp,nam3,&fst,".SOL");
free(nam2);
free(nam3);
if(z=sqlite3_prepare_v3(userdb,"SELECT `OFFSET`, CASE WHEN ?3 THEN `DATA` ELSE `USERSTATE` END "
"FROM `USERCACHEDATA` WHERE `FILE` = ?1 AND `LEVEL` = ?2;",-1,SQLITE_PREPARE_PERSISTENT,&readusercachest,0)) {
fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
}
if(z=sqlite3_exec(userdb,"COMMIT;",0,0,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
fprintf(stderr,"Done\n");
}
static void init_sql(void) {
|