Use symbols for score ranks
[neverball] / share / config.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <ctype.h>
20
21 #include "config.h"
22 #include "common.h"
23 #include "fs.h"
24
25 /*---------------------------------------------------------------------------*/
26
27 /* Integer options. */
28
29 int CONFIG_FULLSCREEN;
30 int CONFIG_WIDTH;
31 int CONFIG_HEIGHT;
32 int CONFIG_STEREO;
33 int CONFIG_CAMERA;
34 int CONFIG_TEXTURES;
35 int CONFIG_GEOMETRY;
36 int CONFIG_REFLECTION;
37 int CONFIG_MULTISAMPLE;
38 int CONFIG_MIPMAP;
39 int CONFIG_ANISO;
40 int CONFIG_BACKGROUND;
41 int CONFIG_SHADOW;
42 int CONFIG_AUDIO_BUFF;
43 int CONFIG_MOUSE_SENSE;
44 int CONFIG_MOUSE_INVERT;
45 int CONFIG_VSYNC;
46 int CONFIG_MOUSE_CAMERA_1;
47 int CONFIG_MOUSE_CAMERA_2;
48 int CONFIG_MOUSE_CAMERA_3;
49 int CONFIG_MOUSE_CAMERA_TOGGLE;
50 int CONFIG_MOUSE_CAMERA_L;
51 int CONFIG_MOUSE_CAMERA_R;
52 int CONFIG_NICE;
53 int CONFIG_FPS;
54 int CONFIG_SOUND_VOLUME;
55 int CONFIG_MUSIC_VOLUME;
56 int CONFIG_JOYSTICK;
57 int CONFIG_JOYSTICK_DEVICE;
58 int CONFIG_JOYSTICK_AXIS_X;
59 int CONFIG_JOYSTICK_AXIS_Y;
60 int CONFIG_JOYSTICK_AXIS_U;
61 int CONFIG_JOYSTICK_AXIS_X_INVERT;
62 int CONFIG_JOYSTICK_AXIS_Y_INVERT;
63 int CONFIG_JOYSTICK_AXIS_U_INVERT;
64 int CONFIG_JOYSTICK_BUTTON_A;
65 int CONFIG_JOYSTICK_BUTTON_B;
66 int CONFIG_JOYSTICK_BUTTON_R;
67 int CONFIG_JOYSTICK_BUTTON_L;
68 int CONFIG_JOYSTICK_BUTTON_EXIT;
69 int CONFIG_JOYSTICK_CAMERA_1;
70 int CONFIG_JOYSTICK_CAMERA_2;
71 int CONFIG_JOYSTICK_CAMERA_3;
72 int CONFIG_JOYSTICK_DPAD_L;
73 int CONFIG_JOYSTICK_DPAD_R;
74 int CONFIG_JOYSTICK_DPAD_U;
75 int CONFIG_JOYSTICK_DPAD_D;
76 int CONFIG_JOYSTICK_CAMERA_TOGGLE;
77 int CONFIG_JOYSTICK_ROTATE_FAST;
78 int CONFIG_KEY_CAMERA_1;
79 int CONFIG_KEY_CAMERA_2;
80 int CONFIG_KEY_CAMERA_3;
81 int CONFIG_KEY_CAMERA_TOGGLE;
82 int CONFIG_KEY_CAMERA_R;
83 int CONFIG_KEY_CAMERA_L;
84 int CONFIG_KEY_FORWARD;
85 int CONFIG_KEY_BACKWARD;
86 int CONFIG_KEY_LEFT;
87 int CONFIG_KEY_RIGHT;
88 int CONFIG_KEY_PAUSE;
89 int CONFIG_KEY_RESTART;
90 int CONFIG_KEY_SCORE_NEXT;
91 int CONFIG_KEY_ROTATE_FAST;
92 int CONFIG_VIEW_FOV;
93 int CONFIG_VIEW_DP;
94 int CONFIG_VIEW_DC;
95 int CONFIG_VIEW_DZ;
96 int CONFIG_ROTATE_FAST;
97 int CONFIG_ROTATE_SLOW;
98 int CONFIG_CHEAT;
99 int CONFIG_STATS;
100 int CONFIG_UNIFORM;
101 int CONFIG_SCREENSHOT;
102 int CONFIG_LOCK_GOALS;
103
104 /* String options. */
105
106 int CONFIG_PLAYER;
107 int CONFIG_BALL_FILE;
108 int CONFIG_WIIMOTE_ADDR;
109 int CONFIG_REPLAY_NAME;
110
111 /*---------------------------------------------------------------------------*/
112
113 static struct
114 {
115     int        *sym;
116     const char *name;
117     const int   def;
118     int         cur;
119 } option_d[] = {
120     { &CONFIG_FULLSCREEN,   "fullscreen",   0 },
121     { &CONFIG_WIDTH,        "width",        800 },
122     { &CONFIG_HEIGHT,       "height",       600 },
123     { &CONFIG_STEREO,       "stereo",       0 },
124     { &CONFIG_CAMERA,       "camera",       0 },
125     { &CONFIG_TEXTURES,     "textures",     1 },
126     { &CONFIG_GEOMETRY,     "geometry",     1 },
127     { &CONFIG_REFLECTION,   "reflection",   1 },
128     { &CONFIG_MULTISAMPLE,  "multisample",  0 },
129     { &CONFIG_MIPMAP,       "mipmap",       0 },
130     { &CONFIG_ANISO,        "aniso",        0 },
131     { &CONFIG_BACKGROUND,   "background",   1 },
132     { &CONFIG_SHADOW,       "shadow",       1 },
133     { &CONFIG_AUDIO_BUFF,   "audio_buff",   AUDIO_BUFF_HI },
134     { &CONFIG_MOUSE_SENSE,  "mouse_sense",  300 },
135     { &CONFIG_MOUSE_INVERT, "mouse_invert", 0 },
136     { &CONFIG_VSYNC,        "vsync",        1 },
137
138     { &CONFIG_MOUSE_CAMERA_1,      "mouse_camera_1",      0 },
139     { &CONFIG_MOUSE_CAMERA_2,      "mouse_camera_2",      0 },
140     { &CONFIG_MOUSE_CAMERA_3,      "mouse_camera_3",      0 },
141     { &CONFIG_MOUSE_CAMERA_TOGGLE, "mouse_camera_toggle", SDL_BUTTON_MIDDLE },
142     { &CONFIG_MOUSE_CAMERA_L,      "mouse_camera_l",      SDL_BUTTON_LEFT },
143     { &CONFIG_MOUSE_CAMERA_R,      "mouse_camera_r",      SDL_BUTTON_RIGHT },
144
145     { &CONFIG_NICE,         "nice",         0 },
146     { &CONFIG_FPS,          "fps",          0 },
147     { &CONFIG_SOUND_VOLUME, "sound_volume", 10 },
148     { &CONFIG_MUSIC_VOLUME, "music_volume", 6 },
149
150     { &CONFIG_JOYSTICK,               "joystick",               1 },
151     { &CONFIG_JOYSTICK_DEVICE,        "joystick_device",        0 },
152     { &CONFIG_JOYSTICK_AXIS_X,        "joystick_axis_x",        0 },
153     { &CONFIG_JOYSTICK_AXIS_Y,        "joystick_axis_y",        1 },
154     { &CONFIG_JOYSTICK_AXIS_U,        "joystick_axis_u",        2 },
155     { &CONFIG_JOYSTICK_AXIS_X_INVERT, "joystick_axis_x_invert", 0 },
156     { &CONFIG_JOYSTICK_AXIS_Y_INVERT, "joystick_axis_y_invert", 0 },
157     { &CONFIG_JOYSTICK_AXIS_U_INVERT, "joystick_axis_u_invert", 0 },
158     { &CONFIG_JOYSTICK_BUTTON_A,      "joystick_button_a",      0 },
159     { &CONFIG_JOYSTICK_BUTTON_B,      "joystick_button_b",      1 },
160     { &CONFIG_JOYSTICK_BUTTON_R,      "joystick_button_r",      2 },
161     { &CONFIG_JOYSTICK_BUTTON_L,      "joystick_button_l",      3 },
162     { &CONFIG_JOYSTICK_BUTTON_EXIT,   "joystick_button_exit",   4 },
163     { &CONFIG_JOYSTICK_CAMERA_1,      "joystick_camera_1",      5 },
164     { &CONFIG_JOYSTICK_CAMERA_2,      "joystick_camera_2",      6 },
165     { &CONFIG_JOYSTICK_CAMERA_3,      "joystick_camera_3",      7 },
166     { &CONFIG_JOYSTICK_DPAD_L,        "joystick_dpad_l",        8 },
167     { &CONFIG_JOYSTICK_DPAD_R,        "joystick_dpad_r",        9 },
168     { &CONFIG_JOYSTICK_DPAD_U,        "joystick_dpad_u",        10 },
169     { &CONFIG_JOYSTICK_DPAD_D,        "joystick_dpad_d",        11 },
170     { &CONFIG_JOYSTICK_CAMERA_TOGGLE, "joystick_camera_toggle", 12 },
171     { &CONFIG_JOYSTICK_ROTATE_FAST,   "joystick_rotate_fast",   13 },
172
173     { &CONFIG_KEY_CAMERA_1,      "key_camera_1",      SDLK_F1 },
174     { &CONFIG_KEY_CAMERA_2,      "key_camera_2",      SDLK_F2 },
175     { &CONFIG_KEY_CAMERA_3,      "key_camera_3",      SDLK_F3 },
176     { &CONFIG_KEY_CAMERA_TOGGLE, "key_camera_toggle", SDLK_e },
177     { &CONFIG_KEY_CAMERA_R,      "key_camera_r",      SDLK_d },
178     { &CONFIG_KEY_CAMERA_L,      "key_camera_l",      SDLK_s },
179     { &CONFIG_KEY_FORWARD,       "key_forward",       SDLK_UP },
180     { &CONFIG_KEY_BACKWARD,      "key_backward",      SDLK_DOWN },
181     { &CONFIG_KEY_LEFT,          "key_left",          SDLK_LEFT },
182     { &CONFIG_KEY_RIGHT,         "key_right",         SDLK_RIGHT },
183     { &CONFIG_KEY_PAUSE,         "key_pause",         SDLK_ESCAPE },
184     { &CONFIG_KEY_RESTART,       "key_restart",       SDLK_r },
185     { &CONFIG_KEY_SCORE_NEXT,    "key_score_next",    SDLK_TAB },
186     { &CONFIG_KEY_ROTATE_FAST,   "key_rotate_fast",   SDLK_LSHIFT },
187
188     { &CONFIG_VIEW_FOV,    "view_fov",    50 },
189     { &CONFIG_VIEW_DP,     "view_dp",     75 },
190     { &CONFIG_VIEW_DC,     "view_dc",     25 },
191     { &CONFIG_VIEW_DZ,     "view_dz",     200 },
192     { &CONFIG_ROTATE_FAST, "rotate_fast", 300 },
193     { &CONFIG_ROTATE_SLOW, "rotate_slow", 150 },
194     { &CONFIG_CHEAT,       "cheat",       0 },
195     { &CONFIG_STATS,       "stats",       0 },
196     { &CONFIG_UNIFORM,     "uniform",     0 },
197     { &CONFIG_SCREENSHOT,  "screenshot",  0 },
198     { &CONFIG_LOCK_GOALS,  "lock_goals",  0 }
199 };
200
201 static struct
202 {
203     int        *sym;
204     const char *name;
205     const char *def;
206     char       *cur;
207 } option_s[] = {
208     { &CONFIG_PLAYER,       "player",       "" },
209     { &CONFIG_BALL_FILE,    "ball_file",    "ball/basic-ball/basic-ball" },
210     { &CONFIG_WIIMOTE_ADDR, "wiimote_addr", "" },
211     { &CONFIG_REPLAY_NAME,  "replay_name",  "%s-%l" }
212 };
213
214 static int dirty = 0;
215
216 /*---------------------------------------------------------------------------*/
217
218 static void config_key(const char *s, int i)
219 {
220     int c;
221
222     config_set_d(i, option_d[i].def);
223
224     for (c = 0; c < SDLK_LAST; c++)
225         if (strcmp(s, SDL_GetKeyName((SDLKey) c)) == 0)
226         {
227             config_set_d(i, c);
228             break;
229         }
230 }
231
232 /*---------------------------------------------------------------------------*/
233
234 static void config_mouse(const char *s, int i)
235 {
236     if      (strcmp(s, "none") == 0)
237         config_set_d(i, 0);
238     else if (strcmp(s, "left") == 0)
239         config_set_d(i, SDL_BUTTON_LEFT);
240     else if (strcmp(s, "right") == 0)
241         config_set_d(i, SDL_BUTTON_RIGHT);
242     else if (strcmp(s, "wheelup") == 0)
243         config_set_d(i, SDL_BUTTON_WHEELUP);
244     else if (strcmp(s, "middle") == 0)
245         config_set_d(i, SDL_BUTTON_MIDDLE);
246     else if (strcmp(s, "wheeldown") == 0)
247         config_set_d(i, SDL_BUTTON_WHEELDOWN);
248     else
249         config_set_d(i, atoi(s));
250 }
251
252 static const char *config_mouse_name(int b)
253 {
254     static char buff[sizeof ("256")];
255
256     sprintf(buff, "%d", b);
257
258     switch (b)
259     {
260     case 0:                    return "none";      break;
261     case SDL_BUTTON_LEFT:      return "left";      break;
262     case SDL_BUTTON_RIGHT:     return "right";     break;
263     case SDL_BUTTON_WHEELUP:   return "wheelup";   break;
264     case SDL_BUTTON_MIDDLE:    return "middle";    break;
265     case SDL_BUTTON_WHEELDOWN: return "wheeldown"; break;
266     default:                   return buff;        break;
267     }
268 }
269
270 /*---------------------------------------------------------------------------*/
271
272 void config_init(void)
273 {
274     int i;
275
276     /*
277      * Store index of each option in its associated config symbol and
278      * initialise current values with defaults.
279      */
280
281     for (i = 0; i < ARRAYSIZE(option_d); i++)
282     {
283         *option_d[i].sym = i;
284         config_set_d(i, option_d[i].def);
285     }
286
287     for (i = 0; i < ARRAYSIZE(option_s); i++)
288     {
289         *option_s[i].sym = i;
290         config_set_s(i, option_s[i].def);
291     }
292 }
293
294 /*
295  * Scan an option string and store pointers to the start of key and
296  * value at the passed-in locations.  No memory is allocated to store
297  * the strings; instead, the option string is modified in-place as
298  * needed.  Return 1 on success, 0 on error.
299  */
300 static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
301 {
302     if (line)
303     {
304         int ks, ke, vs;
305
306         ks = -1;
307         ke = -1;
308         vs = -1;
309
310         sscanf(line, " %n%*s%n %n", &ks, &ke, &vs);
311
312         if (ks < 0 || ke < 0 || vs < 0)
313             return 0;
314
315         if (vs - ke < 1)
316             return 0;
317
318         line[ke] = 0;
319
320         *dst_key = line + ks;
321         *dst_val = line + vs;
322
323         return 1;
324     }
325
326     return 0;
327 }
328
329 void config_load(void)
330 {
331     fs_file fh;
332
333     if ((fh = fs_open(USER_CONFIG_FILE, "r")))
334     {
335         char *line, *key, *val;
336
337         while (read_line(&line, fh))
338         {
339             if (scan_key_and_value(&key, &val, line))
340             {
341                 int i;
342
343                 /* Look up an integer option by that name. */
344
345                 for (i = 0; i < ARRAYSIZE(option_d); i++)
346                 {
347                     if (strcmp(key, option_d[i].name) == 0)
348                     {
349                         /* Translate some strings to integers. */
350
351                         if (i == CONFIG_MOUSE_CAMERA_1      ||
352                             i == CONFIG_MOUSE_CAMERA_2      ||
353                             i == CONFIG_MOUSE_CAMERA_3      ||
354                             i == CONFIG_MOUSE_CAMERA_TOGGLE ||
355                             i == CONFIG_MOUSE_CAMERA_L      ||
356                             i == CONFIG_MOUSE_CAMERA_R)
357                         {
358                             config_mouse(val, i);
359                         }
360                         else if (i == CONFIG_KEY_FORWARD       ||
361                                  i == CONFIG_KEY_BACKWARD      ||
362                                  i == CONFIG_KEY_LEFT          ||
363                                  i == CONFIG_KEY_RIGHT         ||
364                                  i == CONFIG_KEY_CAMERA_1      ||
365                                  i == CONFIG_KEY_CAMERA_2      ||
366                                  i == CONFIG_KEY_CAMERA_3      ||
367                                  i == CONFIG_KEY_CAMERA_TOGGLE ||
368                                  i == CONFIG_KEY_CAMERA_R      ||
369                                  i == CONFIG_KEY_CAMERA_L      ||
370                                  i == CONFIG_KEY_PAUSE         ||
371                                  i == CONFIG_KEY_RESTART       ||
372                                  i == CONFIG_KEY_SCORE_NEXT    ||
373                                  i == CONFIG_KEY_ROTATE_FAST)
374                         {
375                             config_key(val, i);
376                         }
377                         else
378                             config_set_d(i, atoi(val));
379
380                         /* Stop looking. */
381
382                         break;
383                     }
384                 }
385
386                 /* Look up a string option by that name.*/
387
388                 for (i = 0; i < ARRAYSIZE(option_s); i++)
389                 {
390                     if (strcmp(key, option_s[i].name) == 0)
391                     {
392                         config_set_s(i, val);
393                         break;
394                     }
395                 }
396             }
397             free(line);
398         }
399         fs_close(fh);
400
401         dirty = 0;
402     }
403 }
404
405 void config_save(void)
406 {
407     fs_file fh;
408
409     if (dirty && (fh = fs_open(USER_CONFIG_FILE, "w")))
410     {
411         int i;
412
413         /* Write out integer options. */
414
415         for (i = 0; i < ARRAYSIZE(option_d); i++)
416         {
417             const char *s = NULL;
418
419             /* Translate some integers to strings. */
420
421             if (i == CONFIG_MOUSE_CAMERA_1      ||
422                 i == CONFIG_MOUSE_CAMERA_2      ||
423                 i == CONFIG_MOUSE_CAMERA_3      ||
424                 i == CONFIG_MOUSE_CAMERA_TOGGLE ||
425                 i == CONFIG_MOUSE_CAMERA_L      ||
426                 i == CONFIG_MOUSE_CAMERA_R)
427             {
428                 s = config_mouse_name(option_d[i].cur);
429             }
430             else if (i == CONFIG_KEY_FORWARD       ||
431                      i == CONFIG_KEY_BACKWARD      ||
432                      i == CONFIG_KEY_LEFT          ||
433                      i == CONFIG_KEY_RIGHT         ||
434                      i == CONFIG_KEY_CAMERA_1      ||
435                      i == CONFIG_KEY_CAMERA_2      ||
436                      i == CONFIG_KEY_CAMERA_3      ||
437                      i == CONFIG_KEY_CAMERA_TOGGLE ||
438                      i == CONFIG_KEY_CAMERA_R      ||
439                      i == CONFIG_KEY_CAMERA_L      ||
440                      i == CONFIG_KEY_PAUSE         ||
441                      i == CONFIG_KEY_RESTART       ||
442                      i == CONFIG_KEY_SCORE_NEXT    ||
443                      i == CONFIG_KEY_ROTATE_FAST)
444             {
445                 s = SDL_GetKeyName((SDLKey) option_d[i].cur);
446             }
447             else if (i == CONFIG_CHEAT)
448             {
449                 if (!config_cheat())
450                     continue;
451             }
452
453             if (s)
454                 fs_printf(fh, "%-25s %s\n", option_d[i].name, s);
455             else
456                 fs_printf(fh, "%-25s %d\n", option_d[i].name, option_d[i].cur);
457         }
458
459         /* Write out string options. */
460
461         for (i = 0; i < ARRAYSIZE(option_s); i++)
462         {
463             if (option_s[i].cur && *option_s[i].cur)
464                 fs_printf(fh, "%-25s %s\n", option_s[i].name, option_s[i].cur);
465         }
466
467         fs_close(fh);
468     }
469
470     dirty = 0;
471 }
472
473 /*---------------------------------------------------------------------------*/
474
475 void config_set_d(int i, int d)
476 {
477     option_d[i].cur = d;
478     dirty = 1;
479 }
480
481 void config_tgl_d(int i)
482 {
483     option_d[i].cur = (option_d[i].cur ? 0 : 1);
484     dirty = 1;
485 }
486
487 int config_tst_d(int i, int d)
488 {
489     return (option_d[i].cur == d) ? 1 : 0;
490 }
491
492 int config_get_d(int i)
493 {
494     return option_d[i].cur;
495 }
496
497 /*---------------------------------------------------------------------------*/
498
499 void config_set_s(int i, const char *src)
500 {
501     if (option_s[i].cur)
502         free(option_s[i].cur);
503
504     option_s[i].cur = strdup(src);
505
506     dirty = 1;
507 }
508
509 const char *config_get_s(int i)
510 {
511     return option_s[i].cur;
512 }
513
514 /*---------------------------------------------------------------------------*/
515
516 int config_cheat(void)
517 {
518     return config_get_d(CONFIG_CHEAT);
519 }
520
521 void config_set_cheat(void)
522 {
523     config_set_d(CONFIG_CHEAT, 1);
524 }
525
526 void config_clr_cheat(void)
527 {
528     config_set_d(CONFIG_CHEAT, 0);
529 }
530
531 /*---------------------------------------------------------------------------*/
532
533 int config_screenshot(void)
534 {
535     return ++option_d[CONFIG_SCREENSHOT].cur;
536 }
537
538 /*---------------------------------------------------------------------------*/