Free Hero Mesh

Check-in [89cc6486f6]
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 CONFIG_NO_STATUS
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 89cc6486f65f15788d7d54998dc3dcb002a3fb6b
User & Date: user on 2022-08-10 05:45:57
Other Links: manifest | tags
Context
2022-08-11
04:25
Implement CONFIG_EXTRA_SCREEN_INIT check-in: 831a8baa13 user: user tags: trunk
2022-08-10
05:45
Implement CONFIG_NO_STATUS check-in: 89cc6486f6 user: user tags: trunk
2022-08-06
07:47
Implement CONFIG_OMIT_SOUND check-in: c8c053656e user: user tags: trunk
Changes

Modified bindings.c from [59715bfb2c] to [dcc38ad60c].

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

static void*cb_1(xrm_db*db,void*usr) {
  xrm_enumerate(db,cb_2,usr);
  return 0;
}

void load_key_bindings(void) {
  fprintf(stderr,"Loading key bindings...\n");
  cur_modifiers=loose_modifiers=0;
  optionquery[1]=Q_editKey;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,editor_bindings);
  optionquery[1]=Q_gameKey;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,game_bindings);
  optionquery[1]=Q_editClick;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,editor_mouse_bindings);
  optionquery[1]=Q_gameClick;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,game_mouse_bindings);
  fprintf(stderr,"Done\n");
}

const UserCommand*find_key_binding(SDL_Event*ev,int editing) {
  KeyBinding*kb;
  static const UserCommand nul={cmd:0};
  SDLMod m;
  int i;







|









|







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

static void*cb_1(xrm_db*db,void*usr) {
  xrm_enumerate(db,cb_2,usr);
  return 0;
}

void load_key_bindings(void) {
  printStatus("Loading key bindings...\n");
  cur_modifiers=loose_modifiers=0;
  optionquery[1]=Q_editKey;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,editor_bindings);
  optionquery[1]=Q_gameKey;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,game_bindings);
  optionquery[1]=Q_editClick;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,editor_mouse_bindings);
  optionquery[1]=Q_gameClick;
  xrm_search(resourcedb,optionquery,optionquery,2,cb_1,game_mouse_bindings);
  printStatus("Done\n");
}

