1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5
6
7 #include "conf.h"
8
9 #include "naev.h"
10
11 #include <stdlib.h> /* atoi */
12 #include <unistd.h> /* getopt */
13 #include "nstring.h" /* strdup */
14 #include <getopt.h> /* getopt_long */
15
16 #include "nlua.h"
17
18 #include "log.h"
19 #include "player.h"
20 #include "input.h"
21 #include "opengl.h"
22 #include "music.h"
23 #include "nebula.h"
24 #include "ndata.h"
25 #include "nfile.h"
26 #include "nstring.h"
27
28
29 #define conf_loadInt(n,i) \
30 nlua_getenv(env,n); \
31 if (lua_isnumber(naevL, -1)) { \
32 i = (int)lua_tonumber(naevL, -1); \
33 } \
34 lua_pop(naevL,1);
35
36 #define conf_loadFloat(n,f) \
37 nlua_getenv(env,n); \
38 if (lua_isnumber(naevL, -1)) { \
39 f = (double)lua_tonumber(naevL, -1); \
40 } \
41 lua_pop(naevL,1);
42
43 #define conf_loadBool(n,b) \
44 nlua_getenv(env, n); \
45 if (lua_isnumber(naevL,-1)) \
46 b = (lua_tonumber(naevL,-1) != 0.); \
47 else if (!lua_isnil(naevL,-1)) \
48 b = lua_toboolean(naevL, -1); \
49 lua_pop(naevL,1);
50
51 #define conf_loadString(n,s) \
52 nlua_getenv(env, n); \
53 if (lua_isstring(naevL, -1)) { \
54 if (s != NULL) \
55 free(s); \
56 s = strdup(lua_tostring(naevL, -1)); \
57 } \
58 lua_pop(naevL,1);
59
60
61 /* Global configuration. */
62 PlayerConf_t conf = { .ndata = NULL, .sound_backend = NULL, .joystick_nam = NULL };
63
64 /* from main.c */
65 extern int show_fps;
66 extern int max_fps;
67 extern int indjoystick;
68 extern char* namjoystick;
69 /* from player.c */
70 extern const char *keybind_info[][3]; /* keybindings */
71 /* from input.c */
72 extern unsigned int input_afterburnSensitivity;
73
74
75 /*
76 * prototypes
77 */
78 static void print_usage( char **argv );
79
80
81 /*
82 * prints usage
83 */
print_usage(char ** argv)84 static void print_usage( char **argv )
85 {
86 LOG("Usage: %s [OPTIONS] [DATA]", argv[0]);
87 LOG("Options are:");
88 LOG(" -f, --fullscreen activate fullscreen");
89 LOG(" -F n, --fps n limit frames per second to n");
90 LOG(" -V, --vsync enable vsync");
91 LOG(" -W n set width to n");
92 LOG(" -H n set height to n");
93 LOG(" -j n, --joystick n use joystick n");
94 LOG(" -J s, --Joystick s use joystick whose name contains s");
95 LOG(" -M, --mute disables sound");
96 LOG(" -S, --sound forces sound");
97 LOG(" -m f, --mvol f sets the music volume to f");
98 LOG(" -s f, --svol f sets the sound volume to f");
99 LOG(" -G, --generate regenerates the nebula (slow)");
100 LOG(" -N, --nondata do not use ndata and try to use laid out files");
101 LOG(" -d, --datapath specifies a custom path for all user data (saves, screenshots, etc.)");
102 LOG(" -X, --scale defines the scale factor");
103 #ifdef DEBUGGING
104 LOG(" --devmode enables dev mode perks like the editors");
105 LOG(" --devcsv generates csv output from the ndata for development purposes");
106 #endif /* DEBUGGING */
107 LOG(" -h, --help display this message and exit");
108 LOG(" -v, --version print the version and exit");
109 }
110
111
112 /**
113 * @brief Sets the default configuration.
114 */
conf_setDefaults(void)115 void conf_setDefaults (void)
116 {
117 conf_cleanup();
118
119 /* ndata. */
120 if (conf.ndata != NULL)
121 free(conf.ndata);
122 conf.ndata = NULL;
123
124 /* Joystick. */
125 conf.joystick_ind = -1;
126 if (conf.joystick_nam != NULL)
127 free(conf.joystick_nam);
128 conf.joystick_nam = NULL;
129
130 /* GUI. */
131 conf.mesg_visible = 5;
132
133 /* Repeat. */
134 conf.repeat_delay = 500;
135 conf.repeat_freq = 30;
136
137 /* Dynamic zoom. */
138 conf.zoom_manual = 0;
139 conf.zoom_far = 0.5;
140 conf.zoom_near = 1.;
141 conf.zoom_speed = 0.25;
142 conf.zoom_stars = 1.;
143
144 /* Font sizes. */
145 conf.font_size_console = 10;
146 conf.font_size_intro = 18;
147 conf.font_size_def = 12;
148 conf.font_size_small = 10;
149
150 /* Misc. */
151 conf.redirect_file = 1;
152 conf.nosave = 0;
153 conf.devmode = 0;
154 conf.devautosave = 0;
155 conf.devcsv = 0;
156
157 /* Gameplay. */
158 conf_setGameplayDefaults();
159
160 /* Audio. */
161 conf_setAudioDefaults();
162
163 /* Video. */
164 conf_setVideoDefaults();
165
166 /* Input */
167 input_setDefault(1);
168
169 /* Debugging. */
170 conf.fpu_except = 0; /* Causes many issues. */
171
172 /* Editor. */
173 if (conf.dev_save_sys != NULL)
174 free( conf.dev_save_sys );
175 conf.dev_save_sys = strdup( DEV_SAVE_SYSTEM_DEFAULT );
176 if (conf.dev_save_map != NULL)
177 free( conf.dev_save_map );
178 conf.dev_save_map = strdup( DEV_SAVE_MAP_DEFAULT );
179 if (conf.dev_save_asset != NULL)
180 free( conf.dev_save_asset );
181 conf.dev_save_asset = strdup( DEV_SAVE_ASSET_DEFAULT );
182 }
183
184
185 /**
186 * @brief Sets the gameplay defaults.
187 */
conf_setGameplayDefaults(void)188 void conf_setGameplayDefaults (void)
189 {
190 conf.afterburn_sens = AFTERBURNER_SENSITIVITY_DEFAULT;
191 conf.compression_velocity = TIME_COMPRESSION_DEFAULT_MAX;
192 conf.compression_mult = TIME_COMPRESSION_DEFAULT_MULT;
193 conf.save_compress = SAVE_COMPRESSION_DEFAULT;
194 conf.mouse_thrust = MOUSE_THRUST_DEFAULT;
195 conf.mouse_doubleclick = MOUSE_DOUBLECLICK_TIME;
196 conf.autonav_reset_speed = AUTONAV_RESET_SPEED_DEFAULT;
197 conf.zoom_manual = MANUAL_ZOOM_DEFAULT;
198 }
199
200
201 /**
202 * @brief Sets the audio defaults.
203 */
conf_setAudioDefaults(void)204 void conf_setAudioDefaults (void)
205 {
206 if (conf.sound_backend != NULL) {
207 free(conf.sound_backend);
208 conf.sound_backend = NULL;
209 }
210
211 /* Sound. */
212 conf.sound_backend = strdup(BACKEND_DEFAULT);
213 conf.snd_voices = VOICES_DEFAULT;
214 conf.snd_pilotrel = PILOT_RELATIVE_DEFAULT;
215 conf.al_efx = USE_EFX_DEFAULT;
216 conf.al_bufsize = BUFFER_SIZE_DEFAULT;
217 conf.nosound = MUTE_SOUND_DEFAULT;
218 conf.sound = SOUND_VOLUME_DEFAULT;
219 conf.music = MUSIC_VOLUME_DEFAULT;
220 }
221
222
223 /**
224 * @brief Sets the video defaults.
225 */
conf_setVideoDefaults(void)226 void conf_setVideoDefaults (void)
227 {
228 int w, h, f;
229
230 /* More complex resolution handling. */
231 f = 0;
232 if ((gl_screen.desktop_w > 0) && (gl_screen.desktop_h > 0)) {
233 /* Try higher resolution. */
234 w = RESOLUTION_W_DEFAULT;
235 h = RESOLUTION_H_DEFAULT;
236
237 /* Fullscreen and fit everything onscreen. */
238 if ((gl_screen.desktop_w <= w) || (gl_screen.desktop_h <= h)) {
239 w = gl_screen.desktop_w;
240 h = gl_screen.desktop_h;
241 f = FULLSCREEN_DEFAULT;
242 }
243 }
244 else {
245 w = 800;
246 h = 600;
247 }
248
249 /* OpenGL. */
250 conf.fsaa = FSAA_DEFAULT;
251 conf.vsync = VSYNC_DEFAULT;
252 conf.vbo = VBO_DEFAULT; /* Seems to cause a lot of issues. */
253 conf.mipmaps = MIPMAP_DEFAULT; /* Also cause for issues. */
254 conf.compress = TEXTURE_COMPRESSION_DEFAULT;
255 conf.interpolate = INTERPOLATION_DEFAULT;
256 conf.npot = NPOT_TEXTURES_DEFAULT;
257
258 /* Window. */
259 conf.fullscreen = f;
260 conf.width = w;
261 conf.height = h;
262 conf.explicit_dim = 0; /* No need for a define, this is only for first-run. */
263 conf.scalefactor = SCALE_FACTOR_DEFAULT;
264 conf.minimize = MINIMIZE_DEFAULT;
265
266 /* FPS. */
267 conf.fps_show = SHOW_FPS_DEFAULT;
268 conf.fps_max = FPS_MAX_DEFAULT;
269
270 /* Pause. */
271 conf.pause_show = SHOW_PAUSE_DEFAULT;
272
273 /* Memory. */
274 conf.engineglow = ENGINE_GLOWS_DEFAULT;
275 }
276
277
278 /*
279 * Frees some memory the conf allocated.
280 */
conf_cleanup(void)281 void conf_cleanup (void)
282 {
283 if (conf.ndata != NULL)
284 free(conf.ndata);
285 if (conf.sound_backend != NULL)
286 free(conf.sound_backend);
287 if (conf.joystick_nam != NULL)
288 free(conf.joystick_nam);
289
290 if (conf.dev_save_sys != NULL)
291 free(conf.dev_save_sys);
292 if (conf.dev_save_map != NULL)
293 free(conf.dev_save_map);
294 if (conf.dev_save_asset != NULL)
295 free(conf.dev_save_asset);
296
297 /* Clear memory. */
298 memset( &conf, 0, sizeof(conf) );
299 }
300
301
302 /*
303 * @brief Parses the local conf that dictates where user data goes.
304 */
conf_loadConfigPath(void)305 void conf_loadConfigPath( void )
306 {
307 const char *file = "datapath.lua";
308
309 if (!nfile_fileExists(file))
310 return;
311
312 nlua_env env = nlua_newEnv(0);
313 if (nlua_dofileenv(env, file) == 0)
314 conf_loadString("datapath",conf.datapath);
315
316 nlua_freeEnv(env);
317 }
318
319
320 /*
321 * parses the config file
322 */
conf_loadConfig(const char * file)323 int conf_loadConfig ( const char* file )
324 {
325 int i, t;
326 const char *str, *mod;
327 SDLKey key;
328 int type;
329 int w,h;
330 SDLMod m;
331
332 /* Check to see if file exists. */
333 if (!nfile_fileExists(file))
334 return nfile_touch(file);
335
336 /* Load the configuration. */
337 nlua_env env = nlua_newEnv(0);
338 if (nlua_dofileenv(env, file) == 0) {
339
340 /* ndata. */
341 conf_loadString("data",conf.ndata);
342
343 /* OpenGL. */
344 conf_loadInt("fsaa",conf.fsaa);
345 conf_loadBool("vsync",conf.vsync);
346 conf_loadBool("vbo",conf.vbo);
347 conf_loadBool("mipmaps",conf.mipmaps);
348 conf_loadBool("compress",conf.compress);
349 conf_loadBool("interpolate",conf.interpolate);
350 conf_loadBool("npot",conf.npot);
351
352 /* Memory. */
353 conf_loadBool("engineglow",conf.engineglow);
354
355 /* Window. */
356 w = h = 0;
357 conf_loadInt("width",w);
358 conf_loadInt("height",h);
359 if (w != 0) {
360 conf.explicit_dim = 1;
361 conf.width = w;
362 }
363 if (h != 0) {
364 conf.explicit_dim = 1;
365 conf.height = h;
366 }
367 conf_loadFloat("scalefactor",conf.scalefactor);
368 conf_loadBool("fullscreen",conf.fullscreen);
369 conf_loadBool("modesetting",conf.modesetting);
370 conf_loadBool("minimize",conf.minimize);
371
372 /* FPS */
373 conf_loadBool("showfps",conf.fps_show);
374 conf_loadInt("maxfps",conf.fps_max);
375
376 /* Pause */
377 conf_loadBool("showpause",conf.pause_show);
378
379 /* Sound. */
380 conf_loadString("sound_backend",conf.sound_backend);
381 conf_loadInt("snd_voices",conf.snd_voices);
382 conf.snd_voices = MAX( 16, conf.snd_voices ); /* Must be at least 16. */
383 conf_loadBool("snd_pilotrel",conf.snd_pilotrel);
384 conf_loadBool("al_efx",conf.al_efx);
385 conf_loadInt("al_bufsize", conf.al_bufsize);
386 conf_loadBool("nosound",conf.nosound);
387 conf_loadFloat("sound",conf.sound);
388 conf_loadFloat("music",conf.music);
389
390 /* Joystick. */
391 nlua_getenv(env, "joystick");
392 if (lua_isnumber(naevL, -1))
393 conf.joystick_ind = (int)lua_tonumber(naevL, -1);
394 else if (lua_isstring(naevL, -1))
395 conf.joystick_nam = strdup(lua_tostring(naevL, -1));
396 lua_pop(naevL,1);
397
398 /* GUI. */
399 conf_loadInt("mesg_visible",conf.mesg_visible);
400 if (conf.mesg_visible <= 0)
401 conf.mesg_visible = 5;
402
403 /* Key repeat. */
404 conf_loadInt("repeat_delay",conf.repeat_delay);
405 conf_loadInt("repeat_freq",conf.repeat_freq);
406
407 /* Zoom. */
408 conf_loadBool("zoom_manual",conf.zoom_manual);
409 conf_loadFloat("zoom_far",conf.zoom_far);
410 conf_loadFloat("zoom_near",conf.zoom_near);
411 conf_loadFloat("zoom_speed",conf.zoom_speed);
412 conf_loadFloat("zoom_stars",conf.zoom_stars);
413
414 /* Font size. */
415 conf_loadInt("font_size_console",conf.font_size_console);
416 conf_loadInt("font_size_intro",conf.font_size_intro);
417 conf_loadInt("font_size_def",conf.font_size_def);
418 conf_loadInt("font_size_small",conf.font_size_small);
419
420 /* Misc. */
421 conf_loadFloat("compression_velocity",conf.compression_velocity);
422 conf_loadFloat("compression_mult",conf.compression_mult);
423 conf_loadBool("redirect_file",conf.redirect_file);
424 conf_loadBool("save_compress",conf.save_compress);
425 conf_loadInt("afterburn_sensitivity",conf.afterburn_sens);
426 conf_loadInt("mouse_thrust",conf.mouse_thrust);
427 conf_loadFloat("mouse_doubleclick",conf.mouse_doubleclick);
428 conf_loadFloat("autonav_abort",conf.autonav_reset_speed);
429 conf_loadBool("devmode",conf.devmode);
430 conf_loadBool("devautosave",conf.devautosave);
431 conf_loadBool("conf_nosave",conf.nosave);
432
433 /* Debugging. */
434 conf_loadBool("fpu_except",conf.fpu_except);
435
436 /* Editor. */
437 conf_loadString("dev_save_sys",conf.dev_save_sys);
438 conf_loadString("dev_save_map",conf.dev_save_map);
439 conf_loadString("dev_save_asset",conf.dev_save_asset);
440
441 /*
442 * Keybindings.
443 */
444 for (i=0; strcmp(keybind_info[i][0],"end"); i++) {
445 nlua_getenv(env, keybind_info[i][0]);
446 /* Handle "none". */
447 if (lua_isstring(naevL,-1)) {
448 str = lua_tostring(naevL,-1);
449 if (strcmp(str,"none")==0) {
450 input_setKeybind( keybind_info[i][0],
451 KEYBIND_NULL, SDLK_UNKNOWN, NMOD_NONE );
452 }
453 }
454 else if (lua_istable(naevL, -1)) { /* it's a table */
455 /* gets the event type */
456 lua_pushstring(naevL, "type");
457 lua_gettable(naevL, -2);
458 if (lua_isstring(naevL, -1))
459 str = lua_tostring(naevL, -1);
460 else if (lua_isnil(naevL, -1)) {
461 WARN("Found keybind with no type field!");
462 str = "null";
463 }
464 else {
465 WARN("Found keybind with invalid type field!");
466 str = "null";
467 }
468 lua_pop(naevL,1);
469
470 /* gets the key */
471 lua_pushstring(naevL, "key");
472 lua_gettable(naevL, -2);
473 t = lua_type(naevL, -1);
474 if (t == LUA_TNUMBER)
475 key = (int)lua_tonumber(naevL, -1);
476 else if (t == LUA_TSTRING)
477 key = input_keyConv( lua_tostring(naevL, -1));
478 else if (t == LUA_TNIL) {
479 WARN("Found keybind with no key field!");
480 key = SDLK_UNKNOWN;
481 }
482 else {
483 WARN("Found keybind with invalid key field!");
484 key = SDLK_UNKNOWN;
485 }
486 lua_pop(naevL,1);
487
488 /* Get the modifier. */
489 lua_pushstring(naevL, "mod");
490 lua_gettable(naevL, -2);
491 if (lua_isstring(naevL, -1))
492 mod = lua_tostring(naevL, -1);
493 else
494 mod = NULL;
495 lua_pop(naevL,1);
496
497 if (str != NULL) { /* keybind is valid */
498 if (key == SDLK_UNKNOWN) {
499 WARN("Keybind for '%s' is invalid", keybind_info[i][0]);
500 continue;
501 }
502
503 /* get type */
504 if (strcmp(str,"null")==0) type = KEYBIND_NULL;
505 else if (strcmp(str,"keyboard")==0) type = KEYBIND_KEYBOARD;
506 else if (strcmp(str,"jaxispos")==0) type = KEYBIND_JAXISPOS;
507 else if (strcmp(str,"jaxisneg")==0) type = KEYBIND_JAXISNEG;
508 else if (strcmp(str,"jbutton")==0) type = KEYBIND_JBUTTON;
509 else {
510 WARN("Unknown keybinding of type %s", str);
511 continue;
512 }
513
514 /* Set modifier, probably should be able to handle two at a time. */
515 if (mod != NULL) {
516 /* The "rctrl/lctrl" friends are for compat with 0.4.0 and older, remove around 0.5.0 or so. */
517 if (strcmp(mod,"ctrl")==0) m = NMOD_CTRL;
518 else if (strcmp(mod,"lctrl")==0) m = NMOD_CTRL; /* compat. */
519 else if (strcmp(mod,"rctrl")==0) m = NMOD_CTRL; /* compat. */
520 else if (strcmp(mod,"shift")==0) m = NMOD_SHIFT;
521 else if (strcmp(mod,"lshift")==0) m = NMOD_SHIFT; /* compat. */
522 else if (strcmp(mod,"rshift")==0) m = NMOD_SHIFT; /* compat. */
523 else if (strcmp(mod,"alt")==0) m = NMOD_ALT;
524 else if (strcmp(mod,"lalt")==0) m = NMOD_ALT; /* compat. */
525 else if (strcmp(mod,"ralt")==0) m = NMOD_ALT; /* compat. */
526 else if (strcmp(mod,"meta")==0) m = NMOD_META;
527 else if (strcmp(mod,"lmeta")==0) m = NMOD_META; /* compat. */
528 else if (strcmp(mod,"rmeta")==0) m = NMOD_META; /* compat. */
529 else if (strcmp(mod,"any")==0) m = NMOD_ALL;
530 else if (strcmp(mod,"none")==0) m = NMOD_NONE;
531 else {
532 WARN("Unknown keybinding mod of type %s", mod);
533 m = NMOD_NONE;
534 }
535 }
536 else
537 m = NMOD_NONE;
538
539 /* set the keybind */
540 input_setKeybind( keybind_info[i][0], type, key, m );
541 }
542 else
543 WARN("Malformed keybind for '%s' in '%s'.", keybind_info[i][0], file);
544 }
545 /* clean up after table stuff */
546 lua_pop(naevL,1);
547 }
548 }
549 else { /* failed to load the config file */
550 WARN("Config file '%s' has invalid syntax:", file );
551 WARN(" %s", lua_tostring(naevL,-1));
552 nlua_freeEnv(env);
553 return 1;
554 }
555
556 nlua_freeEnv(env);
557 return 0;
558 }
559
560
conf_parseCLIPath(int argc,char ** argv)561 void conf_parseCLIPath( int argc, char** argv )
562 {
563 static struct option long_options[] = {
564 { "datapath", required_argument, 0, 'd' },
565 { NULL, 0, 0, 0 }
566 };
567
568 int option_index = 1;
569 int c = 0;
570
571 /* GNU giveth, and GNU taketh away.
572 * If we don't specify "-" as the first char, getopt will happily
573 * mangle the initial argument order, probably causing crashes when
574 * passing arguments that take values, such as -H and -W.
575 */
576 while ((c = getopt_long(argc, argv, "-:d:",
577 long_options, &option_index)) != -1) {
578 switch(c) {
579 case 'd':
580 conf.datapath = strdup(optarg);
581 break;
582 }
583 }
584 }
585
586
587 /*
588 * parses the CLI options
589 */
conf_parseCLI(int argc,char ** argv)590 void conf_parseCLI( int argc, char** argv )
591 {
592 static struct option long_options[] = {
593 { "datapath", required_argument, 0, 'd' },
594 { "fullscreen", no_argument, 0, 'f' },
595 { "fps", required_argument, 0, 'F' },
596 { "vsync", no_argument, 0, 'V' },
597 { "joystick", required_argument, 0, 'j' },
598 { "Joystick", required_argument, 0, 'J' },
599 { "width", required_argument, 0, 'W' },
600 { "height", required_argument, 0, 'H' },
601 { "mute", no_argument, 0, 'M' },
602 { "sound", no_argument, 0, 'S' },
603 { "mvol", required_argument, 0, 'm' },
604 { "svol", required_argument, 0, 's' },
605 { "generate", no_argument, 0, 'G' },
606 { "nondata", no_argument, 0, 'N' },
607 { "scale", required_argument, 0, 'X' },
608 #ifdef DEBUGGING
609 { "devmode", no_argument, 0, 'D' },
610 { "devcsv", no_argument, 0, 'C' },
611 #endif /* DEBUGGING */
612 { "help", no_argument, 0, 'h' },
613 { "version", no_argument, 0, 'v' },
614 { NULL, 0, 0, 0 } };
615 int option_index = 1;
616 int c = 0;
617
618 /* man 3 getopt says optind should be initialized to 1, but that seems to
619 * cause all options to get parsed, i.e. we cannot detect a trailing ndata
620 * option.
621 */
622 optind = 0;
623 while ((c = getopt_long(argc, argv,
624 "fF:Vd:j:J:W:H:MSm:s:X:GNhv",
625 long_options, &option_index)) != -1) {
626 switch (c) {
627 case 'd':
628 /* Does nothing, datapath is parsed earlier. */
629 break;
630 case 'f':
631 conf.fullscreen = 1;
632 break;
633 case 'F':
634 conf.fps_max = atoi(optarg);
635 break;
636 case 'V':
637 conf.vsync = 1;
638 break;
639 case 'j':
640 conf.joystick_ind = atoi(optarg);
641 break;
642 case 'J':
643 conf.joystick_nam = strdup(optarg);
644 break;
645 case 'W':
646 conf.width = atoi(optarg);
647 conf.explicit_dim = 1;
648 break;
649 case 'H':
650 conf.height = atoi(optarg);
651 conf.explicit_dim = 1;
652 break;
653 case 'M':
654 conf.nosound = 1;
655 break;
656 case 'S':
657 conf.nosound = 0;
658 break;
659 case 'm':
660 conf.music = atof(optarg);
661 break;
662 case 's':
663 conf.sound = atof(optarg);
664 break;
665 case 'G':
666 nebu_forceGenerate();
667 break;
668 case 'N':
669 if (conf.ndata != NULL)
670 free(conf.ndata);
671 conf.ndata = NULL;
672 break;
673 case 'X':
674 conf.scalefactor = atof(optarg);
675 break;
676 #ifdef DEBUGGING
677 case 'D':
678 conf.devmode = 1;
679 LOG("Enabling developer mode.");
680 break;
681
682 case 'C':
683 conf.devcsv = 1;
684 LOG("Will generate CSV output.");
685 break;
686 #endif /* DEBUGGING */
687
688 case 'v':
689 /* by now it has already displayed the version */
690 exit(EXIT_SUCCESS);
691 case 'h':
692 print_usage(argv);
693 exit(EXIT_SUCCESS);
694 }
695 }
696
697 /** @todo handle multiple ndata. */
698 if (optind < argc)
699 conf.ndata = strdup( argv[ optind ] );
700 }
701
702
703 /**
704 * @brief nsnprintf-like function to quote and escape a string for use in Lua source code
705 *
706 * @param str The destination buffer
707 * @param size The maximum amount of space in str to use
708 * @param text The string to quote and escape
709 * @return The number of characters actually written to str
710 */
quoteLuaString(char * str,size_t size,const char * text)711 static size_t quoteLuaString(char *str, size_t size, const char *text)
712 {
713 const unsigned char *in;
714 char slashescape;
715 size_t count;
716
717 if (size == 0)
718 return 0;
719
720 /* Write a Lua nil if we are given a NULL pointer */
721 if (text == NULL)
722 return nsnprintf(str, size, "nil");
723
724 count = 0;
725
726 /* Quote start */
727 str[count++] = '\"';
728 if (count == size)
729 return count;
730
731 /* Iterate over the characters in text */
732 for (in = (const unsigned char *)text; *in != '\0'; in++) {
733 /* Check if we can print this as a friendly backslash-escape */
734 switch (*in) {
735 case '\a': slashescape = 'a'; break;
736 case '\b': slashescape = 'b'; break;
737 case '\f': slashescape = 'f'; break;
738 case '\n': slashescape = 'n'; break;
739 case '\r': slashescape = 'r'; break;
740 case '\t': slashescape = 't'; break;
741 case '\v': slashescape = 'v'; break;
742 case '\\': slashescape = '\\'; break;
743 case '\"': slashescape = '\"'; break;
744 case '\'': slashescape = '\''; break;
745 /* Technically, Lua can also represent \0, but we can't in our input */
746 default: slashescape = 0; break;
747 }
748 if (slashescape != 0)
749 {
750 /* Yes, we can use a backslash-escape! */
751 str[count++] = '\\';
752 if (count == size)
753 return count;
754
755 str[count++] = slashescape;
756 if (count == size)
757 return count;
758
759 continue;
760 }
761
762 /* Check if this is an otherwise printable ASCII character */
763 if (*in >= 0x20 && *in <= 0x7E)
764 {
765 /* Write it straight to the output if so */
766 str[count++] = *in;
767 if (count == size)
768 return count;
769
770 continue;
771 }
772
773 /* Otherwise, escape the character using a \ddd sequence */
774 str[count++] = '\\';
775 if (count == size)
776 return count;
777
778 count += nsnprintf(&str[count], size-count, "%03u", *in);
779 if (count == size)
780 return count;
781 }
782
783 /* Quote end */
784 str[count++] = '\"';
785 if (count == size)
786 return count;
787
788 /* zero-terminate, if possible */
789 if (count != size)
790 str[count] = '\0'; /* don't increase count, like nsnprintf */
791
792 /* return the amount of characters written */
793 return count;
794 }
795
796
797 #define conf_saveComment(t) \
798 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "-- %s\n", t);
799
800 #define conf_saveEmptyLine() \
801 if (sizeof(buf) != pos) \
802 buf[pos++] = '\n';
803
804 #define conf_saveInt(n,i) \
805 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "%s = %d\n", n, i);
806
807 #define conf_saveFloat(n,f) \
808 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "%s = %f\n", n, f);
809
810 #define conf_saveBool(n,b) \
811 if (b) \
812 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "%s = true\n", n); \
813 else \
814 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "%s = false\n", n);
815
816 #define conf_saveString(n,s) \
817 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "%s = ", n); \
818 pos += quoteLuaString(&buf[pos], sizeof(buf)-pos, s); \
819 if (sizeof(buf) != pos) \
820 buf[pos++] = '\n';
821
822 #define GENERATED_START_COMMENT "START GENERATED SECTION"
823 #define GENERATED_END_COMMENT "END GENERATED SECTION"
824
825
826 /*
827 * saves the current configuration
828 */
conf_saveConfig(const char * file)829 int conf_saveConfig ( const char* file )
830 {
831 int i;
832 char *old;
833 const char *oldfooter;
834 int oldsize;
835 char buf[32*1024];
836 size_t pos;
837 SDLKey key;
838 char keyname[17];
839 KeybindType type;
840 const char *typename;
841 SDLMod mod;
842 const char *modname;
843
844 pos = 0;
845 oldfooter = NULL;
846
847 /* User doesn't want to save the config. */
848 if (conf.nosave)
849 return 0;
850
851 /* Read the old configuration, if possible */
852 if (nfile_fileExists(file) && (old = nfile_readFile(&oldsize, file)) != NULL) {
853 /* See if we can find the generated section and preserve
854 * whatever the user wrote before it */
855 const char *tmp = nstrnstr(old, "-- "GENERATED_START_COMMENT"\n", oldsize);
856 if (tmp != NULL) {
857 /* Copy over the user content */
858 pos = MIN(sizeof(buf), (size_t)(tmp - old));
859 memcpy(buf, old, pos);
860
861 /* See if we can find the end of the section */
862 tmp = nstrnstr(tmp, "-- "GENERATED_END_COMMENT"\n", oldsize-pos);
863 if (tmp != NULL) {
864 /* Everything after this should also be preserved */
865 oldfooter = tmp + strlen("-- "GENERATED_END_COMMENT"\n");
866 oldsize -= (oldfooter - old);
867 }
868 }
869 else {
870 /* Treat the contents of the old file as a footer. */
871 oldfooter = old;
872 }
873 }
874 else {
875 old = NULL;
876
877 /* Write a nice header for new configuration files */
878 conf_saveComment(APPNAME " configuration file");
879 conf_saveEmptyLine();
880 }
881
882 /* Back up old configuration. */
883 if (nfile_backupIfExists(file) < 0) {
884 WARN("Not saving configuration.");
885 return -1;
886 }
887
888 /* Header. */
889 conf_saveComment(GENERATED_START_COMMENT);
890 conf_saveComment("The contents of this section will be rewritten by "APPNAME"!");
891 conf_saveEmptyLine();
892
893 /* ndata. */
894 conf_saveComment("The location of "APPNAME"'s data pack, usually called 'ndata'");
895 conf_saveString("data",conf.ndata);
896 conf_saveEmptyLine();
897
898 /* OpenGL. */
899 conf_saveComment("The factor to use in Full-Scene Anti-Aliasing");
900 conf_saveComment("Anything lower than 2 will simply disable FSAA");
901 conf_saveInt("fsaa",conf.fsaa);
902 conf_saveEmptyLine();
903
904 conf_saveComment("Synchronize framebuffer updates with the vertical blanking interval");
905 conf_saveBool("vsync",conf.vsync);
906 conf_saveEmptyLine();
907
908 conf_saveComment("Use OpenGL Vertex Buffer Objects extensions");
909 conf_saveBool("vbo",conf.vbo);
910 conf_saveEmptyLine();
911
912 conf_saveComment("Use OpenGL MipMaps");
913 conf_saveBool("mipmaps",conf.mipmaps);
914 conf_saveEmptyLine();
915
916 conf_saveComment("Use OpenGL Texture Compression");
917 conf_saveBool("compress",conf.compress);
918 conf_saveEmptyLine();
919
920 conf_saveComment("Use OpenGL Texture Interpolation");
921 conf_saveBool("interpolate",conf.interpolate);
922 conf_saveEmptyLine();
923
924 conf_saveComment("Use OpenGL Non-\"Power of Two\" textures if available");
925 conf_saveComment("Lowers memory usage by a lot, but may cause slow downs on some systems");
926 conf_saveBool("npot",conf.npot);
927 conf_saveEmptyLine();
928
929 /* Memory. */
930 conf_saveComment("If true enables engine glow");
931 conf_saveBool("engineglow",conf.engineglow);
932 conf_saveEmptyLine();
933
934 /* Window. */
935 conf_saveComment("The window size or screen resolution");
936 conf_saveComment("Set both of these to 0 to make "APPNAME" try the desktop resolution");
937 if (conf.explicit_dim) {
938 conf_saveInt("width",conf.width);
939 conf_saveInt("height",conf.height);
940 } else {
941 conf_saveInt("width",0);
942 conf_saveInt("height",0);
943 }
944 conf_saveEmptyLine();
945
946 conf_saveComment("Factor used to divide the above resolution with");
947 conf_saveComment("This is used to lower the rendering resolution, and scale to the above");
948 conf_saveFloat("scalefactor",conf.scalefactor);
949 conf_saveEmptyLine();
950
951 conf_saveComment("Run "APPNAME" in full-screen mode");
952 conf_saveBool("fullscreen",conf.fullscreen);
953 conf_saveEmptyLine();
954
955 conf_saveComment("Use video modesetting when fullscreen is enabled (SDL2-only)");
956 conf_saveBool("modesetting",conf.modesetting);
957 conf_saveEmptyLine();
958
959 conf_saveComment("Minimize on focus loss (SDL2-only)");
960 conf_saveBool("minimize",conf.minimize);
961 conf_saveEmptyLine();
962
963 /* FPS */
964 conf_saveComment("Display a framerate counter");
965 conf_saveBool("showfps",conf.fps_show);
966 conf_saveEmptyLine();
967
968 conf_saveComment("Limit the rendering framerate");
969 conf_saveInt("maxfps",conf.fps_max);
970 conf_saveEmptyLine();
971
972 /* Pause */
973 conf_saveComment("Show 'PAUSED' on screen while paused");
974 conf_saveBool("showpause",conf.pause_show);
975 conf_saveEmptyLine();
976
977 /* Sound. */
978 conf_saveComment("Sound backend (can be \"openal\" or \"sdlmix\")");
979 conf_saveString("sound_backend",conf.sound_backend);
980 conf_saveEmptyLine();
981
982 conf_saveComment("Maxmimum number of simultaneous sounds to play, must be at least 16.");
983 conf_saveInt("snd_voices",conf.snd_voices);
984 conf_saveEmptyLine();
985
986 conf_saveComment("Sets sound to be relative to pilot when camera is following a pilot instead of referenced to camera.");
987 conf_saveBool("snd_pilotrel",conf.snd_pilotrel);
988 conf_saveEmptyLine();
989
990 conf_saveComment("Enables EFX extension for OpenAL backend.");
991 conf_saveBool("al_efx",conf.al_efx);
992 conf_saveEmptyLine();
993
994 conf_saveComment("Size of the OpenAL music buffer (in kilobytes).");
995 conf_saveInt("al_bufsize",conf.al_bufsize);
996 conf_saveEmptyLine();
997
998 conf_saveComment("Disable all sound");
999 conf_saveBool("nosound",conf.nosound);
1000 conf_saveEmptyLine();
1001
1002 conf_saveComment("Volume of sound effects and music, between 0.0 and 1.0");
1003 conf_saveFloat("sound",(sound_disabled) ? conf.sound : sound_getVolume());
1004 conf_saveFloat("music",(music_disabled) ? conf.music : music_getVolume());
1005 conf_saveEmptyLine();
1006
1007 /* Joystick. */
1008 conf_saveComment("The name or numeric index of the joystick to use");
1009 conf_saveComment("Setting this to nil disables the joystick support");
1010 if (conf.joystick_nam != NULL) {
1011 conf_saveString("joystick",conf.joystick_nam);
1012 }
1013 else if (conf.joystick_ind >= 0) {
1014 conf_saveInt("joystick",conf.joystick_ind);
1015 }
1016 else {
1017 conf_saveString("joystick",NULL);
1018 }
1019 conf_saveEmptyLine();
1020
1021 /* GUI. */
1022 conf_saveComment("Number of lines visible in the comm window.");
1023 conf_saveInt("mesg_visible",conf.mesg_visible);
1024 conf_saveEmptyLine();
1025
1026 /* Key repeat. */
1027 conf_saveComment("Delay in ms before starting to repeat (0 disables)");
1028 conf_saveInt("repeat_delay",conf.repeat_delay);
1029 conf_saveComment("Delay in ms between repeats once it starts to repeat");
1030 conf_saveInt("repeat_freq",conf.repeat_freq);
1031 conf_saveEmptyLine();
1032
1033 /* Zoom. */
1034 conf_saveComment("Minimum and maximum zoom factor to use in-game");
1035 conf_saveComment("At 1.0, no sprites are scaled");
1036 conf_saveComment("zoom_far should be less then zoom_near");
1037 conf_saveBool("zoom_manual",conf.zoom_manual);
1038 conf_saveFloat("zoom_far",conf.zoom_far);
1039 conf_saveFloat("zoom_near",conf.zoom_near);
1040 conf_saveEmptyLine();
1041
1042 conf_saveComment("Zooming speed in factor increments per second");
1043 conf_saveFloat("zoom_speed",conf.zoom_speed);
1044 conf_saveEmptyLine();
1045
1046 conf_saveComment("Zooming modulation factor for the starry background");
1047 conf_saveFloat("zoom_stars",conf.zoom_stars);
1048 conf_saveEmptyLine();
1049
1050 /* Fonts. */
1051 conf_saveComment("Font sizes (in pixels) for NAEV");
1052 conf_saveComment("Warning, setting to other than the default can cause visual glitches!");
1053 conf_saveComment("Console default: 10");
1054 conf_saveInt("font_size_console",conf.font_size_console);
1055 conf_saveComment("Intro default: 18");
1056 conf_saveInt("font_size_intro",conf.font_size_intro);
1057 conf_saveComment("Default size: 12");
1058 conf_saveInt("font_size_def",conf.font_size_def);
1059 conf_saveComment("Small size: 10");
1060 conf_saveInt("font_size_small",conf.font_size_small);
1061 conf_saveEmptyLine();
1062
1063 /* Misc. */
1064 conf_saveComment("Sets the velocity (px/s) to compress up to when time compression is enabled.");
1065 conf_saveFloat("compression_velocity",conf.compression_velocity);
1066 conf_saveEmptyLine();
1067
1068 conf_saveComment("Sets the multiplier to compress up to when time compression is enabled.");
1069 conf_saveFloat("compression_mult",conf.compression_mult);
1070 conf_saveEmptyLine();
1071
1072 conf_saveComment("Redirects log and error output to files");
1073 conf_saveBool("redirect_file",conf.redirect_file);
1074 conf_saveEmptyLine();
1075
1076 conf_saveComment("Enables compression on savegames");
1077 conf_saveBool("save_compress",conf.save_compress);
1078 conf_saveEmptyLine();
1079
1080 conf_saveComment("Afterburner sensitivity");
1081 conf_saveInt("afterburn_sensitivity",conf.afterburn_sens);
1082 conf_saveEmptyLine();
1083
1084 conf_saveComment("Mouse-flying thrust control");
1085 conf_saveInt("mouse_thrust",conf.mouse_thrust);
1086 conf_saveEmptyLine();
1087
1088 conf_saveComment("Maximum interval to count as a double-click (0 disables).");
1089 conf_saveFloat("mouse_doubleclick",conf.mouse_doubleclick);
1090 conf_saveEmptyLine();
1091
1092 conf_saveComment("Condition under which the autonav aborts.");
1093 conf_saveFloat("autonav_abort",conf.autonav_reset_speed);
1094 conf_saveEmptyLine();
1095
1096 conf_saveComment("Enables developer mode (universe editor and the likes)");
1097 conf_saveBool("devmode",conf.devmode);
1098 conf_saveEmptyLine();
1099
1100 conf_saveComment("Automatic saving for developer mode");
1101 conf_saveBool("devautosave",conf.devautosave);
1102 conf_saveEmptyLine();
1103
1104 conf_saveComment("Save the config everytime game exits (rewriting this bit)");
1105 conf_saveInt("conf_nosave",conf.nosave);
1106 conf_saveEmptyLine();
1107
1108 /* Debugging. */
1109 conf_saveComment("Enables FPU exceptions - only works on DEBUG builds");
1110 conf_saveBool("fpu_except",conf.fpu_except);
1111 conf_saveEmptyLine();
1112
1113 /* Editor. */
1114 conf_saveComment("Paths for saving different files from the editor");
1115 conf_saveString("dev_save_sys",conf.dev_save_sys);
1116 conf_saveString("dev_save_map",conf.dev_save_map);
1117 conf_saveString("dev_save_asset",conf.dev_save_asset);
1118 conf_saveEmptyLine();
1119
1120 /*
1121 * Keybindings.
1122 */
1123 conf_saveEmptyLine();
1124 conf_saveComment("Keybindings");
1125 conf_saveEmptyLine();
1126
1127 /* Use an extra character in keyname to make sure it's always zero-terminated */
1128 keyname[sizeof(keyname)-1] = '\0';
1129
1130 /* Iterate over the keybinding names */
1131 for (i=0; strcmp(keybind_info[i][0], "end"); i++) {
1132 /* Save a comment line containing the description */
1133 conf_saveComment(input_getKeybindDescription( keybind_info[i][0] ));
1134
1135 /* Get the keybind */
1136 key = input_getKeybind( keybind_info[i][0], &type, &mod );
1137
1138 /* Determine the textual name for the keybind type */
1139 switch (type) {
1140 case KEYBIND_KEYBOARD: typename = "keyboard"; break;
1141 case KEYBIND_JAXISPOS: typename = "jaxispos"; break;
1142 case KEYBIND_JAXISNEG: typename = "jaxisneg"; break;
1143 case KEYBIND_JBUTTON: typename = "jbutton"; break;
1144 default: typename = NULL; break;
1145 }
1146 /* Write a nil if an unknown type */
1147 if ((typename == NULL) || (key == SDLK_UNKNOWN)) {
1148 conf_saveString( keybind_info[i][0],"none");
1149 continue;
1150 }
1151
1152 /* Determine the textual name for the modifier */
1153 switch ((int)mod) {
1154 case NMOD_CTRL: modname = "ctrl"; break;
1155 case NMOD_SHIFT: modname = "shift"; break;
1156 case NMOD_ALT: modname = "alt"; break;
1157 case NMOD_META: modname = "meta"; break;
1158 case NMOD_ALL: modname = "any"; break;
1159 default: modname = "none"; break;
1160 }
1161
1162 /* Determine the textual name for the key, if a keyboard keybind */
1163 if (type == KEYBIND_KEYBOARD)
1164 quoteLuaString(keyname, sizeof(keyname)-1, SDL_GetKeyName(key));
1165 /* If SDL can't describe the key, store it as an integer */
1166 if (type != KEYBIND_KEYBOARD || strcmp(keyname, "\"unknown key\"") == 0)
1167 nsnprintf(keyname, sizeof(keyname)-1, "%d", key);
1168
1169 /* Write out a simple Lua table containing the keybind info */
1170 pos += nsnprintf(&buf[pos], sizeof(buf)-pos, "%s = { type = \"%s\", mod = \"%s\", key = %s }\n",
1171 keybind_info[i][0], typename, modname, keyname);
1172 }
1173 conf_saveEmptyLine();
1174
1175 /* Footer. */
1176 conf_saveComment(GENERATED_END_COMMENT);
1177
1178 if (old != NULL) {
1179 if (oldfooter != NULL) {
1180 /* oldfooter and oldsize now reference the old content past the footer */
1181 oldsize = MIN((size_t)oldsize, sizeof(buf)-pos);
1182 memcpy(&buf[pos], oldfooter, oldsize);
1183 pos += oldsize;
1184 }
1185 free(old);
1186 }
1187
1188 if (nfile_writeFile(buf, pos, file) < 0) {
1189 WARN("Failed to write configuration! You'll most likely have to restore it by copying your backup configuration over your current configuration.");
1190 return -1;
1191 }
1192
1193 return 0;
1194 }
1195
1196