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
1

2
3
4
5
6
7
8
9

-
+







#if 0
gcc ${CFLAGS:--s -O2} -c -Wno-multichar game.c `sdl-config --cflags`
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
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;
  FILE*o=open_memstream((char**)&replay_list,&replay_size);
  o=open_memstream((char**)&replay_list,&replay_size);
  if(!o) fatal("Allocation failed\n");
  while(replay_count<0xFFFD && (v=decode_move(fp))) {
  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
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;
  long sz=replay_size;
  if(solution_replay || !replay_list) return;
  if(sz<replay_count+6) {
  size_t sz=0;
  FILE*fp;
  if(solution_replay || !replay_list || !replay_count) return;
  if(gameover==1) solved=1;
    replay_list=realloc(replay_list,sz=replay_count+6);
    if(!replay_list) fatal("Allocation failed\n");
    replay_size=sz;
  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(gameover==1) solved=1;
  sz=replay_count+6;
  replay_list[sz-6]=replay_mark>>8;
  replay_list[sz-5]=replay_mark;
  if(replay_mark) {
    fputc(0x42,fp);
    fputc(replay_mark,fp);
    fputc(replay_mark>>8,fp);
  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);
  }
  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;
  int i,j;
  free(replay_list);
  replay_list=0;
  replay_count=replay_mark=replay_size=0;
  if(solution_replay) {
    gameover_score=NO_SCORE;
    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) {
    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=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++;
      }
        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);
        }
      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) {
    }
  } 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) {
      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;
      // 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=(replay_list[replay_count+2]<<8)|replay_list[replay_count+3];
        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 {
      notfound:
      replay_count=replay_mark=replay_size=0;
      free(replay_list);
      replay_list=0;
    }
  }
      // 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
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==65534 && !gameover) t="Too many moves played";
  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=0xFFFD;
        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>0xFDFF) replay_size=0xFDFF;
      if(replay_size>0xFFFF) replay_size=0xFFFF;
      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;
  FILE*fp;
  Uint8 flag;
  long n;
  unsigned char*buf=0;
  size_t sz=0;
  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);
  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(data);
    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;
  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;
  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);
  data[2]=(boolxrm(v,0)?2:0)|(com?1:0);
  p=data+3;
  if(com) {
    strcpy(p,com);
  fputc(flag,fp);
  if(flag&128) {
    fputc(gameover_score,fp);
    p+=strlen(com)+1;
    fputc(gameover_score>>8,fp);
    fputc(gameover_score>>16,fp);
    fputc(gameover_score>>24,fp);
  }
  if(data[2]&2) {
  if(flag&1) fwrite(com,1,strlen(com+1),fp);
    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;
  n=replay_count;
  }
  memcpy(p,replay_list,replay_pos);
  write_lump(FIL_SOLUTION,level_id,sz,data);
  free(data);
  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();