const UserCommand*find_key_binding(SDL_Event*ev,int editing) {
  KeyBinding*kb;
  static const UserCommand nul={cmd:0};
  SDLMod m;
  int i;

Modified class.c from [7edc77ca68] to [fe260125ee].

2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572

void load_classes(void) {
  int i;
  int gloptr=0;
  Hash*glolocalhash;
  char*nam=sqlite3_mprintf("%s.class",basefilename);
  sqlite3_stmt*vst=0;
  fprintf(stderr,"Loading class definitions...\n");
  if(!nam) fatal("Allocation failed\n");
  classfp=main_options['z']?composite_slice(".class",1):fopen(nam,"r");
  sqlite3_free(nam);
  if(!classfp) fatal("Cannot open class file '%s': %m\n",nam);
  glohash=calloc(HASH_SIZE,sizeof(Hash));
  if(!glohash) fatal("Allocation failed\n");
  glolocalhash=calloc(LOCAL_HASH_SIZE,sizeof(Hash));







|







2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572

void load_classes(void) {
  int i;
  int gloptr=0;
  Hash*glolocalhash;
  char*nam=sqlite3_mprintf("%s.class",basefilename);
  sqlite3_stmt*vst=0;
  printStatus("Loading class definitions...\n");
  if(!nam) fatal("Allocation failed\n");
  classfp=main_options['z']?composite_slice(".class",1):fopen(nam,"r");
  sqlite3_free(nam);
  if(!classfp) fatal("Cannot open class file '%s': %m\n",nam);
  glohash=calloc(HASH_SIZE,sizeof(Hash));
  if(!glohash) fatal("Allocation failed\n");
  glolocalhash=calloc(LOCAL_HASH_SIZE,sizeof(Hash));
2778
2779
2780
2781
2782
2783
2784
2785
2786
  if(macros) for(i=0;i<MAX_MACRO;i++) if(macros[i]) free_macro(macros[i]);
  free(macros);
  if(array_size) {
    array_data=malloc(array_size*sizeof(Value));
    if(!array_data) fatal("Array allocation failed\n");
  }
  if(norders) set_class_orders();
  fprintf(stderr,"Done\n");
}







|

2778
2779
2780
2781
2782
2783
2784
2785
2786
  if(macros) for(i=0;i<MAX_MACRO;i++) if(macros[i]) free_macro(macros[i]);
  free(macros);
  if(array_size) {
    array_data=malloc(array_size*sizeof(Value));
    if(!array_data) fatal("Array allocation failed\n");
  }
  if(norders) set_class_orders();
  printStatus("Done\n");
}

Modified comconfig.doc from [0c26897a26] to [06f1d3453e].

23
24
25
26
27
28
29




30
31
32
33
34
35
36
  Size of local hash table when reading class definition file. This must
  be less than 65535.

CONFIG_NO_PORTABLE
  If defined, then portable mode by checking argv[0] is disabled. (It is
  still possible to use portable mode by HEROMESH_PREFIX)





CONFIG_OMIT_SOUND
  If defined, omit all sound capabilities (including music). (Even if it
  is not defined, it can still be disabled at runtime.)

CONFIG_USING_32BIT_TIMESTAMPS
  If defined, force use of 32-bit timestamps. (This is needed in order to
  avoid compiler warnings on some systems, such as some versions of the







>
>
>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  Size of local hash table when reading class definition file. This must
  be less than 65535.

CONFIG_NO_PORTABLE
  If defined, then portable mode by checking argv[0] is disabled. (It is
  still possible to use portable mode by HEROMESH_PREFIX)

CONFIG_NO_STATUS
  If defined, then most status output is omitted unless -v is specified.
  (Some status output, such as most error messages, are still displayed.)

CONFIG_OMIT_SOUND
  If defined, omit all sound capabilities (including music). (Even if it
  is not defined, it can still be disabled at runtime.)

CONFIG_USING_32BIT_TIMESTAMPS
  If defined, force use of 32-bit timestamps. (This is needed in order to
  avoid compiler warnings on some systems, such as some versions of the
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
79
80
81
82
83





84
85
86
87
88



89
90
91
92
93
94
95


=== Unimplemented options ===

The below options are not implemented and are subject to being changed
in future when they are implemented.





CONFIG_ERROR_CHECKING
  Define error checking level, where 9 is maximum. Lower numbers might
  improve speed but may make the program crash in some cases and may also
  cause security vulnerabilities.





CONFIG_LAUNCHER_PATH
  If defined, then it is the full path to a file to execute if Free Hero
  Mesh has been invoked with no command-line arguments, or if it is given
  the flag to run the launcher program instead.

CONFIG_MULTIUSER_SCORES
  (Meant for storing scores on a multiuser system, somehow)

CONFIG_OMIT_EDITOR
  If defined, omit the level editor and picture editor.











CONFIG_OMIT_MUSIC
  If defined, omit background music playback capabilities. (Even if this
  is not defined, it can still be disabled at runtime.

CONFIG_PRIVATE_USERCACHE
  If defined as a octal number, set the default file permissions of the
  user cache database when creating it. (The user can still change them
  afterward by using chmod.)

CONFIG_RANDOM_SOURCE
  Device to read random numbers from instead of using the random number
  generator built in to SQLite. (This will override the definition of the
  SQL RANDOM() function if it is defined.)






CONFIG_USING_APPIMAGE
  If defined, it will check the environment variables having to do with
  AppImage and will be able to load the default resources from there, as
  well as allow the configuration file to reference the AppImage directory.




CONFIG_USING_BSD_FUNCTIONS
  If defined, use BSD functions (such as funopen) instead of GNU functions
  (such as fopencookie), by adding emulations into the program. (It is
  still necessary to use a C compiler with GNU extensions though, even if
  the GNU library is not available.)

CONFIG_USING_SDL12_COMPAT







>
>
>
>





>
>
>
>









|
>
>
>
>
>
>
>
>
>
>



|
<
<
<
<
<






>
>
>
>
>





>
>
>







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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94





95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120


=== Unimplemented options ===

The below options are not implemented and are subject to being changed
in future when they are implemented.

CONFIG_DOUBLE_PRECISION_AUDIO
  If defined, use double precision for some math calculations in audio
  processing. Some calculations are not affected by this option.

CONFIG_ERROR_CHECKING
  Define error checking level, where 9 is maximum. Lower numbers might
  improve speed but may make the program crash in some cases and may also
  cause security vulnerabilities.

CONFIG_EXTRA_SCREEN_INIT
  Can be a C code to be executed after the video mode is set, but before
  any other screen initialization is done.

CONFIG_LAUNCHER_PATH
  If defined, then it is the full path to a file to execute if Free Hero
  Mesh has been invoked with no command-line arguments, or if it is given
  the flag to run the launcher program instead.

CONFIG_MULTIUSER_SCORES
  (Meant for storing scores on a multiuser system, somehow)

CONFIG_OMIT_EDITOR
  If defined, omit the level editor and picture editor, as well as the
  ability to import and export levels and pictures.

CONFIG_OMIT_GUI
  If defined, omit the GUI. Autotest mode can still be used, and the
  batch import/export works if CONFIG_OMIT_EDITOR is not defined. Any
  other feature which does not require the GUI also can still be used.

CONFIG_OMIT_MBCS
  If defined, omit the capability of multibyte character encodings. (This
  only affects displaying text, not the behaviour of the game.)

CONFIG_OMIT_MUSIC
  If defined, omit background music playback capabilities. (Even if this
  is not defined, it can still be disabled at runtime.)






CONFIG_RANDOM_SOURCE
  Device to read random numbers from instead of using the random number
  generator built in to SQLite. (This will override the definition of the
  SQL RANDOM() function if it is defined.)

CONFIG_USERCACHE_PERMISSIONS
  If defined as a octal number, set the default file permissions of the
  user cache database when creating it. (The user can still change them
  afterward by using chmod.)

CONFIG_USING_APPIMAGE
  If defined, it will check the environment variables having to do with
  AppImage and will be able to load the default resources from there, as
  well as allow the configuration file to reference the AppImage directory.

CONFIG_USING_ARM_NEON
  If defined, use ARM NEON extensions. This only works on ARM.

CONFIG_USING_BSD_FUNCTIONS
  If defined, use BSD functions (such as funopen) instead of GNU functions
  (such as fopencookie), by adding emulations into the program. (It is
  still necessary to use a C compiler with GNU extensions though, even if
  the GNU library is not available.)

CONFIG_USING_SDL12_COMPAT
103
104
105
106
107
108
109





110
111
112
113
114
  some parts of the program to improve compatibility.)

CONFIG_USING_X86_BMI2
  If defined, use PDEP and PEXT instructions. This only works on x86 and
  only on some processor models. (Apparently this is fast on Intel but slow
  on AMD, so it should not be used on AMD, even if it is available.)






CONFIG_WITH_VIDEO_RECORDING
  If defined, then include code to implement video recording, which will
  override some of the SDL functions (and some other functions), and might
  make them more slowly even if the video recording is not enabled.








>
>
>
>
>





128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  some parts of the program to improve compatibility.)

CONFIG_USING_X86_BMI2
  If defined, use PDEP and PEXT instructions. This only works on x86 and
  only on some processor models. (Apparently this is fast on Intel but slow
  on AMD, so it should not be used on AMD, even if it is available.)

CONFIG_WITH_NETWORK
  If defined, include code for network/internet access. Even if this is
  defined, it must still be enabled by the end user; it will not connect
  to any remote services unless the user explicitly commands it to do so.

CONFIG_WITH_VIDEO_RECORDING
  If defined, then include code to implement video recording, which will
  override some of the SDL functions (and some other functions), and might
  make them more slowly even if the video recording is not enabled.

Modified commandline.doc from [0d7234c266] to [91980e1714].

127
128
129
130
131
132
133
134



not end with a slash, it will be a prefix to the file name too).

3. If argv[0] contains a forward slash, use portable mode; it will use
files "current.heromeshrc" and "current.heromeshsession" in the directory
where the executable file is allegedly found.

4. Otherwise, home mode is used.












>
>
>
127
128
129
130
131
132
133
134
135
136
137
not end with a slash, it will be a prefix to the file name too).

