Free Hero Mesh

Check-in [a9af94f292]
Login
This is a mirror of the main repository for Free Hero Mesh. New tickets and changes will not be accepted at this mirror.
Overview
Comment:Implement level hashes calculation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a9af94f292aad2806d0f77ddc38cd975f5da57f8
User & Date: user on 2022-08-11 20:11:07
Other Links: manifest | tags
Context
2022-08-17
01:15
Implement hue/shade filter in dependent pictures. check-in: 0aa4ff0fdf user: user tags: trunk
2022-08-11
20:11
Implement level hashes calculation. check-in: a9af94f292 user: user tags: trunk
04:38
Ensure that class/message names made by {make} do not contain invalid characters. check-in: eaea2c00a7 user: user tags: trunk
Changes

Modified class.c from [dbf58fd0d2] to [2a869824f7].

643
644
645
646
647
648
649

650
651
652
653
654
655
656
  InputStack*nxt=inpstack;
  inpstack=malloc(sizeof(InputStack));
  if(!inpstack) fatal("Allocation failed\n");
  inpstack->classfp=classfp;
  inpstack->linenum=linenum;
  inpstack->next=nxt;
  linenum=1;

  if(*name=='.' || *name=='_' || *name=='-' || !*name || name[strspn(name,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-abcdefghijklmnopqrstuvwxyz.")])
   ParseError("Improper name of include file \"%s\"\n",name);
  if(main_options['z']) {
    if(strlen(name)<9 || memcmp(name-strlen(name),".include",8)) ParseError("Include file name doesn't end with .include\n");
    classfp=composite_slice(name,0)?:fopen(name,"r");
  } else {
    classfp=fopen(name,"r");







>







643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
  InputStack*nxt=inpstack;
  inpstack=malloc(sizeof(InputStack));
  if(!inpstack) fatal("Allocation failed\n");
  inpstack->classfp=classfp;
  inpstack->linenum=linenum;
  inpstack->next=nxt;
  linenum=1;
  if(main_options['S']) fatal("Cannot use {include} with level hash calculation mode\n");
  if(*name=='.' || *name=='_' || *name=='-' || !*name || name[strspn(name,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-abcdefghijklmnopqrstuvwxyz.")])
   ParseError("Improper name of include file \"%s\"\n",name);
  if(main_options['z']) {
    if(strlen(name)<9 || memcmp(name-strlen(name),".include",8)) ParseError("Include file name doesn't end with .include\n");
    classfp=composite_slice(name,0)?:fopen(name,"r");
  } else {
    classfp=fopen(name,"r");

Modified commandline.doc from [91980e1714] to [862853a4a5].

20
21
22
23
24
25
26





27
28
29
30
31
32
33

-M
  Write details of macro expansion to stdout.

-O
  Write a solution Hamster archive with private solutions to stdout.






-T
  Mode for testing some internal functions of Free Hero Mesh and SDL. You
  probably do not need to use this mode yourself.

-U
  Enable unstable/experimental features. These features may change in
  future and may later be made not needing this switch; but for now you







>
>
>
>
>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

-M
  Write details of macro expansion to stdout.

-O
  Write a solution Hamster archive with private solutions to stdout.

-S
  Compute level hashes. The level hashes depend on the class definitions
  and on the level itself, except for the title. It does not depend on any
  other files, including other levels, nor the level number.

-T
  Mode for testing some internal functions of Free Hero Mesh and SDL. You
  probably do not need to use this mode yourself.

-U
  Enable unstable/experimental features. These features may change in
  future and may later be made not needing this switch; but for now you

Modified config.doc from [b4646fa155] to [a7e06c877f].

74
75
76
77
78
79
80





81
82
83
84
85
86
87
  The window title to use in game mode. A tilde is replaced by the name of
  the puzzle set.

.gamma
  Gamma setting. If this is set, colours are gamma corrected by the number
  specified here. The default setting is 1.0.






.imageSize
  The picture size to use, up to 255. If the puzzle set contains pictures
  of the requested size, it will use those, otherwise it will try to make
  the pictures it does have larger by integer scaling; if that also does
  not work, then it is an error.

.keyRepeat







>
>
>
>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  The window title to use in game mode. A tilde is replaced by the name of
  the puzzle set.

.gamma
  Gamma setting. If this is set, colours are gamma corrected by the number
  specified here. The default setting is 1.0.

.hash
  Hash algorithm to use for some features; this must be one of the numbers
  for hash algorithms listed in the hash.h source file. Can be decimal, or
  hexadecimal if it starts with "0x".

.imageSize
  The picture size to use, up to 255. If the puzzle set contains pictures
  of the requested size, it will use those, otherwise it will try to make
  the pictures it does have larger by integer scaling; if that also does
  not work, then it is an error.

.keyRepeat

Modified edit.c from [3f10c9de85] to [50c4e83094].

12
13
14
15
16
17
18

19
20
21
22
23
24
25
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include "smallxrm.h"
#include "heromesh.h"
#include "quarks.h"
#include "cursorshapes.h"


EditorRect editrect;

typedef struct {
  Uint16 class;
  Uint8 img,dir;
  Value misc1,misc2,misc3;







>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include "smallxrm.h"
#include "heromesh.h"
#include "quarks.h"
#include "cursorshapes.h"
#include "hash.h"

EditorRect editrect;

typedef struct {
  Uint16 class;
  Uint8 img,dir;
  Value misc1,misc2,misc3;
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
    case TY_CLASS: fprintf(fp," $%s",classes[v.u]->name); break;
    case TY_MESSAGE: fprintf(fp," %s%s",v.u<256?"":"#",v.u<256?standard_message_names[v.u]:messages[v.u-256]);
    case TY_LEVELSTRING: fprintf(fp," %%%u",(int)v.u); break;
    default: fprintf(fp," ???"); break;
  }
}

static void export_level(const char*cmd) {
  Uint32*p=playfield;
  int i;
  Uint32 n;
  Object*o;
  FILE*fp;
  if(!cmd || !*cmd) return;
  fp=popen(cmd,"w");
  if(!fp) {
    screen_message("Cannot open pipe");
    return;
  }
  fprintf(fp,"; Free Hero Mesh exported level ID=%d ORD=%d\n",level_id,level_ord);
  fprint_esc(fp,'@',level_title);
  fprintf(fp,"C %d\nD %d %d\n",level_code,pfwidth,pfheight);
  again:
  for(i=0;i<64*64;i++) {
    n=p[i];
    while(n!=VOIDLINK) {
      o=objects[n];
      fprintf(fp,"%d %d $%s %d",o->x,o->y,classes[o->class]->name,o->image);







|




<
<
<
<
<
<
<
<
|







849
850
851
852
853
854
855
856
857
858
859
860








861
862
863
864
865
866
867
868
    case TY_CLASS: fprintf(fp," $%s",classes[v.u]->name); break;
    case TY_MESSAGE: fprintf(fp," %s%s",v.u<256?"":"#",v.u<256?standard_message_names[v.u]:messages[v.u-256]);
    case TY_LEVELSTRING: fprintf(fp," %%%u",(int)v.u); break;
    default: fprintf(fp," ???"); break;
  }
}

static void export_level_stream(FILE*fp) {
  Uint32*p=playfield;
  int i;
  Uint32 n;
  Object*o;








  if(!main_options['S']) fprint_esc(fp,'@',level_title);
  fprintf(fp,"C %d\nD %d %d\n",level_code,pfwidth,pfheight);
  again:
  for(i=0;i<64*64;i++) {
    n=p[i];
    while(n!=VOIDLINK) {
      o=objects[n];
      fprintf(fp,"%d %d $%s %d",o->x,o->y,classes[o->class]->name,o->image);
886
887
888
889
890
891
892












893
894
895
896
897
898
899
      fprintf(fp,"W\n");
      goto again;
    }
  } else {
    fprintf(fp,"W\n");
  }
  for(i=0;i<nlevelstrings;i++) fprint_esc(fp,'%',levelstrings[i]);












  pclose(fp);
}

static char*import_numbers(char*p,int*x,int*y) {
  if(!p) return 0;
  while(*p==' ' || *p=='\t') p++;
  if(*p<'0' || *p>'9') return 0;







>
>
>
>
>
>
>
>
>
>
>
>







879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
      fprintf(fp,"W\n");
      goto again;
    }
  } else {
    fprintf(fp,"W\n");
  }
  for(i=0;i<nlevelstrings;i++) fprint_esc(fp,'%',levelstrings[i]);
}

static void export_level(const char*cmd) {
  FILE*fp;
  if(!cmd || !*cmd) return;
  fp=popen(cmd,"w");
  if(!fp) {
    screen_message("Cannot open pipe");
    return;
  }
  fprintf(fp,"; Free Hero Mesh exported level ID=%d ORD=%d\n",level_id,level_ord);
  export_level_stream(fp);
  pclose(fp);
}

static char*import_numbers(char*p,int*x,int*y) {
  if(!p) return 0;
  while(*p==' ' || *p=='\t') p++;
  if(*p<'0' || *p>'9') return 0;
2040
2041
2042
2043
2044
2045
2046




















































    import_level("#");
    if(nobjects) {
      save_level();
      if(!feof(stdin)) new_level();
    }
  }
}



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
    import_level("#");
    if(nobjects) {
      save_level();
      if(!feof(stdin)) new_level();
    }
  }
}

static inline void hexout(const unsigned char*p,int n) {
  while(n--) printf("%02x",*p++);
}

void make_level_hashes(void) {
  char*v;
  FILE*in;
  FILE*out;
  unsigned char h1[128];
  unsigned char h2[128];
  long long alg;
  int hn,c,n;
  optionquery[1]=Q_hash;
  alg=strtol(xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"",0,0)?:HASH_SHA3_256;
  hn=hash_length(alg);
  if(hn<1 || hn>128) fatal("Hash algorithm not valid or too long: %llu (length %d)\n",alg,hn);
  // Class hash
  if(main_options['z']) {
    in=composite_slice(".class",1);
  } else {
    v=sqlite3_mprintf("%s.class",basefilename);
    if(!v) fatal("Allocation failed\n");
    in=fopen(v,"r");
    sqlite3_free(v);
  }
  if(!in) fatal("Cannot open class file\n");
  out=hash_stream(alg,0,h1);
  if(!out) fatal("Allocation failed\n");
  while((c=fgetc(in))!=EOF) {
    if(c=='\t') c=' ';
    if(c!='\r') fputc(c,out);
  }
  fclose(in);
  fclose(out);
  printf("-1 0 %llx ",alg);
  hexout(h1,hn);
  putchar('\n');
  for(n=0;n<level_nindex;n++) {
    if(load_level(level_index[n])) fatal("Level load error\n");
    out=hash_stream(alg,0,h2);
    if(!out) fatal("Allocation failed\n");
    fprintf(out,"\x01%llx %x\n",alg,hn);
    fwrite(h1,1,hn,out);
    export_level_stream(out);
    fclose(out);
    printf("%d 0 %llx ",level_id,alg);
    hexout(h2,hn);
    putchar('\n');
  }
  exit(0);
}

Modified heromesh.h from [fb2fb6b1fb] to [f41b1c99d8].

361
362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377
378
379
380
} EditorRect;

extern EditorRect editrect;

void run_editor(void);
void write_empty_level_set(FILE*);
void batch_import(void);


// == picedit ==

void run_picture_editor(void);

// == sound ==

void init_sound(void);
void set_sound_effect(Value v1,Value v2);
Uint16 find_user_sound(const char*name);
void set_sound_on(int on);
void sound_test(void);








>













361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
} EditorRect;

extern EditorRect editrect;

void run_editor(void);
void write_empty_level_set(FILE*);
void batch_import(void);
void make_level_hashes(void);

// == picedit ==

void run_picture_editor(void);

// == sound ==

void init_sound(void);
void set_sound_effect(Value v1,Value v2);
Uint16 find_user_sound(const char*name);
void set_sound_on(int on);
void sound_test(void);

Modified main.c from [71b6e89339] to [db5cb1b03f].

1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
    main_options['r']=1;
  }
  if(main_options['n'] && main_options['i']) fatal("Switches -n and -i are conflicting\n");
  if(main_options['n'] || main_options['i']) {
    if(main_options['r']) fatal("Switches -r and -%c are conflicting\n",main_options['i']?'i':'n');
    main_options['x']=1;
  }
  if(main_options['a'] || main_options['O']) main_options['r']=main_options['x']=1;
  if(main_options['p']) main_options['r']=1;
  if(main_options['f']) main_options['x']=1;
  if(!main_options['c']) {
    set_path(argv[0]);
    load_options();
  }
  if(argc>optind) read_options(argc-optind,argv+optind);







|







1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
    main_options['r']=1;
  }
  if(main_options['n'] && main_options['i']) fatal("Switches -n and -i are conflicting\n");
  if(main_options['n'] || main_options['i']) {
    if(main_options['r']) fatal("Switches -r and -%c are conflicting\n",main_options['i']?'i':'n');
    main_options['x']=1;
  }
  if(main_options['a'] || main_options['O'] || main_options['S']) main_options['r']=main_options['x']=1;
  if(main_options['p']) main_options['r']=1;
  if(main_options['f']) main_options['x']=1;
  if(!main_options['c']) {
    set_path(argv[0]);
    load_options();
  }
  if(argc>optind) read_options(argc-optind,argv+optind);
