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