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