3. If argv[0] contains a forward slash, use portable mode; it will use
files "current.heromeshrc" and "current.heromeshsession" in the directory
where the executable file is allegedly found.

4. Otherwise, home mode is used.

Determining portable mode by argv[0] can be disabled at compile-time. Even
if it is disabled, HEROMESH_PREFIX environment variable can still be used.

Modified function.c from [793458f469] to [311098a0c7].

1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
  const unsigned char*d;
  unsigned char*p;
  int i,j;
  long n;
  int txn=sqlite3_get_autocommit(userdb)?sqlite3_exec(userdb,"BEGIN;",0,0,0):1;
  if(!levels_schema) return SQLITE_CORRUPT_VTAB;
  if(screen) set_cursor(XC_coffee_mug);
  fprintf(stderr,"Loading level table...\n");
  if(sqlite3_exec(userdb,levels_schema,0,0,0)) {
    err: fatal("SQL error while loading LEVELS table: %s\n",sqlite3_errmsg(userdb));
  }
  if(sqlite3_prepare_v2(userdb,"SELECT `LEVEL`, IFNULL(`DATA`,READ_LUMP_AT(`OFFSET`,?1)), `USERSTATE` FROM `USERCACHEDATA`"
   " WHERE `FILE` = LEVEL_CACHEID() AND `LEVEL` NOT NULL AND `LEVEL` >= 0 ORDER BY `LEVEL`;",-1,&st1,0))
   goto err;
  // ?1=ID, ?2=CODE, ?3=WIDTH, ?4=HEIGHT, ?5=TITLE, ?6=SOLVED, ?7=SOLVABLE







|







