Overview
Comment: | Some more changes in game.c and in documentation, to work with the new move list encoding/decoding functions. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
c295ff21b6157bfc9e33807367f160dd |
User & Date: | user on 2022-06-28 03:57:49 |
Other Links: | manifest | tags |
Context
2022-06-28
| ||
04:45 | Use the move encoding functions in the level import function. check-in: 5463756bc9 user: user tags: trunk | |
03:57 | Some more changes in game.c and in documentation, to work with the new move list encoding/decoding functions. check-in: c295ff21b6 user: user tags: trunk | |
2022-06-26
| ||
04:05 | Use the new move list functions in move list import/export functions, and change the type of replay_size from Uint16 to size_t. check-in: cafa4fd29b user: user tags: trunk | |
Changes
Modified fileformat.doc from [490957ffa7] to [723b223c5e].
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 | of this lump has the following format: * Level version number (16-bits small-endian): If this does not match the level version number in the .LVL lump, then the solution is considered to be invalid. * Flags (8-bits): Specifies which other fields are present. * Comment (null-terminated; only present if flag bit0 set): Normally contains a user name, but may be any arbitrary text. * Timestamp (64-bits small-endian; only present if flag bit1 set): The UNIX timestamp when the solution was recorded. | > > > | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | of this lump has the following format: * Level version number (16-bits small-endian): If this does not match the level version number in the .LVL lump, then the solution is considered to be invalid. * Flags (8-bits): Specifies which other fields are present. * Score (signed 32-bits small-endian; only present if flag bit7 is set): The score of this level. Lower numbers are better. * Comment (null-terminated; only present if flag bit0 set): Normally contains a user name, but may be any arbitrary text. * Timestamp (64-bits small-endian; only present if flag bit1 set): The UNIX timestamp when the solution was recorded. |
︙ | ︙ |
Modified game.c from [dd24aafcf7] to [e7202bf383].
1 | #if 0 | | | 1 2 3 4 5 6 7 8 9 | #if 0 gcc ${CFLAGS:--s -O2} -c -Wno-multichar -fwrapv game.c `sdl-config --cflags` exit #endif /* This program is part of Free Hero Mesh and is public domain. */ |
︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | // Returns the number of bytes of the encoded move. fputc(v,fp); return 1; } int encode_move_list(FILE*fp) { // Encodes the current replay list into the file; returns the number of bytes. fwrite(replay_list,1,replay_count,fp); return replay_count; } MoveItem decode_move(FILE*fp) { // Decodes a single move from the file, and returns the move. // Returns zero if there is no more moves. int v=fgetc(fp); return (v==EOF?0:v); } int decode_move_list(FILE*fp) { // Decodes a move list from the file, and stores it in replay_list and replay_count. // Returns the number of moves (replay_count). MoveItem v; free(replay_list); replay_list=0; replay_size=0; replay_count=0; | > > | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | // Returns the number of bytes of the encoded move. fputc(v,fp); return 1; } int encode_move_list(FILE*fp) { // Encodes the current replay list into the file; returns the number of bytes. // Does not write a null terminator. fwrite(replay_list,1,replay_count,fp); return replay_count; } MoveItem decode_move(FILE*fp) { // Decodes a single move from the file, and returns the move. // Returns zero if there is no more moves. int v=fgetc(fp); return (v==EOF?0:v); } int decode_move_list(FILE*fp) { // Decodes a move list from the file, and stores it in replay_list and replay_count. // Returns the number of moves (replay_count). MoveItem v; FILE*o; free(replay_list); replay_list=0; replay_size=0; replay_count=0; o=open_memstream((char**)&replay_list,&replay_size); if(!o) fatal("Allocation failed\n"); while(replay_count<0xFFFE && (v=decode_move(fp))) { fwrite(&v,1,sizeof(MoveItem),o); replay_count++; } fclose(o); if(replay_count && !replay_list) fatal("Allocation failed\n"); return replay_count; } |
︙ | ︙ | |||
287 288 289 290 291 292 293 | SDL_LockSurface(screen); draw_text(0,40,buf,0xF0,0xF1); SDL_UnlockSurface(screen); SDL_Flip(screen); } static void save_replay(void) { | > | > | | < > | > > > > > > | > < | > | | < < < < > > > | > > > | > > > | > > | | < > | | | > > > > > | | > | < < < < < < < | < > | > > | < > | | | > > > | > > | | > > > > > > > | > > > > > > > > > > > | | > > > > | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | SDL_LockSurface(screen); draw_text(0,40,buf,0xF0,0xF1); SDL_UnlockSurface(screen); SDL_Flip(screen); } static void save_replay(void) { unsigned char*buf=0; size_t sz=0; FILE*fp; if(solution_replay || !replay_list || !replay_count) return; if(gameover==1) solved=1; fp=open_memstream((char**)&buf,&sz); if(!fp) fatal("Allocation failed\n"); fputc(0x00,fp); fputc(0x01,fp); encode_move_list(fp); fputc(0x00,fp); if(solved) { fputc(0x41,fp); fputc(level_version,fp); fputc(level_version>>8,fp); } if(replay_mark) { fputc(0x42,fp); fputc(replay_mark,fp); fputc(replay_mark>>8,fp); } fclose(fp); if(!buf) fatal("Allocation failed\n"); write_userstate(FIL_LEVEL,level_id,sz,buf); free(buf); } static void load_replay(void) { FILE*fp=0; unsigned char*buf=0; long sz; int i,j; free(replay_list); replay_list=0; replay_count=replay_mark=replay_size=0; if(solution_replay) { gameover_score=NO_SCORE; if(buf=read_lump(FIL_SOLUTION,level_id,&sz)) { fp=fmemopen(buf,sz,"r"); if(!fp) fatal("Allocation failed\n"); // Solution format: version (16-bits), flag (8-bits), score (32-bits), user name (null-terminated), timestamp (64-bits), move list if(sz>3) { i=fgetc(fp); i|=fgetc(fp)<<8; if(i==level_version) { j=fgetc(fp); if(j&128) { gameover_score=fgetc(fp); gameover_score|=fgetc(fp)<<8; gameover_score|=fgetc(fp)<<16; gameover_score|=fgetc(fp)<<24; } if(j&1) while(fgetc(fp)>0); if(j&2) for(i=0;i<8;i++) fgetc(fp); decode_move_list(fp); } } } } else if(buf=read_userstate(FIL_LEVEL,level_id,&sz)) { fp=fmemopen(buf,sz,"r"); if(!fp) fatal("Allocation failed\n"); if(sz>2 && *buf) { // Old format replay_count=(buf[sz-2]<<8)|buf[sz-1]; if(sz-replay_count>=4) replay_mark=(buf[replay_count]<<8)|buf[replay_count+1]; else replay_mark=0; if(sz-replay_count>=6) { i=(buf[replay_count+2]<<8)|buf[replay_count+3]; if(i==level_version) solved=1; } replay_list=malloc(replay_size=sizeof(MoveItem)*replay_count+1); if(!replay_list) fatal("Allocation failed\n"); for(i=0;i<replay_size;i++) replay_list[i]=buf[i]; } else { // New format fgetc(fp); // skip first null byte while((i=fgetc(fp))!=EOF) switch(i) { case 0x01: // Replay list if(replay_list) goto skip; decode_move_list(fp); break; case 0x41: // Solved version i=fgetc(fp); i|=fgetc(fp)<<8; if(i==level_version) solved=1; break; case 0x42: // Mark replay_mark=fgetc(fp); replay_mark|=fgetc(fp)<<8; break; default: skip: if(i<0x40) { while(fgetc(fp)>0); } else if(i<0x80) { fgetc(fp); fgetc(fp); } else if(i<0xC0) { for(i=0;i<4;i++) fgetc(fp); } else { for(i=0;i<8;i++) fgetc(fp); } } } } if(fp) fclose(fp); free(buf); } static void begin_level(int id) { const char*t; replay_time=0; if(replay_count) save_replay(); inputs_count=0; |
︙ | ︙ | |||
1326 1327 1328 1329 1330 1331 1332 | } timerflag=1; return n; } static inline void input_move(Uint8 k) { const char*t=execute_turn(k); | | | | < | | | > > | | | > > > > | | | > | | > > > | > > | | | < < > | | < > > > | < < < | < | > > > > | | | 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 | } timerflag=1; return n; } static inline void input_move(Uint8 k) { const char*t=execute_turn(k); if(replay_pos>0xFFFE && !gameover) t="Too many moves played"; if(t) { screen_message(t); gameover=-1; return; } if(!key_ignored) { if(inserting) { if(replay_pos>=0xFFFE || replay_pos==replay_count) { inserting=0; } else { if(replay_count>0xFFFE) replay_count=0xFFFE; if(replay_size<0xFFFF) { replay_list=realloc(replay_list,replay_size=0xFFFF); if(!replay_list) fatal("Allocation failed\n"); } memmove(replay_list+replay_pos+1,replay_list+replay_pos,replay_count-replay_pos); replay_count++; } } if(replay_pos>=replay_size) { if(replay_size>0xFFFF) replay_size=0xFFFF; replay_list=realloc(replay_list,replay_size+=0x200); if(!replay_list) fatal("Allocation failed\n"); } replay_list[replay_pos++]=k; if(replay_pos>replay_count) replay_count=replay_pos; } } static void record_solution(void) { const char*v; const char*com; FILE*fp; Uint8 flag; long n; unsigned char*buf=0; size_t sz=0; if(solution_replay) return; if(buf=read_lump(FIL_SOLUTION,level_id,&n)) { if(n<3 || (buf[0]|(buf[1]<<8))!=level_version || (buf[2]&~0x83)) goto dontkeep; n-=3; if((buf[2]&128) && n>4) { Sint32 sco=buf[3]|(buf[4]<<8)|(buf[5]<<16)|(buf[6]<<24); if(gameover_score!=NO_SCORE && sco<=gameover_score) goto dontkeep; } else { if(buf[2]&1) n-=strnlen(buf+3,n); if(buf[2]&2) n-=8; if(n<=0 || n>replay_pos) goto dontkeep; } free(buf); return; dontkeep: free(buf); buf=0; } optionquery[1]=Q_solutionComment; com=xrm_get_resource(resourcedb,optionquery,optionquery,2); if(com && !*com) com=0; optionquery[1]=Q_solutionTimestamp; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; flag=0; if(com) flag|=1; if(boolxrm(v,0)) flag|=2; if(gameover_score!=NO_SCORE) flag|=128; fp=open_memstream((char**)&buf,&sz); if(!fp) fatal("Allocation failed\n"); fputc(level_version,fp); fputc(level_version>>8,fp); fputc(flag,fp); if(flag&128) { fputc(gameover_score,fp); fputc(gameover_score>>8,fp); fputc(gameover_score>>16,fp); fputc(gameover_score>>24,fp); } if(flag&1) fwrite(com,1,strlen(com+1),fp); n=replay_count; replay_count=replay_pos; encode_move_list(fp); replay_count=n; fclose(fp); if(!buf) fatal("Allocation failed\n"); write_lump(FIL_SOLUTION,level_id,sz,buf); free(buf); sqlite3_exec(userdb,"UPDATE `LEVELS` SET `SOLVABLE` = 1 WHERE `ID` = LEVEL_ID();",0,0,0); } void run_game(void) { int i; SDL_Event ev; set_caption(); |
︙ | ︙ |
Modified internals.doc from [a0bdf57f86] to [72305d8afa].
︙ | ︙ | |||
117 118 119 120 121 122 123 | 16 (\q) = Quiz button 30 (\d) = Data 31 (\x) = Next byte is a character to display as graphic Codes 32-255 are displayed as is, but characters 1-31 cannot be displayed as a graphic unless a \x escape is present. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 117 118 119 120 121 122 123 124 125 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 | 16 (\q) = Quiz button 30 (\d) = Data 31 (\x) = Next byte is a character to display as graphic Codes 32-255 are displayed as is, but characters 1-31 cannot be displayed as a graphic unless a \x escape is present. === User state data === The user state data for levels in the user cache database can be in the old format or the new format. The old format is: * The move list. Only single-byte moves are valid. * The replay mark position (a big-endian 16-bit number). * The level version number (a big-endian 16-bit number); this will match the actual level version number if the player has solved this level, or otherwise will not match. * The number of moves in the move list. The new format starts with a null byte, and then is followed by any number of records. The first byte of a record determines its format: * 0x01 to 0x3F = Null-terminated * 0x41 to 0x7F = Two more bytes * 0x81 to 0xBF = Four more bytes * 0xC1 to 0xFF = Eight more bytes The new format is always small-endian. Record types: * 0x01 = Replay list * 0x41 = Level version, if solved (omitted otherwise) * 0x42 = Mark position |