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 
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 
22 #include "config.h"
23 #include "common.h"
24 #include "fs.h"
25 
26 /*---------------------------------------------------------------------------*/
27 
28 /* Integer options. */
29 
30 int CONFIG_FULLSCREEN;
31 int CONFIG_DISPLAY;
32 int CONFIG_WIDTH;
33 int CONFIG_HEIGHT;
34 int CONFIG_STEREO;
35 int CONFIG_CAMERA;
36 int CONFIG_TEXTURES;
37 int CONFIG_REFLECTION;
38 int CONFIG_MULTISAMPLE;
39 int CONFIG_MIPMAP;
40 int CONFIG_ANISO;
41 int CONFIG_BACKGROUND;
42 int CONFIG_SHADOW;
43 int CONFIG_AUDIO_BUFF;
44 int CONFIG_MOUSE_SENSE;
45 int CONFIG_MOUSE_RESPONSE;
46 int CONFIG_MOUSE_INVERT;
47 int CONFIG_VSYNC;
48 int CONFIG_HMD;
49 int CONFIG_HIGHDPI;
50 int CONFIG_MOUSE_CAMERA_1;
51 int CONFIG_MOUSE_CAMERA_2;
52 int CONFIG_MOUSE_CAMERA_3;
53 int CONFIG_MOUSE_CAMERA_TOGGLE;
54 int CONFIG_MOUSE_CAMERA_L;
55 int CONFIG_MOUSE_CAMERA_R;
56 int CONFIG_NICE;
57 int CONFIG_FPS;
58 int CONFIG_SOUND_VOLUME;
59 int CONFIG_MUSIC_VOLUME;
60 int CONFIG_JOYSTICK;
61 int CONFIG_JOYSTICK_DEVICE;
62 int CONFIG_JOYSTICK_RESPONSE;
63 int CONFIG_JOYSTICK_AXIS_X0;
64 int CONFIG_JOYSTICK_AXIS_Y0;
65 int CONFIG_JOYSTICK_AXIS_X1;
66 int CONFIG_JOYSTICK_AXIS_Y1;
67 int CONFIG_JOYSTICK_AXIS_X0_INVERT;
68 int CONFIG_JOYSTICK_AXIS_Y0_INVERT;
69 int CONFIG_JOYSTICK_AXIS_X1_INVERT;
70 int CONFIG_JOYSTICK_AXIS_Y1_INVERT;
71 
72 int CONFIG_JOYSTICK_BUTTON_A;
73 int CONFIG_JOYSTICK_BUTTON_B;
74 int CONFIG_JOYSTICK_BUTTON_X;
75 int CONFIG_JOYSTICK_BUTTON_Y;
76 int CONFIG_JOYSTICK_BUTTON_L1;
77 int CONFIG_JOYSTICK_BUTTON_R1;
78 int CONFIG_JOYSTICK_BUTTON_L2;
79 int CONFIG_JOYSTICK_BUTTON_R2;
80 int CONFIG_JOYSTICK_BUTTON_START;
81 int CONFIG_JOYSTICK_DPAD_L;
82 int CONFIG_JOYSTICK_DPAD_R;
83 int CONFIG_JOYSTICK_DPAD_U;
84 int CONFIG_JOYSTICK_DPAD_D;
85 
86 int CONFIG_KEY_CAMERA_1;
87 int CONFIG_KEY_CAMERA_2;
88 int CONFIG_KEY_CAMERA_3;
89 int CONFIG_KEY_CAMERA_TOGGLE;
90 int CONFIG_KEY_CAMERA_R;
91 int CONFIG_KEY_CAMERA_L;
92 int CONFIG_KEY_FORWARD;
93 int CONFIG_KEY_BACKWARD;
94 int CONFIG_KEY_LEFT;
95 int CONFIG_KEY_RIGHT;
96 int CONFIG_KEY_RESTART;
97 int CONFIG_KEY_SCORE_NEXT;
98 int CONFIG_KEY_ROTATE_FAST;
99 int CONFIG_VIEW_FOV;
100 int CONFIG_VIEW_DP;
101 int CONFIG_VIEW_DC;
102 int CONFIG_VIEW_DZ;
103 int CONFIG_ROTATE_FAST;
104 int CONFIG_ROTATE_SLOW;
105 int CONFIG_CHEAT;
106 int CONFIG_STATS;
107 int CONFIG_SCREENSHOT;
108 int CONFIG_LOCK_GOALS;
109 int CONFIG_CAMERA_1_SPEED;
110 int CONFIG_CAMERA_2_SPEED;
111 int CONFIG_CAMERA_3_SPEED;
112 
113 
114 /* String options. */
115 
116 int CONFIG_PLAYER;
117 int CONFIG_BALL_FILE;
118 int CONFIG_WIIMOTE_ADDR;
119 int CONFIG_REPLAY_NAME;
120 int CONFIG_LANGUAGE;
121 int CONFIG_THEME;
122 
123 /*---------------------------------------------------------------------------*/
124 
125 static struct
126 {
127     int        *sym;
128     const char *name;
129     const int   def;
130     int         cur;
131 } option_d[] = {
132     { &CONFIG_FULLSCREEN,   "fullscreen",   0 },
133     { &CONFIG_DISPLAY,      "display",      0 },
134     { &CONFIG_WIDTH,        "width",        800 },
135     { &CONFIG_HEIGHT,       "height",       600 },
136     { &CONFIG_STEREO,       "stereo",       0 },
137     { &CONFIG_CAMERA,       "camera",       0 },
138     { &CONFIG_TEXTURES,     "textures",     1 },
139     { &CONFIG_REFLECTION,   "reflection",   1 },
140     { &CONFIG_MULTISAMPLE,  "multisample",  0 },
141     { &CONFIG_MIPMAP,       "mipmap",       1 },
142     { &CONFIG_ANISO,        "aniso",        0 },
143     { &CONFIG_BACKGROUND,   "background",   1 },
144     { &CONFIG_SHADOW,       "shadow",       1 },
145     { &CONFIG_AUDIO_BUFF,   "audio_buff",   AUDIO_BUFF_HI },
146     { &CONFIG_MOUSE_SENSE,  "mouse_sense",  300 },
147     { &CONFIG_MOUSE_RESPONSE, "mouse_response", 50 },
148     { &CONFIG_MOUSE_INVERT, "mouse_invert", 0 },
149     { &CONFIG_VSYNC,        "vsync",        1 },
150     { &CONFIG_HMD,          "hmd",          0 },
151     { &CONFIG_HIGHDPI,      "highdpi",      1 },
152 
153     { &CONFIG_MOUSE_CAMERA_1,      "mouse_camera_1",      0 },
154     { &CONFIG_MOUSE_CAMERA_2,      "mouse_camera_2",      0 },
155     { &CONFIG_MOUSE_CAMERA_3,      "mouse_camera_3",      0 },
156     { &CONFIG_MOUSE_CAMERA_TOGGLE, "mouse_camera_toggle", SDL_BUTTON_MIDDLE },
157     { &CONFIG_MOUSE_CAMERA_L,      "mouse_camera_l",      SDL_BUTTON_LEFT },
158     { &CONFIG_MOUSE_CAMERA_R,      "mouse_camera_r",      SDL_BUTTON_RIGHT },
159 
160     { &CONFIG_NICE,         "nice",         0 },
161     { &CONFIG_FPS,          "fps",          0 },
162     { &CONFIG_SOUND_VOLUME, "sound_volume", 10 },
163     { &CONFIG_MUSIC_VOLUME, "music_volume", 6 },
164 
165     { &CONFIG_JOYSTICK,                "joystick",                1 },
166     { &CONFIG_JOYSTICK_DEVICE,         "joystick_device",         0 },
167     { &CONFIG_JOYSTICK_RESPONSE,       "joystick_response",       250 },
168     { &CONFIG_JOYSTICK_AXIS_X0,        "joystick_axis_x0",        0 },
169     { &CONFIG_JOYSTICK_AXIS_Y0,        "joystick_axis_y0",        1 },
170     { &CONFIG_JOYSTICK_AXIS_X1,        "joystick_axis_x1",        2 },
171     { &CONFIG_JOYSTICK_AXIS_Y1,        "joystick_axis_y1",        3 },
172     { &CONFIG_JOYSTICK_AXIS_X0_INVERT, "joystick_axis_x0_invert", 0 },
173     { &CONFIG_JOYSTICK_AXIS_Y0_INVERT, "joystick_axis_y0_invert", 0 },
174     { &CONFIG_JOYSTICK_AXIS_X1_INVERT, "joystick_axis_x1_invert", 0 },
175     { &CONFIG_JOYSTICK_AXIS_Y1_INVERT, "joystick_axis_y1_invert", 0 },
176 
177     { &CONFIG_JOYSTICK_BUTTON_A,      "joystick_button_a",      0 },
178     { &CONFIG_JOYSTICK_BUTTON_B,      "joystick_button_b",      1 },
179     { &CONFIG_JOYSTICK_BUTTON_X,      "joystick_button_x",      2 },
180     { &CONFIG_JOYSTICK_BUTTON_Y,      "joystick_button_y",      3 },
181     { &CONFIG_JOYSTICK_BUTTON_L1,     "joystick_button_l1",     4 },
182     { &CONFIG_JOYSTICK_BUTTON_R1,     "joystick_button_r1",     5 },
183     { &CONFIG_JOYSTICK_BUTTON_L2,     "joystick_button_l2",     6 },
184     { &CONFIG_JOYSTICK_BUTTON_R2,     "joystick_button_r2",     7 },
185     { &CONFIG_JOYSTICK_BUTTON_START,  "joystick_button_start",  8 },
186     { &CONFIG_JOYSTICK_DPAD_L,        "joystick_dpad_l",       -1 },
187     { &CONFIG_JOYSTICK_DPAD_R,        "joystick_dpad_r",       -1 },
188     { &CONFIG_JOYSTICK_DPAD_U,        "joystick_dpad_u",       -1 },
189     { &CONFIG_JOYSTICK_DPAD_D,        "joystick_dpad_d",       -1 },
190 
191     { &CONFIG_KEY_CAMERA_1,      "key_camera_1",      SDLK_1 },
192     { &CONFIG_KEY_CAMERA_2,      "key_camera_2",      SDLK_2 },
193     { &CONFIG_KEY_CAMERA_3,      "key_camera_3",      SDLK_3 },
194     { &CONFIG_KEY_CAMERA_TOGGLE, "key_camera_toggle", SDLK_e },
195     { &CONFIG_KEY_CAMERA_R,      "key_camera_r",      SDLK_d },
196     { &CONFIG_KEY_CAMERA_L,      "key_camera_l",      SDLK_s },
197     { &CONFIG_KEY_FORWARD,       "key_forward",       SDLK_UP },
198     { &CONFIG_KEY_BACKWARD,      "key_backward",      SDLK_DOWN },
199     { &CONFIG_KEY_LEFT,          "key_left",          SDLK_LEFT },
200     { &CONFIG_KEY_RIGHT,         "key_right",         SDLK_RIGHT },
201     { &CONFIG_KEY_RESTART,       "key_restart",       SDLK_r },
202     { &CONFIG_KEY_SCORE_NEXT,    "key_score_next",    SDLK_TAB },
203     { &CONFIG_KEY_ROTATE_FAST,   "key_rotate_fast",   SDLK_LSHIFT },
204 
205     { &CONFIG_VIEW_FOV,    "view_fov",    50 },
206     { &CONFIG_VIEW_DP,     "view_dp",     75 },
207     { &CONFIG_VIEW_DC,     "view_dc",     25 },
208     { &CONFIG_VIEW_DZ,     "view_dz",     200 },
209     { &CONFIG_ROTATE_FAST, "rotate_fast", 300 },
210     { &CONFIG_ROTATE_SLOW, "rotate_slow", 150 },
211     { &CONFIG_CHEAT,       "cheat",       0 },
212     { &CONFIG_STATS,       "stats",       0 },
213     { &CONFIG_SCREENSHOT,  "screenshot",  0 },
214     { &CONFIG_LOCK_GOALS,  "lock_goals",  0 },
215 
216     { &CONFIG_CAMERA_1_SPEED, "camera_1_speed", 250 },
217     { &CONFIG_CAMERA_2_SPEED, "camera_2_speed", 0 },
218     { &CONFIG_CAMERA_3_SPEED, "camera_3_speed", -1 },
219 };
220 
221 static struct
222 {
223     int        *sym;
224     const char *name;
225     const char *def;
226     char       *cur;
227 } option_s[] = {
228     { &CONFIG_PLAYER,       "player",       "" },
229     { &CONFIG_BALL_FILE,    "ball_file",    "ball/basic-ball/basic-ball" },
230     { &CONFIG_WIIMOTE_ADDR, "wiimote_addr", "" },
231     { &CONFIG_REPLAY_NAME,  "replay_name",  "%s-%l" },
232     { &CONFIG_LANGUAGE,     "language",     "" },
233     { &CONFIG_THEME,        "theme",        "classic" }
234 };
235 
236 static int dirty = 0;
237 
238 /*---------------------------------------------------------------------------*/
239 
config_key(const char * s,int i)240 static void config_key(const char *s, int i)
241 {
242     SDL_Keycode c = SDL_GetKeyFromName(s);
243 
244     if (c == SDLK_UNKNOWN)
245         config_set_d(i, option_d[i].def);
246     else
247         config_set_d(i, c);
248 }
249 
250 /*---------------------------------------------------------------------------*/
251 
config_mouse(const char * s,int i)252 static void config_mouse(const char *s, int i)
253 {
254     if      (strcmp(s, "none") == 0)
255         config_set_d(i, 0);
256     else if (strcmp(s, "left") == 0)
257         config_set_d(i, SDL_BUTTON_LEFT);
258     else if (strcmp(s, "right") == 0)
259         config_set_d(i, SDL_BUTTON_RIGHT);
260     else if (strcmp(s, "middle") == 0)
261         config_set_d(i, SDL_BUTTON_MIDDLE);
262     else
263         config_set_d(i, atoi(s));
264 }
265 
config_mouse_name(int b)266 static const char *config_mouse_name(int b)
267 {
268     static char buff[sizeof ("256")];
269 
270     sprintf(buff, "%d", b);
271 
272     switch (b)
273     {
274     case 0:                    return "none";      break;
275     case SDL_BUTTON_LEFT:      return "left";      break;
276     case SDL_BUTTON_RIGHT:     return "right";     break;
277     case SDL_BUTTON_MIDDLE:    return "middle";    break;
278     default:                   return buff;        break;
279     }
280 }
281 
282 /*---------------------------------------------------------------------------*/
283 
config_init(void)284 void config_init(void)
285 {
286     int i;
287 
288     /*
289      * Store index of each option in its associated config symbol and
290      * initialise current values with defaults.
291      */
292 
293     for (i = 0; i < ARRAYSIZE(option_d); i++)
294     {
295         *option_d[i].sym = i;
296         config_set_d(i, option_d[i].def);
297     }
298 
299     for (i = 0; i < ARRAYSIZE(option_s); i++)
300     {
301         *option_s[i].sym = i;
302         config_set_s(i, option_s[i].def);
303     }
304 }
305 
306 /*
307  * Scan an option string and store pointers to the start of key and
308  * value at the passed-in locations.  No memory is allocated to store
309  * the strings; instead, the option string is modified in-place as
310  * needed.  Return 1 on success, 0 on error.
311  */
scan_key_and_value(char ** dst_key,char ** dst_val,char * line)312 static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
313 {
314     if (line)
315     {
316         int ks, ke, vs;
317 
318         ks = -1;
319         ke = -1;
320         vs = -1;
321 
322         sscanf(line, " %n%*s%n %n", &ks, &ke, &vs);
323 
324         if (ks < 0 || ke < 0 || vs < 0)
325             return 0;
326 
327         if (vs - ke < 1)
328             return 0;
329 
330         line[ke] = 0;
331 
332         *dst_key = line + ks;
333         *dst_val = line + vs;
334 
335         return 1;
336     }
337 
338     return 0;
339 }
340 
config_load(void)341 void config_load(void)
342 {
343     fs_file fh;
344 
345     SDL_assert(SDL_WasInit(SDL_INIT_VIDEO));
346 
347     if ((fh = fs_open(USER_CONFIG_FILE, "r")))
348     {
349         char *line, *key, *val;
350 
351         while (read_line(&line, fh))
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_RESTART       ||
385                                  i == CONFIG_KEY_SCORE_NEXT    ||
386                                  i == CONFIG_KEY_ROTATE_FAST)
387                         {
388                             config_key(val, i);
389                         }
390                         else
391                             config_set_d(i, atoi(val));
392 
393                         /* Stop looking. */
394 
395                         break;
396                     }
397                 }
398 
399                 /* Look up a string option by that name.*/
400 
401                 for (i = 0; i < ARRAYSIZE(option_s); i++)
402                 {
403                     if (strcmp(key, option_s[i].name) == 0)
404                     {
405                         config_set_s(i, val);
406                         break;
407                     }
408                 }
409             }
410             free(line);
411         }
412         fs_close(fh);
413 
414         dirty = 0;
415     }
416 }
417 
config_save(void)418 void config_save(void)
419 {
420     fs_file fh;
421 
422     SDL_assert(SDL_WasInit(SDL_INIT_VIDEO));
423 
424     if (dirty && (fh = fs_open(USER_CONFIG_FILE, "w")))
425     {
426         int i;
427 
428         /* Write out integer options. */
429 
430         for (i = 0; i < ARRAYSIZE(option_d); i++)
431         {
432             const char *s = NULL;
433 
434             /* Translate some integers to strings. */
435 
436             if (i == CONFIG_MOUSE_CAMERA_1      ||
437                 i == CONFIG_MOUSE_CAMERA_2      ||
438                 i == CONFIG_MOUSE_CAMERA_3      ||
439                 i == CONFIG_MOUSE_CAMERA_TOGGLE ||
440                 i == CONFIG_MOUSE_CAMERA_L      ||
441                 i == CONFIG_MOUSE_CAMERA_R)
442             {
443                 s = config_mouse_name(option_d[i].cur);
444             }
445             else if (i == CONFIG_KEY_FORWARD       ||
446                      i == CONFIG_KEY_BACKWARD      ||
447                      i == CONFIG_KEY_LEFT          ||
448                      i == CONFIG_KEY_RIGHT         ||
449                      i == CONFIG_KEY_CAMERA_1      ||
450                      i == CONFIG_KEY_CAMERA_2      ||
451                      i == CONFIG_KEY_CAMERA_3      ||
452                      i == CONFIG_KEY_CAMERA_TOGGLE ||
453                      i == CONFIG_KEY_CAMERA_R      ||
454                      i == CONFIG_KEY_CAMERA_L      ||
455                      i == CONFIG_KEY_RESTART       ||
456                      i == CONFIG_KEY_SCORE_NEXT    ||
457                      i == CONFIG_KEY_ROTATE_FAST)
458             {
459                 s = SDL_GetKeyName((SDL_Keycode) option_d[i].cur);
460             }
461             else if (i == CONFIG_CHEAT)
462             {
463                 if (!config_cheat())
464                     continue;
465             }
466 
467             if (s)
468                 fs_printf(fh, "%-25s %s\n", option_d[i].name, s);
469             else
470                 fs_printf(fh, "%-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             fs_printf(fh, "%-25s %s\n", option_s[i].name, option_s[i].cur);
477 
478         fs_close(fh);
479     }
480 
481     dirty = 0;
482 }
483 
484 /*---------------------------------------------------------------------------*/
485 
config_set_d(int i,int d)486 void config_set_d(int i, int d)
487 {
488     option_d[i].cur = d;
489     dirty = 1;
490 }
491 
config_tgl_d(int i)492 void config_tgl_d(int i)
493 {
494     option_d[i].cur = (option_d[i].cur ? 0 : 1);
495     dirty = 1;
496 }
497 
config_tst_d(int i,int d)498 int config_tst_d(int i, int d)
499 {
500     return (option_d[i].cur == d) ? 1 : 0;
501 }
502 
config_get_d(int i)503 int config_get_d(int i)
504 {
505     return option_d[i].cur;
506 }
507 
508 /*---------------------------------------------------------------------------*/
509 
config_set_s(int i,const char * src)510 void config_set_s(int i, const char *src)
511 {
512     if (option_s[i].cur)
513         free(option_s[i].cur);
514 
515     option_s[i].cur = strdup(src);
516 
517     dirty = 1;
518 }
519 
config_get_s(int i)520 const char *config_get_s(int i)
521 {
522     return option_s[i].cur;
523 }
524 
525 /*---------------------------------------------------------------------------*/
526 
config_cheat(void)527 int config_cheat(void)
528 {
529     return config_get_d(CONFIG_CHEAT);
530 }
531 
config_set_cheat(void)532 void config_set_cheat(void)
533 {
534     config_set_d(CONFIG_CHEAT, 1);
535 }
536 
config_clr_cheat(void)537 void config_clr_cheat(void)
538 {
539     config_set_d(CONFIG_CHEAT, 0);
540 }
541 
542 /*---------------------------------------------------------------------------*/
543 
config_screenshot(void)544 int config_screenshot(void)
545 {
546     return ++option_d[CONFIG_SCREENSHOT].cur;
547 }
548 
549 /*---------------------------------------------------------------------------*/
550