share/gui: don't use less of widget width for truncation than available
[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_VIEW_FOV;
82 int CONFIG_VIEW_DP;
83 int CONFIG_VIEW_DC;
84 int CONFIG_VIEW_DZ;
85 int CONFIG_ROTATE_FAST;
86 int CONFIG_ROTATE_SLOW;
87 int CONFIG_KEY_FORWARD;
88 int CONFIG_KEY_BACKWARD;
89 int CONFIG_KEY_LEFT;
90 int CONFIG_KEY_RIGHT;
91 int CONFIG_KEY_PAUSE;
92 int CONFIG_KEY_RESTART;
93 int CONFIG_KEY_SCORE_NEXT;
94 int CONFIG_KEY_ROTATE_FAST;
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_VIEW_FOV,          "view_fov",          50 },
174     { &CONFIG_VIEW_DP,           "view_dp",           75 },
175     { &CONFIG_VIEW_DC,           "view_dc",           25 },
176     { &CONFIG_VIEW_DZ,           "view_dz",           200 },
177     { &CONFIG_ROTATE_FAST,       "rotate_fast",       300 },
178     { &CONFIG_ROTATE_SLOW,       "rotate_slow",       150 },
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     { &CONFIG_CHEAT,             "cheat",             0 },
188     { &CONFIG_STATS,             "stats",             0 },
189     { &CONFIG_UNIFORM,           "uniform",           0 },
190     { &CONFIG_SCREENSHOT,        "screenshot",        0 },
191     { &CONFIG_LOCK_GOALS,        "lock_goals",        0 }
192 };
193
194 static struct
195 {
196     int        *sym;
197     const char *name;
198     const char *def;
199     char       *cur;
200 } option_s[] = {
201     { &CONFIG_PLAYER,       "player",       "" },
202     { &CONFIG_BALL_FILE,    "ball_file",    "ball/basic-ball/basic-ball" },
203     { &CONFIG_WIIMOTE_ADDR, "wiimote_addr", "" },
204     { &CONFIG_REPLAY_NAME,  "replay_name",  "%s-%l" }
205 };
206
207 static int dirty = 0;
208
209 /*---------------------------------------------------------------------------*/
210
211 static void config_key(const char *s, int i)
212 {
213     int c;
214
215     config_set_d(i, option_d[i].def);
216
217     for (c = 0; c < SDLK_LAST; c++)
218         if (strcmp(s, SDL_GetKeyName((SDLKey) c)) == 0)
219         {
220             config_set_d(i, c);
221             break;
222         }
223 }
224
225 /*---------------------------------------------------------------------------*/
226
227 static void config_mouse(const char *s, int i)
228 {
229     if      (strcmp(s, "none") == 0)
230         config_set_d(i, 0);
231     else if (strcmp(s, "left") == 0)
232         config_set_d(i, SDL_BUTTON_LEFT);
233     else if (strcmp(s, "right") == 0)
234         config_set_d(i, SDL_BUTTON_RIGHT);
235     else if (strcmp(s, "wheelup") == 0)
236         config_set_d(i, SDL_BUTTON_WHEELUP);
237     else if (strcmp(s, "middle") == 0)
238         config_set_d(i, SDL_BUTTON_MIDDLE);
239     else if (strcmp(s, "wheeldown") == 0)
240         config_set_d(i, SDL_BUTTON_WHEELDOWN);
241     else
242         config_set_d(i, atoi(s));
243 }
244
245 static const char *config_mouse_name(int b)
246 {
247     static char buff[sizeof ("256")];
248
249     sprintf(buff, "%d", b);
250
251     switch (b)
252     {
253     case 0:                    return "none";      break;
254     case SDL_BUTTON_LEFT:      return "left";      break;
255     case SDL_BUTTON_RIGHT:     return "right";     break;
256     case SDL_BUTTON_WHEELUP:   return "wheelup";   break;
257     case SDL_BUTTON_MIDDLE:    return "middle";    break;
258     case SDL_BUTTON_WHEELDOWN: return "wheeldown"; break;
259     default:                   return buff;        break;
260     }
261 }
262
263 /*---------------------------------------------------------------------------*/
264
265 void config_init(void)
266 {
267     int i;
268
269     /*
270      * Store index of each option in its associated config symbol and
271      * initialise current values with defaults.
272      */
273
274     for (i = 0; i < ARRAYSIZE(option_d); i++)
275     {
276         *option_d[i].sym = i;
277         config_set_d(i, option_d[i].def);
278     }
279
280     for (i = 0; i < ARRAYSIZE(option_s); i++)
281     {
282         *option_s[i].sym = i;
283         config_set_s(i, option_s[i].def);
284     }
285 }
286
287 /*
288  * Scan a NUL-terminated string LINE according to the format
289  * '^<space>?<key><space><value>$' and store pointers to the start of key and
290  * value at DST_KEY and DST_VAL, respectively.  No memory is allocated to store
291  * the strings;  instead, the memory pointed to by LINE modified in-place as
292  * needed.
293  *
294  * Return 1 if LINE matches the format, return 0 otherwise.
295  */
296
297 static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
298 {
299     if (line)
300     {
301         char *key, *val, *space;
302
303         for (key = line; *key && isspace(*key); key++);
304
305         if (*key)
306         {
307             if (dst_key)
308                 *dst_key = key;
309         }
310         else
311             return 0;
312
313         for (space = key; *space && !isspace(*space); space++);
314
315         if (*space)
316         {
317             /* NUL-terminate the key, if necessary. */
318
319             if (dst_key)
320             {
321                 *space = '\0';
322                 space++;
323             }
324         }
325         else
326             return 0;
327
328         for (val = space; *val && isspace(*val); val++);
329
330         if (*val)
331         {
332             if (dst_val)
333                 *dst_val = val;
334         }
335         else
336             return 0;
337
338         return 1;
339     }
340
341     return 0;
342 }
343
344 void config_load(void)
345 {
346     fs_file fh;
347
348     if ((fh = fs_open(USER_CONFIG_FILE, "r")))
349     {
350         char *line, *key, *val;
351
352         while (read_line(&line, fh))
353         {
354             if (scan_key_and_value(&key, &val, line))
355             {
356                 int i;
357
358                 /* Look up an integer option by that name. */
359
360                 for (i = 0; i < ARRAYSIZE(option_d); i++)
361                 {
362                     if (strcmp(key, option_d[i].name) == 0)
363                     {
364                         /* Translate some strings to integers. */
365
366                         if (i == CONFIG_MOUSE_CAMERA_1      ||
367                             i == CONFIG_MOUSE_CAMERA_2      ||
368                             i == CONFIG_MOUSE_CAMERA_3      ||
369                             i == CONFIG_MOUSE_CAMERA_TOGGLE ||
370                             i == CONFIG_MOUSE_CAMERA_L      ||
371                             i == CONFIG_MOUSE_CAMERA_R)
372                         {
373                             config_mouse(val, i);
374                         }
375                         else if (i == CONFIG_KEY_FORWARD       ||
376                                  i == CONFIG_KEY_BACKWARD      ||
377                                  i == CONFIG_KEY_LEFT          ||
378                                  i == CONFIG_KEY_RIGHT         ||
379                                  i == CONFIG_KEY_CAMERA_1      ||
380                                  i == CONFIG_KEY_CAMERA_2      ||
381                                  i == CONFIG_KEY_CAMERA_3      ||
382                                  i == CONFIG_KEY_CAMERA_TOGGLE ||
383                                  i == CONFIG_KEY_CAMERA_R      ||
384                                  i == CONFIG_KEY_CAMERA_L      ||
385                                  i == CONFIG_KEY_PAUSE         ||
386                                  i == CONFIG_KEY_RESTART       ||
387                                  i == CONFIG_KEY_SCORE_NEXT    ||
388                                  i == CONFIG_KEY_ROTATE_FAST)
389                         {
390                             config_key(val, i);
391                         }
392                         else
393                             config_set_d(i, atoi(val));
394
395                         /* Stop looking. */
396
397                         break;
398                     }
399                 }
400
401                 /* Look up a string option by that name.*/
402
403                 for (i = 0; i < ARRAYSIZE(option_s); i++)
404                 {
405                     if (strcmp(key, option_s[i].name) == 0)
406                     {
407                         config_set_s(i, val);
408                         break;
409                     }
410                 }
411             }
412             free(line);
413         }
414         fs_close(fh);
415
416         dirty = 0;
417     }
418 }
419
420 void config_save(void)
421 {
422     fs_file fh;
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_PAUSE         ||
456                      i == CONFIG_KEY_RESTART       ||
457                      i == CONFIG_KEY_SCORE_NEXT    ||
458                      i == CONFIG_KEY_ROTATE_FAST)
459             {
460                 s = SDL_GetKeyName((SDLKey) option_d[i].cur);
461             }
462             else if (i == CONFIG_CHEAT)
463             {
464                 if (!config_cheat())
465                     continue;
466             }
467
468             if (s)
469                 fs_printf(fh, "%-25s %s\n", option_d[i].name, s);
470             else
471                 fs_printf(fh, "%-25s %d\n", option_d[i].name, option_d[i].cur);
472         }
473
474         /* Write out string options. */
475
476         for (i = 0; i < ARRAYSIZE(option_s); i++)
477         {
478             if (option_s[i].cur && *option_s[i].cur)
479                 fs_printf(fh, "%-25s %s\n", option_s[i].name, option_s[i].cur);
480         }
481
482         fs_close(fh);
483     }
484
485     dirty = 0;
486 }
487
488 /*---------------------------------------------------------------------------*/
489
490 void config_set_d(int i, int d)
491 {
492     option_d[i].cur = d;
493     dirty = 1;
494 }
495
496 void config_tgl_d(int i)
497 {
498     option_d[i].cur = (option_d[i].cur ? 0 : 1);
499     dirty = 1;
500 }
501
502 int config_tst_d(int i, int d)
503 {
504     return (option_d[i].cur == d) ? 1 : 0;
505 }
506
507 int config_get_d(int i)
508 {
509     return option_d[i].cur;
510 }
511
512 /*---------------------------------------------------------------------------*/
513
514 void config_set_s(int i, const char *src)
515 {
516     if (option_s[i].cur)
517         free(option_s[i].cur);
518
519     option_s[i].cur = strdup(src);
520
521     dirty = 1;
522 }
523
524 const char *config_get_s(int i)
525 {
526     return option_s[i].cur;
527 }
528
529 /*---------------------------------------------------------------------------*/
530
531 int config_cheat(void)
532 {
533     return config_get_d(CONFIG_CHEAT);
534 }
535
536 void config_set_cheat(void)
537 {
538     config_set_d(CONFIG_CHEAT, 1);
539 }
540
541 void config_clr_cheat(void)
542 {
543     config_set_d(CONFIG_CHEAT, 0);
544 }
545
546 /*---------------------------------------------------------------------------*/
547
548 int config_screenshot(void)
549 {
550     return ++option_d[CONFIG_SCREENSHOT].cur;
551 }
552
553 /*---------------------------------------------------------------------------*/