1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
  const unsigned char*d;
  unsigned char*p;
  int i,j;
  long n;
  int txn=sqlite3_get_autocommit(userdb)?sqlite3_exec(userdb,"BEGIN;",0,0,0):1;
  if(!levels_schema) return SQLITE_CORRUPT_VTAB;
  if(screen) set_cursor(XC_coffee_mug);
  printStatus("Loading level table...\n");
  if(sqlite3_exec(userdb,levels_schema,0,0,0)) {
    err: fatal("SQL error while loading LEVELS table: %s\n",sqlite3_errmsg(userdb));
  }
  if(sqlite3_prepare_v2(userdb,"SELECT `LEVEL`, IFNULL(`DATA`,READ_LUMP_AT(`OFFSET`,?1)), `USERSTATE` FROM `USERCACHEDATA`"
   " WHERE `FILE` = LEVEL_CACHEID() AND `LEVEL` NOT NULL AND `LEVEL` >= 0 ORDER BY `LEVEL`;",-1,&st1,0))
   goto err;
  // ?1=ID, ?2=CODE, ?3=WIDTH, ?4=HEIGHT, ?5=TITLE, ?6=SOLVED, ?7=SOLVABLE
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
    sqlite3_bind_int(st2,2,level_index[j]);
    while((i=sqlite3_step(st2))==SQLITE_ROW);
    if(i!=SQLITE_DONE) goto err;
  }
  sqlite3_finalize(st2);
  sqlite3_exec(userdb,"CREATE UNIQUE INDEX `LEVELS_ORD` ON `LEVELS`(`ORD`) WHERE `ORD` NOT NULL;",0,0,0);
  if(!txn) sqlite3_exec(userdb,"COMMIT;",0,0,0);
  fprintf(stderr,"Done\n");
  if(screen) set_cursor(XC_arrow);
  return SQLITE_SCHEMA;
}

Module(vt_levels,
  .xConnect=vt1_levels_connect,
  .xOpen=vt1_levels_open,







|







1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
    sqlite3_bind_int(st2,2,level_index[j]);
    while((i=sqlite3_step(st2))==SQLITE_ROW);
    if(i!=SQLITE_DONE) goto err;
  }
  sqlite3_finalize(st2);
  sqlite3_exec(userdb,"CREATE UNIQUE INDEX `LEVELS_ORD` ON `LEVELS`(`ORD`) WHERE `ORD` NOT NULL;",0,0,0);
  if(!txn) sqlite3_exec(userdb,"COMMIT;",0,0,0);
  printStatus("Done\n");
  if(screen) set_cursor(XC_arrow);
  return SQLITE_SCHEMA;
}

Module(vt_levels,
  .xConnect=vt1_levels_connect,
  .xOpen=vt1_levels_open,

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

1
2
3
4
5
6
7






8
9
10
11
12
13
14
/*
  This file is part of Free Hero Mesh and is public domain.
*/

#include "comconfig.h"

// == main ==







#define fatal(...) do{ fprintf(stderr,"FATAL: " __VA_ARGS__); exit(1); }while(0)
#define boolxrm(a,b) (*a=='1'||*a=='y'||*a=='t'||*a=='Y'||*a=='T'?1:*a=='0'||*a=='n'||*a=='f'||*a=='N'||*a=='F'?0:b)

#define TY_NUMBER 0
#define TY_CLASS 1
#define TY_MESSAGE 2







>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
  This file is part of Free Hero Mesh and is public domain.
*/

#include "comconfig.h"

// == main ==

#ifdef CONFIG_NO_STATUS
#define printStatus(...) do{ if(main_options['v']) fprintf(stderr,__VA_ARGS__); }while(0)
#else
#define printStatus(...) fprintf(stderr,__VA_ARGS__)
#endif

#define fatal(...) do{ fprintf(stderr,"FATAL: " __VA_ARGS__); exit(1); }while(0)
#define boolxrm(a,b) (*a=='1'||*a=='y'||*a=='t'||*a=='Y'||*a=='T'?1:*a=='0'||*a=='n'||*a=='f'||*a=='N'||*a=='F'?0:b)

#define TY_NUMBER 0
#define TY_CLASS 1
#define TY_MESSAGE 2

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

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  if(e!=SQLITE_DONE) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
  sqlite3_finalize(st);
}

