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