Index: bindings.c ================================================================== --- bindings.c +++ bindings.c @@ -118,21 +118,21 @@ xrm_enumerate(db,cb_2,usr); return 0; } void load_key_bindings(void) { - fprintf(stderr,"Loading key bindings...\n"); + 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); - fprintf(stderr,"Done\n"); + printStatus("Done\n"); } const UserCommand*find_key_binding(SDL_Event*ev,int editing) { KeyBinding*kb; static const UserCommand nul={cmd:0}; Index: class.c ================================================================== --- class.c +++ class.c @@ -2560,11 +2560,11 @@ int i; int gloptr=0; Hash*glolocalhash; char*nam=sqlite3_mprintf("%s.class",basefilename); sqlite3_stmt*vst=0; - fprintf(stderr,"Loading class definitions...\n"); + 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)); @@ -2780,7 +2780,7 @@ 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"); + printStatus("Done\n"); } Index: comconfig.doc ================================================================== --- comconfig.doc +++ comconfig.doc @@ -25,10 +25,14 @@ 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 @@ -49,45 +53,66 @@ === 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. + 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_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.) + 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.) @@ -105,10 +130,15 @@ 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. Index: commandline.doc ================================================================== --- commandline.doc +++ commandline.doc @@ -129,6 +129,9 @@ 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. Index: function.c ================================================================== --- function.c +++ function.c @@ -1716,11 +1716,11 @@ 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"); + 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)) @@ -1775,11 +1775,11 @@ 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"); + printStatus("Done\n"); if(screen) set_cursor(XC_arrow); return SQLITE_SCHEMA; } Module(vt_levels, Index: heromesh.h ================================================================== --- heromesh.h +++ heromesh.h @@ -3,10 +3,16 @@ */ #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 Index: main.c ================================================================== --- main.c +++ main.c @@ -566,26 +566,26 @@ } static void flush_usercache(void) { int e; if(main_options['r']) return; - fprintf(stderr,"Flushing user cache...\n"); + 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)); - fprintf(stderr,"Done\n"); + 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); - fprintf(stderr,"Loading puzzle set...\n"); + 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); @@ -626,11 +626,11 @@ 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"); + printStatus("Done\n"); } static void init_usercache(void) { sqlite3_stmt*st; int z; @@ -637,11 +637,11 @@ sqlite3_int64 t1,t2; char*nam1; char*nam2; char*nam3; struct stat fst; - fprintf(stderr,"Initializing user cache...\n"); + 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); @@ -689,11 +689,11 @@ 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"); + printStatus("Done\n"); } static void new_puzzle_set(void) { char*nam1; char*nam2; @@ -1091,11 +1091,11 @@ 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(!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"); @@ -1179,13 +1179,13 @@ } 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"); + 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(); } } Index: picedit.c ================================================================== --- picedit.c +++ picedit.c @@ -211,11 +211,11 @@ "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"); + printStatus("Loading pictures...\n"); fp=fopen(nam,"r"); sqlite3_free(nam); if(!fp) { nam=0; fprintf(stderr,"%m\n"); @@ -257,11 +257,11 @@ done: if(st) sqlite3_finalize(st); if(fp) fclose(fp); free(nam); free(buf); - fprintf(stderr,"Done\n"); + 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" @@ -277,11 +277,11 @@ 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"); + 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)); @@ -299,11 +299,11 @@ } 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"); + printStatus("Done\n"); } static sqlite3_int64 ask_picture_id(const char*t) { sqlite3_stmt*st; const char*r=screen_prompt(t); Index: picture.c ================================================================== --- picture.c +++ picture.c @@ -776,11 +776,11 @@ 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"); + 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); @@ -896,11 +896,11 @@ 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"); + printStatus("Done\n"); } void init_screen(void) { const char*v; int w,h,i; Index: sound.c ================================================================== --- sound.c +++ sound.c @@ -249,11 +249,11 @@ 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"); + 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"); @@ -285,11 +285,11 @@ 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"); + printStatus("Done.\n"); wavesound=0; mmlpos=0; SDL_PauseAudio(0); sound_on=1; }