0dddce3c6a5e62587f583e00808cd42095d32ee4
[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 a NUL-terminated string LINE according to the format
296  * '^<space>?<key><space><value>$' and store pointers to the start of key and
297  * value at DST_KEY and DST_VAL, respectively.  No memory is allocated to store
298  * the strings;  instead, the memory pointed to by LINE modified in-place as
299  * needed.
300  *
301  * Return 1 if LINE matches the format, return 0 otherwise.
302  */
303
304 static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
305 {
306     if (line)
307     {
308         char *key, *val, *space;
309
310         for (key = line; *key && isspace(*key); key++);
311
312         if (*key)
313         {
314             if (dst_key)
315                 *dst_key = key;
316         }
317         else
318             return 0;
319
320         for (space = key; *space && !isspace(*space); space++);
321
322         if (*space)
323         {
324             /* NUL-terminate the key, if necessary. */
325
326             if (dst_key)
327             {
328                 *space = '\0';
329                 space++;
330             }
331         }
332         else
333             return 0;
334
335         for (val = space; *val && isspace(*val); val++);
336
337         if (*val)
338         {
339             if (dst_val)
340                 *dst_val = val;
341         }
342         else
343             return 0;
344
345         return 1;
346     }
347
348     return 0;
349 }
350
351 void config_load(void)
352 {
353     fs_file fh;
354
355     if ((fh = fs_open(USER_CONFIG_FILE, "r")))
356     {
357         char *line, *key, *val;
358
359         while (read_line(&line, fh))
360         {
361             if (scan_key_and_value(&key, &val, line))
362             {
363                 int i;
364
365                 /* Look up an integer option by that name. */
366
367                 for (i = 0; i < ARRAYSIZE(option_d); i++)
368                 {
369                     if (strcmp(key, option_d[i].name) == 0)
370                     {
371                         /* Translate some strings to integers. */
372
373                         if (i == CONFIG_MOUSE_CAMERA_1      ||
374                             i == CONFIG_MOUSE_CAMERA_2      ||
375                             i == CONFIG_MOUSE_CAMERA_3      ||
376                             i == CONFIG_MOUSE_CAMERA_TOGGLE ||
377                             i == CONFIG_MOUSE_CAMERA_L      ||
378                             i == CONFIG_MOUSE_CAMERA_R)
379                         {
380                             config_mouse(val, i);
381                         }
382                         else if (i == CONFIG_KEY_FORWARD       ||
383                                  i == CONFIG_KEY_BACKWARD      ||
384                                  i == CONFIG_KEY_LEFT          ||
385                                  i == CONFIG_KEY_RIGHT         ||
386                                  i == CONFIG_KEY_CAMERA_1      ||
387                                  i == CONFIG_KEY_CAMERA_2      ||
388                                  i == CONFIG_KEY_CAMERA_3      ||
389                                  i == CONFIG_KEY_CAMERA_TOGGLE ||
390                                  i == CONFIG_KEY_CAMERA_R      ||
391                                  i == CONFIG_KEY_CAMERA_L      ||
392                                  i == CONFIG_KEY_PAUSE         ||
393                                  i == CONFIG_KEY_RESTART       ||
394                                  i == CONFIG_KEY_SCORE_NEXT    ||
395                                  i == CONFIG_KEY_ROTATE_FAST)
396                         {
397                             config_key(val, i);
398                         }
399                         else
400                             config_set_d(i, atoi(val));
401
402                         /* Stop looking. */
403
404                         break;
405                     }
406                 }
407
408                 /* Look up a string option by that name.*/
409
410                 for (i = 0; i < ARRAYSIZE(option_s); i++)
411                 {
412                     if (strcmp(key, option_s[i].name) == 0)
413                     {
414                         config_set_s(i, val);
415                         break;
416                     }
417                 }
418             }
419             free(line);
420         }
421         fs_close(fh);
422
423         dirty = 0;
424     }
425 }
426
427 void config_save(void)
428 {
429     fs_file fh;
430
431     if (dirty && (fh = fs_open(USER_CONFIG_FILE, "w")))
432     {
433         int i;
434
435         /* Write out integer options. */
436
437         for (i = 0; i < ARRAYSIZE(option_d); i++)
438         {
439             const char *s = NULL;
440
441             /* Translate some integers to strings. */
442
443             if (i == CONFIG_MOUSE_CAMERA_1      ||
444                 i == CONFIG_MOUSE_CAMERA_2      ||
445                 i == CONFIG_MOUSE_CAMERA_3      ||
446                 i == CONFIG_MOUSE_CAMERA_TOGGLE ||
447                 i == CONFIG_MOUSE_CAMERA_L      ||
448                 i == CONFIG_MOUSE_CAMERA_R)
449             {
450                 s = config_mouse_name(option_d[i].cur);
451             }
452             else if (i == CONFIG_KEY_FORWARD       ||
453                      i == CONFIG_KEY_BACKWARD      ||
454                      i == CONFIG_KEY_LEFT          ||
455                      i == CONFIG_KEY_RIGHT         ||
456                      i == CONFIG_KEY_CAMERA_1      ||
457                      i == CONFIG_KEY_CAMERA_2      ||
458                      i == CONFIG_KEY_CAMERA_3      ||
459                      i == CONFIG_KEY_CAMERA_TOGGLE ||
460                      i == CONFIG_KEY_CAMERA_R      ||
461                      i == CONFIG_KEY_CAMERA_L      ||
462                      i == CONFIG_KEY_PAUSE         ||
463                      i == CONFIG_KEY_RESTART       ||
464                      i == CONFIG_KEY_SCORE_NEXT    ||
465                      i == CONFIG_KEY_ROTATE_FAST)
466             {
467                 s = SDL_GetKeyName((SDLKey) option_d[i].cur);
468             }
469             else if (i == CONFIG_CHEAT)
470             {
471                 if (!config_cheat())
472                     continue;
473             }
474
475             if (s)
476                 fs_printf(fh, "%-25s %s\n", option_d[i].name, s);
477             else
478                 fs_printf(fh, "%-25s %d\n", option_d[i].name, option_d[i].cur);
479         }
480
481         /* Write out string options. */
482
483         for (i = 0; i < ARRAYSIZE(option_s); i++)
484         {
485             if (option_s[i].cur && *option_s[i].cur)
486                 fs_printf(fh, "%-25s %s\n", option_s[i].name, option_s[i].cur);
487         }
488
489         fs_close(fh);
490     }
491
492     dirty = 0;
493 }
494
495 /*---------------------------------------------------------------------------*/
496
497 void config_set_d(int i, int d)
498 {
499     option_d[i].cur = d;
500     dirty = 1;
501 }
502
503 void config_tgl_d(int i)
504 {
505     option_d[i].cur = (option_d[i].cur ? 0 : 1);
506     dirty = 1;
507 }
508
509 int config_tst_d(int i, int d)
510 {
511     return (option_d[i].cur == d) ? 1 : 0;
512 }
513
514 int config_get_d(int i)
515 {
516     return option_d[i].cur;
517 }
518
519 /*---------------------------------------------------------------------------*/
520
521 void config_set_s(int i, const char *src)
522 {
523     if (option_s[i].cur)
524         free(option_s[i].cur);
525
526     option_s[i].cur = strdup(src);
527
528     dirty = 1;
529 }
530
531 const char *config_get_s(int i)
532 {
533     return option_s[i].cur;
534 }
535
536 /*---------------------------------------------------------------------------*/
537
538 int config_cheat(void)
539 {
540     return config_get_d(CONFIG_CHEAT);
541 }
542
543 void config_set_cheat(void)
544 {
545     config_set_d(CONFIG_CHEAT, 1);
546 }
547
548 void config_clr_cheat(void)
549 {
550     config_set_d(CONFIG_CHEAT, 0);
551 }
552
553 /*---------------------------------------------------------------------------*/
554
555 int config_screenshot(void)
556 {
557     return ++option_d[CONFIG_SCREENSHOT].cur;
558 }
559
560 /*---------------------------------------------------------------------------*/