Free Hero Mesh

Diff
Login
This is a mirror of the main repository for Free Hero Mesh. New tickets and changes will not be accepted at this mirror.

Differences From Artifact [dd24aafcf7]:

To Artifact [e7202bf383]:


1
2
3
4
5
6
7
8
9
#if 0
gcc ${CFLAGS:--s -O2} -c -Wno-multichar game.c `sdl-config --cflags`
exit
#endif

/*
  This program is part of Free Hero Mesh and is public domain.
*/


|







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
67
68
69
70
71
72
73
74
75
76
  // 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;
  FILE*o=open_memstream((char**)&replay_list,&replay_size);
  if(!o) fatal("Allocation failed\n");
  while(replay_count<0xFFFD && (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;
}







>















>




|

|







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

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
  SDL_LockSurface(screen);
  draw_text(0,40,buf,0xF0,0xF1);
  SDL_UnlockSurface(screen);
  SDL_Flip(screen);
}

static void save_replay(void) {

  long sz=replay_size;

  if(solution_replay || !replay_list) return;
  if(sz<replay_count+6) {
    replay_list=realloc(replay_list,sz=replay_count+6);

    if(!replay_list) fatal("Allocation failed\n");






    replay_size=sz;

  }
  if(gameover==1) solved=1;
  sz=replay_count+6;

  replay_list[sz-6]=replay_mark>>8;
  replay_list[sz-5]=replay_mark;
  replay_list[sz-4]=(level_version+solved-1)>>8;
  replay_list[sz-3]=level_version+solved-1;
  replay_list[sz-2]=replay_count>>8;
  replay_list[sz-1]=replay_count;



  write_userstate(FIL_LEVEL,level_id,sz,replay_list);

}

static void load_replay(void) {


  long sz;
  int i;
  free(replay_list);


  if(solution_replay) {

    replay_list=read_lump(FIL_SOLUTION,level_id,&sz);


    // Solution format: version (16-bits), flag (8-bits), user name (null-terminated), timestamp (64-bits), move list
    if(sz>3) {
      i=replay_list[0]|(replay_list[1]<<8);

      if(i!=level_version) goto notfound;
      i=3;
      if(replay_list[2]&1) {





        while(i<sz && replay_list[i]) i++;
        i++;

      }
      if(replay_list[2]&2) i+=8;
      if(i>=sz || sz-i>0xFFFF) goto notfound;
      replay_size=sz;
      memmove(replay_list,replay_list+i,replay_count=sz-i);
      replay_mark=0;
    } else {
      goto notfound;
    }
  } else {

    replay_list=read_userstate(FIL_LEVEL,level_id,&sz);


    if(sz>=2) {
      replay_size=sz;

      replay_count=(replay_list[sz-2]<<8)|replay_list[sz-1];
      if(sz-replay_count>=4) replay_mark=(replay_list[replay_count]<<8)|replay_list[replay_count+1]; else replay_mark=0;
      if(sz-replay_count>=6) {
        i=(replay_list[replay_count+2]<<8)|replay_list[replay_count+3];
        if(i==level_version) solved=1;
      }



    } else {
      notfound:


      replay_count=replay_mark=replay_size=0;
      free(replay_list);







      replay_list=0;











    }
  }




}

static void begin_level(int id) {
  const char*t;
  replay_time=0;
  if(replay_count) save_replay();
  inputs_count=0;







>
|
>
|
|
<
>
|
>
>
>
>
>
>
|
>

<
|
>
|
|
<
<
<
<
>
>
>
|
>



>
>

|

>
>

>
|
>
>
|
|
<
>
|
|
|
>
>
>
>
>
|
|
>
|
<
<
<
<
<
<
<
|
<
>
|
>
>
|
<
>
|
|

|


>
>
>

|
>
>
|
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>







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
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369


1370
1371
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
  }
  timerflag=1;
  return n;
}

static inline void input_move(Uint8 k) {
  const char*t=execute_turn(k);
  if(replay_pos==65534 && !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=0xFFFD;
        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>0xFDFF) replay_size=0xFDFF;
      if(replay_size+0x200<=replay_pos) fatal("Confusion in input_move function\n");
      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;
  Uint8*data;
  Uint8*p;
  long sz;


  if(solution_replay) return;
  if(data=read_lump(FIL_SOLUTION,level_id,&sz)) {
    if(sz<3 || (data[0]|(data[1]<<8))!=level_version || (data[2]&~3)) goto dontkeep;
    sz-=3;




    if(data[2]&1) sz-=strnlen(data+3,sz);
    if(data[2]&2) sz-=8;
    if(sz<=0 || sz>replay_pos) goto dontkeep;

    free(data);
    return;
    dontkeep:
    free(data);

  }
  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)?:"";


  data=malloc(sz=replay_pos+(boolxrm(v,0)?8:0)+(com?strlen(com)+1:0)+3);


  if(!data) fatal("Allocation failed\n");
  data[0]=level_version&255;
  data[1]=level_version>>8;
  data[2]=(boolxrm(v,0)?2:0)|(com?1:0);
  p=data+3;

  if(com) {
    strcpy(p,com);
    p+=strlen(com)+1;



  }
  if(data[2]&2) {
    time_t t=time(0);
    p[0]=t>>000; p[1]=t>>010; p[2]=t>>020; p[3]=t>>030;
    p[4]=t>>040; p[5]=t>>050; p[6]=t>>060; p[7]=t>>070;
    p+=8;
  }
  memcpy(p,replay_list,replay_pos);




  write_lump(FIL_SOLUTION,level_id,sz,data);
  free(data);
  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();







|










|









|
<











|
|
|
>
>

|
|
|
>
>
>
>
|
|
|
>
|


|
>






>
>
|
>
>
|
|
|
<
<
>
|
|
<
>
>
>

|
<
<
<
|
<
|
>
>
>
>
|
|







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();