static void flush_usercache(void) {
  int e;
  if(main_options['r']) return;
  fprintf(stderr,"Flushing user cache...\n");
  if(e=sqlite3_exec(userdb,"BEGIN;",0,0,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
  flush_usercache_1(FIL_LEVEL);
  flush_usercache_1(FIL_SOLUTION);
  if(e=sqlite3_exec(userdb,"COMMIT;",0,0,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
  fprintf(stderr,"Done\n");
}

static void init_composite(void) {
  FILE*fp=compositefp=fopen(basefilename,"r");
  sqlite3_stmt*st;
  sqlite3_int64 t1,t2;
  int z;
  struct stat fst;
  if(!fp) fatal("Cannot open '%s' for reading: %m\n",basefilename);
  fprintf(stderr,"Loading puzzle set...\n");
  if(z=sqlite3_exec(userdb,"BEGIN;",0,0,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  if(z=sqlite3_prepare_v2(userdb,"SELECT `ID`, `TIME` FROM `USERCACHEINDEX` WHERE `NAME` = CHAR(?2)||'//'||?1;",-1,&st,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  basefilename=realpath(basefilename,0);
  if(!basefilename) fatal("Cannot find real path of puzzle set: %m\n");
  sqlite3_bind_text(st,1,basefilename,-1,0);
  levelfp=composite_slice(".level",1);
  solutionfp=composite_slice(".solution",1);







|




|









|







564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  if(e!=SQLITE_DONE) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
  sqlite3_finalize(st);
}

static void flush_usercache(void) {
  int e;
  if(main_options['r']) return;
  printStatus("Flushing user cache...\n");
  if(e=sqlite3_exec(userdb,"BEGIN;",0,0,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
  flush_usercache_1(FIL_LEVEL);
  flush_usercache_1(FIL_SOLUTION);
  if(e=sqlite3_exec(userdb,"COMMIT;",0,0,0)) fatal("SQL error (%d): %s\n",e,sqlite3_errmsg(userdb));
  printStatus("Done\n");
}

static void init_composite(void) {
  FILE*fp=compositefp=fopen(basefilename,"r");
  sqlite3_stmt*st;
  sqlite3_int64 t1,t2;
  int z;
  struct stat fst;
  if(!fp) fatal("Cannot open '%s' for reading: %m\n",basefilename);
  printStatus("Loading puzzle set...\n");
  if(z=sqlite3_exec(userdb,"BEGIN;",0,0,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  if(z=sqlite3_prepare_v2(userdb,"SELECT `ID`, `TIME` FROM `USERCACHEINDEX` WHERE `NAME` = CHAR(?2)||'//'||?1;",-1,&st,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  basefilename=realpath(basefilename,0);
  if(!basefilename) fatal("Cannot find real path of puzzle set: %m\n");
  sqlite3_bind_text(st,1,basefilename,-1,0);
  levelfp=composite_slice(".level",1);
  solutionfp=composite_slice(".solution",1);
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    sqlite3_free(p);
  }
  if(z=sqlite3_prepare_v3(userdb,"SELECT `OFFSET`, CASE WHEN ?3 THEN `USERSTATE` ELSE `DATA` 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_usercache(void) {
  sqlite3_stmt*st;
  int z;
  sqlite3_int64 t1,t2;
  char*nam1;
  char*nam2;
  char*nam3;
  struct stat fst;
  fprintf(stderr,"Initializing user cache...\n");
  if(z=sqlite3_exec(userdb,"BEGIN;",0,0,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  if(z=sqlite3_prepare_v2(userdb,"SELECT `ID`, `TIME` FROM `USERCACHEINDEX` WHERE `NAME` = ?1;",-1,&st,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  nam1=sqlite3_mprintf("%s.level",basefilename);
  if(!nam1) fatal("Allocation failed\n");
  nam2=realpath(nam1,0);
  if(!nam2) fatal("Cannot find real path of '%s': %m\n",nam1);
  levelfp=fopen(nam2,main_options['r']?"r":"r+");







|










|







624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    sqlite3_free(p);
  }
  if(z=sqlite3_prepare_v3(userdb,"SELECT `OFFSET`, CASE WHEN ?3 THEN `USERSTATE` ELSE `DATA` 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));
  printStatus("Done\n");
}

static void init_usercache(void) {
  sqlite3_stmt*st;
  int z;
  sqlite3_int64 t1,t2;
  char*nam1;
  char*nam2;
  char*nam3;
  struct stat fst;
  printStatus("Initializing user cache...\n");
  if(z=sqlite3_exec(userdb,"BEGIN;",0,0,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  if(z=sqlite3_prepare_v2(userdb,"SELECT `ID`, `TIME` FROM `USERCACHEINDEX` WHERE `NAME` = ?1;",-1,&st,0)) fatal("SQL error (%d): %s\n",z,sqlite3_errmsg(userdb));
  nam1=sqlite3_mprintf("%s.level",basefilename);
  if(!nam1) fatal("Allocation failed\n");
  nam2=realpath(nam1,0);
  if(!nam2) fatal("Cannot find real path of '%s': %m\n",nam1);
  levelfp=fopen(nam2,main_options['r']?"r":"r+");
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  free(nam2);
  free(nam3);
  if(z=sqlite3_prepare_v3(userdb,"SELECT `OFFSET`, CASE WHEN ?3 THEN `USERSTATE` ELSE `DATA` 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 new_puzzle_set(void) {
  char*nam1;
  char*nam2;
  nam1=sqlite3_mprintf("%s.level",basefilename);
  if(!nam1) fatal("Allocation failed\n");







|







687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  free(nam2);
  free(nam3);
  if(z=sqlite3_prepare_v3(userdb,"SELECT `OFFSET`, CASE WHEN ?3 THEN `USERSTATE` ELSE `DATA` 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));
  printStatus("Done\n");
}

static void new_puzzle_set(void) {
  char*nam1;
  char*nam2;
  nam1=sqlite3_mprintf("%s.level",basefilename);
  if(!nam1) fatal("Allocation failed\n");
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
  while(argc>optind && argv[optind][0]=='-') {
    int i;
    const char*s=argv[optind++];
    if(s[1]=='-' && !s[2]) break;
    for(i=1;s[i];i++) main_options[s[i]&127]=1;
  }
  setbuf(stderr,0);
  if(!main_options['c']) fprintf(stderr,"FREE HERO MESH\n");
  if(argc<=optind) fatal("usage: %s [switches] [--] basename [options...]\n",argc?argv[0]:"heromesh");
  if(xrm_init(realloc)) fatal("Failed to initialize resource manager\n");
  if(xrm_init_quarks(global_quarks)) fatal("Failed to initialize resource manager\n");
  resourcedb=xrm_create();
  if(!resourcedb) fatal("Allocation of resource database failed\n");
  basefilename=argv[optind++];
  if(argc>optind && argv[1][0]=='=') {







|







1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
  while(argc>optind && argv[optind][0]=='-') {
    int i;
    const char*s=argv[optind++];
    if(s[1]=='-' && !s[2]) break;
    for(i=1;s[i];i++) main_options[s[i]&127]=1;
  }
  setbuf(stderr,0);
  if(!main_options['c']) printStatus("FREE HERO MESH\n");
  if(argc<=optind) fatal("usage: %s [switches] [--] basename [options...]\n",argc?argv[0]:"heromesh");
  if(xrm_init(realloc)) fatal("Failed to initialize resource manager\n");
  if(xrm_init_quarks(global_quarks)) fatal("Failed to initialize resource manager\n");
  resourcedb=xrm_create();
  if(!resourcedb) fatal("Allocation of resource database failed\n");
  basefilename=argv[optind++];
  if(argc>optind && argv[1][0]=='=') {
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
      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");
      flush_usercache();
      return 0;
    }
    fprintf(stderr,"Ready for executing SQL statements.\n");
    no_dead_anim=1;
    do_sql_mode();
    return 0;
  }
  set_autosave();
  for(;;) { if(main_options['e']) run_editor(); else run_game(); }
}







|







1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
      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");
      flush_usercache();
      return 0;
    }
    printStatus("Ready for executing SQL statements.\n");
    no_dead_anim=1;
    do_sql_mode();
    return 0;
  }
  set_autosave();
  for(;;) { if(main_options['e']) run_editor(); else run_game(); }
}

Modified picedit.c from [583dab5e00] to [e191836d4c].

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    "BEGIN;"
    "CREATE TEMPORARY TABLE `PICEDIT`(`ID` INTEGER PRIMARY KEY,`NAME` TEXT NOT NULL COLLATE NOCASE,`TYPE` INT,`DATA` BLOB);"
    "CREATE INDEX `PICEDIT_I1` ON `PICEDIT`(`NAME`,`TYPE`);"
  ,0,0,0);
  if(i) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
  nam=sqlite3_mprintf("%s.xclass",basefilename);
  if(!nam) fatal("Allocation failed\n");
  fprintf(stderr,"Loading pictures...\n");
  fp=fopen(nam,"r");
  sqlite3_free(nam);
  if(!fp) {
    nam=0;
    fprintf(stderr,"%m\n");
    goto done;
  }







|







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    "BEGIN;"
    "CREATE TEMPORARY TABLE `PICEDIT`(`ID` INTEGER PRIMARY KEY,`NAME` TEXT NOT NULL COLLATE NOCASE,`TYPE` INT,`DATA` BLOB);"
    "CREATE INDEX `PICEDIT_I1` ON `PICEDIT`(`NAME`,`TYPE`);"
  ,0,0,0);
  if(i) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
  nam=sqlite3_mprintf("%s.xclass",basefilename);
  if(!nam) fatal("Allocation failed\n");
  printStatus("Loading pictures...\n");
  fp=fopen(nam,"r");
  sqlite3_free(nam);
  if(!fp) {
    nam=0;
    fprintf(stderr,"%m\n");
    goto done;
  }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
    if(i!=SQLITE_DONE) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
  }
done:
  if(st) sqlite3_finalize(st);
  if(fp) fclose(fp);
  free(nam);
  free(buf);
  fprintf(stderr,"Done\n");
  sqlite3_exec(userdb,
    "CREATE TRIGGER `PICEDIT_T1` BEFORE INSERT ON `PICEDIT` BEGIN"
    "  SELECT RAISE(FAIL,'Duplicate name') FROM `PICEDIT` WHERE REPLACE(`NAME`||'*','.IMG*','.DEP*')=REPLACE(NEW.`NAME`||'*','.IMG*','.DEP*');"
    "END;"
    "CREATE TRIGGER `PICEDIT_T2` BEFORE UPDATE OF `NAME` ON `PICEDIT` BEGIN"
    "  SELECT RAISE(FAIL,'Duplicate name') FROM `PICEDIT` WHERE REPLACE(`NAME`||'*','.IMG*','.DEP*')=REPLACE(NEW.`NAME`||'*','.IMG*','.DEP*');"
    "END;"
  ,0,0,0);
  return r;
}

static void save_picture_file(void) {
  sqlite3_stmt*st;
  FILE*fp;
  char*s=sqlite3_mprintf("%s.xclass",basefilename);
  int i;
  const char*nam;
  const char*buf;
  if(!s) fatal("Allocation failed\n");
  fprintf(stderr,"Saving pictures...\n");
  fp=fopen(s,"w");
  if(!fp) fatal("Cannot open picture file for writing: %m\n");
  sqlite3_free(s);
  i=sqlite3_prepare_v2(userdb,"SELECT `NAME`,`DATA` FROM `PICEDIT`;",-1,&st,0);
  if(i) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
  while((i=sqlite3_step(st))==SQLITE_ROW) {
    nam=sqlite3_column_text(st,0);







|



















|







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
    if(i!=SQLITE_DONE) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
  }
done:
  if(st) sqlite3_finalize(st);
  if(fp) fclose(fp);
  free(nam);
  free(buf);
  printStatus("Done\n");
  sqlite3_exec(userdb,
    "CREATE TRIGGER `PICEDIT_T1` BEFORE INSERT ON `PICEDIT` BEGIN"
    "  SELECT RAISE(FAIL,'Duplicate name') FROM `PICEDIT` WHERE REPLACE(`NAME`||'*','.IMG*','.DEP*')=REPLACE(NEW.`NAME`||'*','.IMG*','.DEP*');"
    "END;"
    "CREATE TRIGGER `PICEDIT_T2` BEFORE UPDATE OF `NAME` ON `PICEDIT` BEGIN"
    "  SELECT RAISE(FAIL,'Duplicate name') FROM `PICEDIT` WHERE REPLACE(`NAME`||'*','.IMG*','.DEP*')=REPLACE(NEW.`NAME`||'*','.IMG*','.DEP*');"
    "END;"
  ,0,0,0);
  return r;
}

static void save_picture_file(void) {
  sqlite3_stmt*st;
  FILE*fp;
  char*s=sqlite3_mprintf("%s.xclass",basefilename);
  int i;
  const char*nam;
  const char*buf;
  if(!s) fatal("Allocation failed\n");
  printStatus("Saving pictures...\n");
  fp=fopen(s,"w");
  if(!fp) fatal("Cannot open picture file for writing: %m\n");
  sqlite3_free(s);
  i=sqlite3_prepare_v2(userdb,"SELECT `NAME`,`DATA` FROM `PICEDIT`;",-1,&st,0);
  if(i) fatal("SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
  while((i=sqlite3_step(st))==SQLITE_ROW) {
    nam=sqlite3_column_text(st,0);
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
    fputc(i>>8,fp);
    if(i) fwrite(buf,1,i,fp);
  }
  if(i!=SQLITE_DONE) fprintf(stderr,"SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
done:
  if(st) sqlite3_finalize(st);
  if(fp) fclose(fp);
  fprintf(stderr,"Done\n");
}

static sqlite3_int64 ask_picture_id(const char*t) {
  sqlite3_stmt*st;
  const char*r=screen_prompt(t);
  int i;
  sqlite3_int64 id=0;







|







297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
    fputc(i>>8,fp);
    if(i) fwrite(buf,1,i,fp);
  }
  if(i!=SQLITE_DONE) fprintf(stderr,"SQL error (%d): %s\n",i,sqlite3_errmsg(userdb));
done:
  if(st) sqlite3_finalize(st);
  if(fp) fclose(fp);
  printStatus("Done\n");
}

static sqlite3_int64 ask_picture_id(const char*t) {
  sqlite3_stmt*st;
  const char*r=screen_prompt(t);
  int i;
  sqlite3_int64 id=0;

Modified picture.c from [292dc50f72] to [e253fa7409].

774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
  Uint8 altImage;
  Uint8 havesize1[256];
  Uint16 havesize[256];
  char*nam=sqlite3_mprintf("%s.xclass",basefilename);
  const char*v;
  int i,j,n;
  if(!nam) fatal("Allocation failed\n");
  fprintf(stderr,"Loading pictures...\n");
  fp=main_options['z']?composite_slice(".xclass",1):fopen(nam,"r");
  if(!fp) fatal("Failed to open xclass file (%m)\n");
  sqlite3_free(nam);
  optionquery[1]=Q_altImage;
  altImage=strtol(xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"0",0,10);
  optionquery[1]=Q_imageSize;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,2);







|







774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
  Uint8 altImage;
  Uint8 havesize1[256];
  Uint16 havesize[256];
  char*nam=sqlite3_mprintf("%s.xclass",basefilename);
  const char*v;
  int i,j,n;
  if(!nam) fatal("Allocation failed\n");
  printStatus("Loading pictures...\n");
  fp=main_options['z']?composite_slice(".xclass",1):fopen(nam,"r");
  if(!fp) fatal("Failed to open xclass file (%m)\n");
  sqlite3_free(nam);
  optionquery[1]=Q_altImage;
  altImage=strtol(xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"0",0,10);
  optionquery[1]=Q_imageSize;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,2);
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    }
  }
  sqlite3_finalize(st);
  fclose(fp);
  SDL_SetColorKey(picts,SDL_SRCCOLORKEY|SDL_RLEACCEL,0);
done:
  if(n=sqlite3_exec(userdb,"COMMIT;",0,0,0)) fatal("SQL error (%d): %s\n",n,sqlite3_errmsg(userdb));
  fprintf(stderr,"Done\n");
}

void init_screen(void) {
  const char*v;
  int w,h,i;
  if(main_options['x']) return;
  if(!fontdata) fontdata=pcfont;







|







894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    }
  }
  sqlite3_finalize(st);
  fclose(fp);
  SDL_SetColorKey(picts,SDL_SRCCOLORKEY|SDL_RLEACCEL,0);
done:
  if(n=sqlite3_exec(userdb,"COMMIT;",0,0,0)) fatal("SQL error (%d): %s\n",n,sqlite3_errmsg(userdb));
  printStatus("Done\n");
}

void init_screen(void) {
  const char*v;
  int w,h,i;
  if(main_options['x']) return;
  if(!fontdata) fontdata=pcfont;

Modified sound.c from [feb9ba4e2f] to [f2bb0c2e49].

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  if(!v) return;
  spec.freq=strtol(v,0,10);
  optionquery[2]=Q_buffer;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,3);
  if(!v) return;
  spec.samples=strtol(v,0,10);
  if(!spec.freq || !spec.samples) return;
  fprintf(stderr,"Initializing audio...\n");
  spec.channels=1;
  spec.format=AUDIO_S16SYS;
  spec.callback=audio_callback;
  if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
    fprintf(stderr,"Cannot initalize audio subsystem.\n");
    return;
  }







|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  if(!v) return;
  spec.freq=strtol(v,0,10);
  optionquery[2]=Q_buffer;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,3);
  if(!v) return;
  spec.samples=strtol(v,0,10);
  if(!spec.freq || !spec.samples) return;
  printStatus("Initializing audio...\n");
  spec.channels=1;
  spec.format=AUDIO_S16SYS;
  spec.callback=audio_callback;
  if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
    fprintf(stderr,"Cannot initalize audio subsystem.\n");
    return;
  }
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    for(i=0;i<190;i++) mmltuning[i]=f*pow(2.0,(i-96)/24.0);
    for(i=0;i<64;i++) mmltuning[i+190]=(((long long)(i+2))<<37)/spec.freq;
    optionquery[2]=Q_mmlTempo;
    if(v=xrm_get_resource(resourcedb,optionquery,optionquery,3)) i=strtol(v,0,10); else i=120;
    // Convert quarter notes per minute to samples per sixty-fourth note
    mmltempo=(spec.freq*60)/(i*16);
  }
  fprintf(stderr,"Done.\n");
  wavesound=0;
  mmlpos=0;
  SDL_PauseAudio(0);
  sound_on=1;
}

static void set_mml(const unsigned char*s) {







|







283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    for(i=0;i<190;i++) mmltuning[i]=f*pow(2.0,(i-96)/24.0);
    for(i=0;i<64;i++) mmltuning[i+190]=(((long long)(i+2))<<37)/spec.freq;
    optionquery[2]=Q_mmlTempo;
    if(v=xrm_get_resource(resourcedb,optionquery,optionquery,3)) i=strtol(v,0,10); else i=120;
    // Convert quarter notes per minute to samples per sixty-fourth note
    mmltempo=(spec.freq*60)/(i*16);
  }
  printStatus("Done.\n");
  wavesound=0;
  mmlpos=0;
  SDL_PauseAudio(0);
  sound_on=1;
}

static void set_mml(const unsigned char*s) {