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