1167
1168
1169
1170
1171
1172
1173

1174
1175
1176
1177
1178
1179
1180
    if(level_ord>level_nindex) level_ord=level_nindex;
    log_if_error(load_level(-level_ord));
  }
  optionquery[1]=Q_maxTrigger;
  max_trigger=strtol(xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"",0,10);
  if(main_options['a']) run_auto_test();
  if(main_options['O']) export_private_solutions();

  if(main_options['x']) {
    if(main_options['i']) {
      batch_import();
      if(main_options['f']) flush_usercache();
      return 0;
    } else if(main_options['f']) {
      if(main_options['r']) fatal("Cannot flush user cache; puzzle set is read-only\n");







>







1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
    if(level_ord>level_nindex) level_ord=level_nindex;
    log_if_error(load_level(-level_ord));
  }
  optionquery[1]=Q_maxTrigger;
  max_trigger=strtol(xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"",0,10);
  if(main_options['a']) run_auto_test();
  if(main_options['O']) export_private_solutions();
  if(main_options['S']) make_level_hashes();
  if(main_options['x']) {
    if(main_options['i']) {
      batch_import();
      if(main_options['f']) flush_usercache();
      return 0;
    } else if(main_options['f']) {
      if(main_options['r']) fatal("Cannot flush user cache; puzzle set is read-only\n");

Modified man6/heromesh.6 from [fe215be8ba] to [fbe3c2a7bd].

19
20
21
22
23
24
25



26
27
28
29
30
31
32
Dump all class data.
.IP -H
Dump the hash table.
.IP -L
Display all tokens being read from the class definition file.
.IP -O
Output a solution Hamster archive with private solutions to stdout and then terminate.



.IP -U
Enable experimental/unstable features.
.IP -a
Autotest levels, ensuring that the provided solution is valid.
.IP -c
Only load classes and then terminate.
This can be used to verify that the class definition file does not contain syntax errors.







>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Dump all class data.
.IP -H
Dump the hash table.
.IP -L
Display all tokens being read from the class definition file.
.IP -O
Output a solution Hamster archive with private solutions to stdout and then terminate.
.IP -S
Compute level hashes.
Level hashes depend only on the class definition and on the level being hashed, except the title.
.IP -U
Enable experimental/unstable features.
.IP -a
Autotest levels, ensuring that the provided solution is valid.
.IP -c
Only load classes and then terminate.
This can be used to verify that the class definition file does not contain syntax errors.