Make new grow/shrink models
[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 <errno.h>
20 #include <math.h>
21 #include <ctype.h>
22
23 #include "config.h"
24 #include "glext.h"
25 #include "vec3.h"
26 #include "sync.h"
27 #include "common.h"
28
29 /*---------------------------------------------------------------------------*/
30
31 /* Define the mkdir symbol. */
32
33 #ifdef _WIN32
34 #include <direct.h>
35 #else
36 #include <sys/stat.h>
37 #endif
38
39 /*---------------------------------------------------------------------------*/
40
41 static int   option_d[CONFIG_OPTION_D_COUNT];
42 static char *option_s[CONFIG_OPTION_S_COUNT];
43
44 static int dirty = 0;
45
46 /*---------------------------------------------------------------------------*/
47
48 static void config_key(const char *s, int i, int d)
49 {
50     int c;
51
52     config_set_d(i, d);
53
54     for (c = 0; c < SDLK_LAST; c++)
55         if (strcmp(s, SDL_GetKeyName((SDLKey) c)) == 0)
56         {
57             config_set_d(i, c);
58             break;
59         }
60 }
61
62 /*---------------------------------------------------------------------------*/
63
64 void config_init(void)
65 {
66     memset(option_d, 0, sizeof (option_d));
67     memset(option_s, 0, sizeof (option_s));
68
69     config_set_d(CONFIG_FULLSCREEN,           DEFAULT_FULLSCREEN);
70     config_set_d(CONFIG_WIDTH,                DEFAULT_WIDTH);
71     config_set_d(CONFIG_HEIGHT,               DEFAULT_HEIGHT);
72     config_set_d(CONFIG_STEREO,               DEFAULT_STEREO);
73     config_set_d(CONFIG_CAMERA,               DEFAULT_CAMERA);
74     config_set_d(CONFIG_TEXTURES,             DEFAULT_TEXTURES);
75     config_set_d(CONFIG_GEOMETRY,             DEFAULT_GEOMETRY);
76     config_set_d(CONFIG_REFLECTION,           DEFAULT_REFLECTION);
77     config_set_d(CONFIG_MULTISAMPLE,          DEFAULT_MULTISAMPLE);
78     config_set_d(CONFIG_MIPMAP,               DEFAULT_MIPMAP);
79     config_set_d(CONFIG_ANISO,                DEFAULT_ANISO);
80     config_set_d(CONFIG_BACKGROUND,           DEFAULT_BACKGROUND);
81     config_set_d(CONFIG_SHADOW,               DEFAULT_SHADOW);
82     config_set_d(CONFIG_AUDIO_BUFF,           DEFAULT_AUDIO_BUFF);
83     config_set_d(CONFIG_MOUSE_SENSE,          DEFAULT_MOUSE_SENSE);
84     config_set_d(CONFIG_MOUSE_INVERT,         DEFAULT_MOUSE_INVERT);
85     config_set_d(CONFIG_VSYNC,                DEFAULT_VSYNC);
86     config_set_d(CONFIG_NICE,                 DEFAULT_NICE);
87     config_set_d(CONFIG_FPS,                  DEFAULT_FPS);
88     config_set_d(CONFIG_SOUND_VOLUME,         DEFAULT_SOUND_VOLUME);
89     config_set_d(CONFIG_MUSIC_VOLUME,         DEFAULT_MUSIC_VOLUME);
90     config_set_d(CONFIG_JOYSTICK,             DEFAULT_JOYSTICK);
91     config_set_d(CONFIG_JOYSTICK_DEVICE,      DEFAULT_JOYSTICK_DEVICE);
92     config_set_d(CONFIG_JOYSTICK_AXIS_X,      DEFAULT_JOYSTICK_AXIS_X);
93     config_set_d(CONFIG_JOYSTICK_AXIS_Y,      DEFAULT_JOYSTICK_AXIS_Y);
94     config_set_d(CONFIG_JOYSTICK_BUTTON_A,    DEFAULT_JOYSTICK_BUTTON_A);
95     config_set_d(CONFIG_JOYSTICK_BUTTON_B,    DEFAULT_JOYSTICK_BUTTON_B);
96     config_set_d(CONFIG_JOYSTICK_BUTTON_L,    DEFAULT_JOYSTICK_BUTTON_L);
97     config_set_d(CONFIG_JOYSTICK_BUTTON_R,    DEFAULT_JOYSTICK_BUTTON_R);
98     config_set_d(CONFIG_JOYSTICK_BUTTON_EXIT, DEFAULT_JOYSTICK_BUTTON_EXIT);
99     config_set_d(CONFIG_JOYSTICK_CAMERA_1,    DEFAULT_JOYSTICK_CAMERA_1);
100     config_set_d(CONFIG_JOYSTICK_CAMERA_2,    DEFAULT_JOYSTICK_CAMERA_2);
101     config_set_d(CONFIG_JOYSTICK_CAMERA_3,    DEFAULT_JOYSTICK_CAMERA_3);
102     config_set_d(CONFIG_JOYSTICK_DPAD_L,      DEFAULT_JOYSTICK_DPAD_L);
103     config_set_d(CONFIG_JOYSTICK_DPAD_R,      DEFAULT_JOYSTICK_DPAD_R);
104     config_set_d(CONFIG_JOYSTICK_DPAD_U,      DEFAULT_JOYSTICK_DPAD_U);
105     config_set_d(CONFIG_JOYSTICK_DPAD_D,      DEFAULT_JOYSTICK_DPAD_D);
106     config_set_d(CONFIG_KEY_CAMERA_1,         DEFAULT_KEY_CAMERA_1);
107     config_set_d(CONFIG_KEY_CAMERA_2,         DEFAULT_KEY_CAMERA_2);
108     config_set_d(CONFIG_KEY_CAMERA_3,         DEFAULT_KEY_CAMERA_3);
109     config_set_d(CONFIG_KEY_CAMERA_R,         DEFAULT_KEY_CAMERA_R);
110     config_set_d(CONFIG_KEY_CAMERA_L,         DEFAULT_KEY_CAMERA_L);
111     config_set_d(CONFIG_VIEW_FOV,             DEFAULT_VIEW_FOV);
112     config_set_d(CONFIG_VIEW_DP,              DEFAULT_VIEW_DP);
113     config_set_d(CONFIG_VIEW_DC,              DEFAULT_VIEW_DC);
114     config_set_d(CONFIG_VIEW_DZ,              DEFAULT_VIEW_DZ);
115     config_set_d(CONFIG_ROTATE_FAST,          DEFAULT_ROTATE_FAST);
116     config_set_d(CONFIG_ROTATE_SLOW,          DEFAULT_ROTATE_SLOW);
117     config_set_s(CONFIG_PLAYER,               DEFAULT_PLAYER);
118     config_set_s(CONFIG_BALL,                 DEFAULT_BALL);
119     config_set_s(CONFIG_WIIMOTE_ADDR,         DEFAULT_WIIMOTE_ADDR);
120     config_set_s(CONFIG_REPLAY_NAME,          DEFAULT_REPLAY_NAME);
121     config_set_d(CONFIG_CHEAT,                DEFAULT_CHEAT);
122     config_set_d(CONFIG_STATS,                DEFAULT_STATS);
123     config_set_d(CONFIG_UNIFORM,              DEFAULT_UNIFORM);
124     config_set_d(CONFIG_KEY_FORWARD,          DEFAULT_KEY_FORWARD);
125     config_set_d(CONFIG_KEY_BACKWARD,         DEFAULT_KEY_BACKWARD);
126     config_set_d(CONFIG_KEY_LEFT,             DEFAULT_KEY_LEFT);
127     config_set_d(CONFIG_KEY_RIGHT,            DEFAULT_KEY_RIGHT);
128     config_set_d(CONFIG_KEY_PAUSE,            DEFAULT_KEY_PAUSE);
129     config_set_d(CONFIG_KEY_RESTART,          DEFAULT_KEY_RESTART);
130     config_set_d(CONFIG_KEY_SCORE_NEXT,       DEFAULT_KEY_SCORE_NEXT);
131     config_set_d(CONFIG_SCREENSHOT,           DEFAULT_SCREENSHOT);
132     config_set_d(CONFIG_LOCK_GOALS,           DEFAULT_LOCK_GOALS);
133 }
134
135 /*
136  * Scan a NUL-terminated string LINE according to the format
137  * '^<space>?<key><space><value>$' and store pointers to the start of key and
138  * value at DST_KEY and DST_VAL, respectively.  No memory is allocated to store
139  * the strings;  instead, the memory pointed to by LINE modified in-place as
140  * needed.
141  *
142  * Return 1 if LINE matches the format, return 0 otherwise.
143  */
144
145 static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
146 {
147     if (line)
148     {
149         char *key, *val, *space;
150
151         for (key = line; *key && isspace(*key); key++);
152
153         if (*key)
154         {
155             if (dst_key)
156                 *dst_key = key;
157         }
158         else
159             return 0;
160
161         for (space = key; *space && !isspace(*space); space++);
162
163         if (*space)
164         {
165             /* NUL-terminate the key, if necessary. */
166
167             if (dst_key)
168             {
169                 *space = '\0';
170                 space++;
171             }
172         }
173         else
174             return 0;
175
176         for (val = space; *val && isspace(*val); val++);
177
178         if (*val)
179         {
180             if (dst_val)
181                 *dst_val = val;
182         }
183         else
184             return 0;
185
186         return 1;
187     }
188
189     return 0;
190 }
191
192 void config_load(void)
193 {
194     FILE *fp;
195
196     if ((fp = fopen(config_user(USER_CONFIG_FILE), "r")))
197     {
198         char *line, *key, *val;
199
200         while (read_line(&line, fp))
201         {
202             if (scan_key_and_value(&key, &val, line))
203             {
204                 if      (strcmp(key, "fullscreen")            == 0)
205                     config_set_d(CONFIG_FULLSCREEN,           atoi(val));
206                 else if (strcmp(key, "width")                 == 0)
207                     config_set_d(CONFIG_WIDTH,                atoi(val));
208                 else if (strcmp(key, "height")                == 0)
209                     config_set_d(CONFIG_HEIGHT,               atoi(val));
210                 else if (strcmp(key, "stereo")                == 0)
211                     config_set_d(CONFIG_STEREO,               atoi(val));
212                 else if (strcmp(key, "camera")                == 0)
213                     config_set_d(CONFIG_CAMERA,               atoi(val));
214                 else if (strcmp(key, "textures")              == 0)
215                     config_set_d(CONFIG_TEXTURES,             atoi(val));
216                 else if (strcmp(key, "geometry")              == 0)
217                     config_set_d(CONFIG_GEOMETRY,             atoi(val));
218                 else if (strcmp(key, "reflection")            == 0)
219                     config_set_d(CONFIG_REFLECTION,           atoi(val));
220                 else if (strcmp(key, "multisample")           == 0)
221                     config_set_d(CONFIG_MULTISAMPLE,          atoi(val));
222                 else if (strcmp(key, "mipmap")                == 0)
223                     config_set_d(CONFIG_MIPMAP,               atoi(val));
224                 else if (strcmp(key, "aniso")                 == 0)
225                     config_set_d(CONFIG_ANISO,                atoi(val));
226                 else if (strcmp(key, "background")            == 0)
227                     config_set_d(CONFIG_BACKGROUND,           atoi(val));
228                 else if (strcmp(key, "shadow")                == 0)
229                     config_set_d(CONFIG_SHADOW,               atoi(val));
230                 else if (strcmp(key, "audio_buff")            == 0)
231                     config_set_d(CONFIG_AUDIO_BUFF,           atoi(val));
232                 else if (strcmp(key, "mouse_sense")           == 0)
233                     config_set_d(CONFIG_MOUSE_SENSE,          atoi(val));
234                 else if (strcmp(key, "mouse_invert")          == 0)
235                     config_set_d(CONFIG_MOUSE_INVERT,         atoi(val));
236                 else if (strcmp(key, "vsync")                 == 0)
237                     config_set_d(CONFIG_VSYNC,                atoi(val));
238                 else if (strcmp(key, "nice")                  == 0)
239                     config_set_d(CONFIG_NICE,                 atoi(val));
240                 else if (strcmp(key, "fps")                   == 0)
241                     config_set_d(CONFIG_FPS,                  atoi(val));
242                 else if (strcmp(key, "sound_volume")          == 0)
243                     config_set_d(CONFIG_SOUND_VOLUME,         atoi(val));
244                 else if (strcmp(key, "music_volume")          == 0)
245                     config_set_d(CONFIG_MUSIC_VOLUME,         atoi(val));
246                 else if (strcmp(key, "joystick")              == 0)
247                     config_set_d(CONFIG_JOYSTICK,             atoi(val));
248                 else if (strcmp(key, "joystick_device")       == 0)
249                     config_set_d(CONFIG_JOYSTICK_DEVICE,      atoi(val));
250                 else if (strcmp(key, "joystick_axis_x")       == 0)
251                     config_set_d(CONFIG_JOYSTICK_AXIS_X,      atoi(val));
252                 else if (strcmp(key, "joystick_axis_y")       == 0)
253                     config_set_d(CONFIG_JOYSTICK_AXIS_Y,      atoi(val));
254                 else if (strcmp(key, "joystick_button_a")     == 0)
255                     config_set_d(CONFIG_JOYSTICK_BUTTON_A,    atoi(val));
256                 else if (strcmp(key, "joystick_button_b")     == 0)
257                     config_set_d(CONFIG_JOYSTICK_BUTTON_B,    atoi(val));
258                 else if (strcmp(key, "joystick_button_r")     == 0)
259                     config_set_d(CONFIG_JOYSTICK_BUTTON_R,    atoi(val));
260                 else if (strcmp(key, "joystick_button_l")     == 0)
261                     config_set_d(CONFIG_JOYSTICK_BUTTON_L,    atoi(val));
262                 else if (strcmp(key, "joystick_button_exit")  == 0)
263                     config_set_d(CONFIG_JOYSTICK_BUTTON_EXIT, atoi(val));
264                 else if (strcmp(key, "joystick_camera_1")     == 0)
265                     config_set_d(CONFIG_JOYSTICK_CAMERA_1,    atoi(val));
266                 else if (strcmp(key, "joystick_camera_2")     == 0)
267                     config_set_d(CONFIG_JOYSTICK_CAMERA_2,    atoi(val));
268                 else if (strcmp(key, "joystick_camera_3")     == 0)
269                     config_set_d(CONFIG_JOYSTICK_CAMERA_3,    atoi(val));
270                 else if (strcmp(key, "view_fov")              == 0)
271                     config_set_d(CONFIG_VIEW_FOV,             atoi(val));
272                 else if (strcmp(key, "view_dp")               == 0)
273                     config_set_d(CONFIG_VIEW_DP,              atoi(val));
274                 else if (strcmp(key, "view_dc")               == 0)
275                     config_set_d(CONFIG_VIEW_DC,              atoi(val));
276                 else if (strcmp(key, "view_dz")               == 0)
277                     config_set_d(CONFIG_VIEW_DZ,              atoi(val));
278                 else if (strcmp(key, "rotate_fast")           == 0)
279                     config_set_d(CONFIG_ROTATE_FAST,          atoi(val));
280                 else if (strcmp(key, "rotate_slow")           == 0)
281                     config_set_d(CONFIG_ROTATE_SLOW,          atoi(val));
282
283                 else if (strcmp(key, "key_forward")  == 0)
284                     config_key(val, CONFIG_KEY_FORWARD, DEFAULT_KEY_FORWARD);
285                 else if (strcmp(key, "key_backward")  == 0)
286                     config_key(val, CONFIG_KEY_BACKWARD, DEFAULT_KEY_BACKWARD);
287                 else if (strcmp(key, "key_left")  == 0)
288                     config_key(val, CONFIG_KEY_LEFT, DEFAULT_KEY_LEFT);
289                 else if (strcmp(key, "key_right")  == 0)
290                     config_key(val, CONFIG_KEY_RIGHT, DEFAULT_KEY_RIGHT);
291
292                 else if (strcmp(key, "key_camera_1")  == 0)
293                     config_key(val, CONFIG_KEY_CAMERA_1, DEFAULT_KEY_CAMERA_1);
294                 else if (strcmp(key, "key_camera_2")  == 0)
295                     config_key(val, CONFIG_KEY_CAMERA_2, DEFAULT_KEY_CAMERA_2);
296                 else if (strcmp(key, "key_camera_3")  == 0)
297                     config_key(val, CONFIG_KEY_CAMERA_3, DEFAULT_KEY_CAMERA_3);
298                 else if (strcmp(key, "key_camera_r")  == 0)
299                     config_key(val, CONFIG_KEY_CAMERA_R, DEFAULT_KEY_CAMERA_R);
300                 else if (strcmp(key, "key_camera_l")  == 0)
301                     config_key(val, CONFIG_KEY_CAMERA_L, DEFAULT_KEY_CAMERA_L);
302
303                 else if (strcmp(key, "key_pause")    == 0)
304                     config_key(val, CONFIG_KEY_PAUSE,   DEFAULT_KEY_PAUSE);
305                 else if (strcmp(key, "key_restart")  == 0)
306                     config_key(val, CONFIG_KEY_RESTART, DEFAULT_KEY_RESTART);
307
308                 else if (strcmp(key, "key_score_next") == 0)
309                     config_key(val, CONFIG_KEY_SCORE_NEXT, DEFAULT_KEY_SCORE_NEXT);
310
311                 else if (strcmp(key, "player") == 0)
312                     config_set_s(CONFIG_PLAYER, val);
313                 else if (strcmp(key, "ball_file") == 0)
314                     config_set_s(CONFIG_BALL, val);
315                 else if (strcmp(key, "wiimote_addr") == 0)
316                     config_set_s(CONFIG_WIIMOTE_ADDR, val);
317                 else if (strcmp(key, "replay_name") == 0)
318                     config_set_s(CONFIG_REPLAY_NAME, val);
319
320                 else if (strcmp(key, "cheat")   == 0)
321                     config_set_d(CONFIG_CHEAT, atoi(val));
322                 else if (strcmp(key, "stats")   == 0)
323                     config_set_d(CONFIG_STATS, atoi(val));
324                 else if (strcmp(key, "uniform") == 0)
325                     config_set_d(CONFIG_UNIFORM, atoi(val));
326                 else if (strcmp(key, "screenshot") == 0)
327                     config_set_d(CONFIG_SCREENSHOT, atoi(val));
328                 else if (strcmp(key, "lock_goals") == 0)
329                     config_set_d(CONFIG_LOCK_GOALS, atoi(val));
330             }
331
332             free(line);
333         }
334
335         fclose(fp);
336
337         dirty = 0;
338     }
339 }
340
341 void config_save(void)
342 {
343     FILE *fp;
344
345     if (dirty && (fp = fopen(config_user(USER_CONFIG_FILE), "w")))
346     {
347         fprintf(fp, "fullscreen           %d\n",
348                 option_d[CONFIG_FULLSCREEN]);
349         fprintf(fp, "width                %d\n",
350                 option_d[CONFIG_WIDTH]);
351         fprintf(fp, "height               %d\n",
352                 option_d[CONFIG_HEIGHT]);
353         fprintf(fp, "stereo               %d\n",
354                 option_d[CONFIG_STEREO]);
355         fprintf(fp, "camera               %d\n",
356                 option_d[CONFIG_CAMERA]);
357         fprintf(fp, "textures             %d\n",
358                 option_d[CONFIG_TEXTURES]);
359         fprintf(fp, "geometry             %d\n",
360                 option_d[CONFIG_GEOMETRY]);
361         fprintf(fp, "reflection           %d\n",
362                 option_d[CONFIG_REFLECTION]);
363         fprintf(fp, "multisample          %d\n",
364                 option_d[CONFIG_MULTISAMPLE]);
365         fprintf(fp, "mipmap               %d\n",
366                 option_d[CONFIG_MIPMAP]);
367         fprintf(fp, "aniso                %d\n",
368                 option_d[CONFIG_ANISO]);
369         fprintf(fp, "background           %d\n",
370                 option_d[CONFIG_BACKGROUND]);
371         fprintf(fp, "shadow               %d\n",
372                 option_d[CONFIG_SHADOW]);
373         fprintf(fp, "audio_buff           %d\n",
374                 option_d[CONFIG_AUDIO_BUFF]);
375         fprintf(fp, "mouse_sense          %d\n",
376                 option_d[CONFIG_MOUSE_SENSE]);
377         fprintf(fp, "mouse_invert         %d\n",
378                 option_d[CONFIG_MOUSE_INVERT]);
379         fprintf(fp, "vsync                %d\n",
380                 option_d[CONFIG_VSYNC]);
381         fprintf(fp, "nice                 %d\n",
382                 option_d[CONFIG_NICE]);
383         fprintf(fp, "fps                  %d\n",
384                 option_d[CONFIG_FPS]);
385         fprintf(fp, "sound_volume         %d\n",
386                 option_d[CONFIG_SOUND_VOLUME]);
387         fprintf(fp, "music_volume         %d\n",
388                 option_d[CONFIG_MUSIC_VOLUME]);
389         fprintf(fp, "joystick             %d\n",
390                 option_d[CONFIG_JOYSTICK]);
391         fprintf(fp, "joystick_device      %d\n",
392                 option_d[CONFIG_JOYSTICK_DEVICE]);
393         fprintf(fp, "joystick_axis_x      %d\n",
394                 option_d[CONFIG_JOYSTICK_AXIS_X]);
395         fprintf(fp, "joystick_axis_y      %d\n",
396                 option_d[CONFIG_JOYSTICK_AXIS_Y]);
397         fprintf(fp, "joystick_button_a    %d\n",
398                 option_d[CONFIG_JOYSTICK_BUTTON_A]);
399         fprintf(fp, "joystick_button_b    %d\n",
400                 option_d[CONFIG_JOYSTICK_BUTTON_B]);
401         fprintf(fp, "joystick_button_r    %d\n",
402                 option_d[CONFIG_JOYSTICK_BUTTON_R]);
403         fprintf(fp, "joystick_button_l    %d\n",
404                 option_d[CONFIG_JOYSTICK_BUTTON_L]);
405         fprintf(fp, "joystick_button_exit %d\n",
406                 option_d[CONFIG_JOYSTICK_BUTTON_EXIT]);
407         fprintf(fp, "joystick_camera_1    %d\n",
408                 option_d[CONFIG_JOYSTICK_CAMERA_1]);
409         fprintf(fp, "joystick_camera_2    %d\n",
410                 option_d[CONFIG_JOYSTICK_CAMERA_2]);
411         fprintf(fp, "joystick_camera_3    %d\n",
412                 option_d[CONFIG_JOYSTICK_CAMERA_3]);
413         fprintf(fp, "view_fov             %d\n",
414                 option_d[CONFIG_VIEW_FOV]);
415         fprintf(fp, "view_dp              %d\n",
416                 option_d[CONFIG_VIEW_DP]);
417         fprintf(fp, "view_dc              %d\n",
418                 option_d[CONFIG_VIEW_DC]);
419         fprintf(fp, "view_dz              %d\n",
420                 option_d[CONFIG_VIEW_DZ]);
421         fprintf(fp, "rotate_fast          %d\n",
422                 option_d[CONFIG_ROTATE_FAST]);
423         fprintf(fp, "rotate_slow          %d\n",
424                 option_d[CONFIG_ROTATE_SLOW]);
425
426         fprintf(fp, "key_forward          %s\n",
427                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_FORWARD]));
428         fprintf(fp, "key_backward         %s\n",
429                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_BACKWARD]));
430         fprintf(fp, "key_left             %s\n",
431                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_LEFT]));
432         fprintf(fp, "key_right            %s\n",
433                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_RIGHT]));
434
435         fprintf(fp, "key_camera_1         %s\n",
436                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_1]));
437         fprintf(fp, "key_camera_2         %s\n",
438                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_2]));
439         fprintf(fp, "key_camera_3         %s\n",
440                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_3]));
441         fprintf(fp, "key_camera_r         %s\n",
442                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_R]));
443         fprintf(fp, "key_camera_l         %s\n",
444                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_L]));
445
446         fprintf(fp, "key_pause            %s\n",
447                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_PAUSE]));
448         fprintf(fp, "key_restart          %s\n",
449                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_RESTART]));
450
451         fprintf(fp, "key_score_next       %s\n",
452                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_SCORE_NEXT]));
453
454         if (strlen(option_s[CONFIG_PLAYER]) > 0)
455             fprintf(fp, "player       %s\n", option_s[CONFIG_PLAYER]);
456         if (strlen(option_s[CONFIG_BALL]) > 0)
457             fprintf(fp, "ball_file    %s\n", option_s[CONFIG_BALL]);
458         if (strlen(option_s[CONFIG_WIIMOTE_ADDR]) > 0)
459             fprintf(fp, "wiimote_addr %s\n", option_s[CONFIG_WIIMOTE_ADDR]);
460         if (strlen(option_s[CONFIG_REPLAY_NAME]) > 0)
461             fprintf(fp, "replay_name  %s\n", option_s[CONFIG_REPLAY_NAME]);
462
463         fprintf(fp, "stats                %d\n", option_d[CONFIG_STATS]);
464         fprintf(fp, "uniform              %d\n", option_d[CONFIG_UNIFORM]);
465         fprintf(fp, "screenshot           %d\n", option_d[CONFIG_SCREENSHOT]);
466         fprintf(fp, "lock_goals           %d\n", option_d[CONFIG_LOCK_GOALS]);
467
468         if (config_cheat())
469             fprintf(fp, "cheat                %d\n", option_d[CONFIG_CHEAT]);
470
471         fclose(fp);
472     }
473
474     dirty = 0;
475 }
476
477 /*---------------------------------------------------------------------------*/
478
479 int check_extension(const char *needle)
480 {
481     const GLubyte *haystack, *c;
482
483     /* Search for the given string in the OpenGL extension strings. */
484
485     for (haystack = glGetString(GL_EXTENSIONS); *haystack; haystack++)
486     {
487         for (c = (const GLubyte *) needle; *c && *haystack; c++, haystack++)
488             if (*c != *haystack)
489                 break;
490
491         if ((*c == 0) && (*haystack == ' ' || *haystack == '\0'))
492             return 1;
493     }
494
495     return 0;
496 }
497
498 int config_mode(int f, int w, int h)
499 {
500     int stereo  = config_get_d(CONFIG_STEREO)      ? 1 : 0;
501     int stencil = config_get_d(CONFIG_REFLECTION)  ? 1 : 0;
502     int buffers = config_get_d(CONFIG_MULTISAMPLE) ? 1 : 0;
503     int samples = config_get_d(CONFIG_MULTISAMPLE);
504     int vsync   = config_get_d(CONFIG_VSYNC)       ? 1 : 0;
505
506     SDL_GL_SetAttribute(SDL_GL_STEREO,             stereo);
507     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,       stencil);
508     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, buffers);
509     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
510     SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL,       vsync);
511
512     /* Try to set the currently specified mode. */
513
514     if (SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (f ? SDL_FULLSCREEN : 0)))
515     {
516         config_set_d(CONFIG_FULLSCREEN, f);
517         config_set_d(CONFIG_WIDTH,      w);
518         config_set_d(CONFIG_HEIGHT,     h);
519
520         glViewport(0, 0, w, h);
521         glClearColor(0.0f, 0.0f, 0.1f, 0.0f);
522
523         glEnable(GL_NORMALIZE);
524         glEnable(GL_CULL_FACE);
525         glEnable(GL_DEPTH_TEST);
526         glEnable(GL_TEXTURE_2D);
527         glEnable(GL_LIGHTING);
528         glEnable(GL_BLEND);
529
530         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,
531                       GL_SEPARATE_SPECULAR_COLOR);
532         glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
533
534         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
535         glDepthFunc(GL_LEQUAL);
536
537         /*
538          * Mac OS X might still need this, because apparently SDL doesn't do
539          * SDL_GL_SWAP_CONTROL on OS X.  TODO: investigate.
540          */
541 #if 0
542         if (vsync) sync_init();
543 #endif
544
545         /* If GL supports multisample, and SDL got a multisample buffer... */
546
547 #ifdef GL_ARB_multisample
548         if (check_extension("ARB_multisample"))
549         {
550             SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
551             if (buffers)
552                 glEnable(GL_MULTISAMPLE_ARB);
553         }
554 #endif
555
556         return 1;
557     }
558
559     /* If the mode failed, try it without stereo. */
560
561     else if (stereo)
562     {
563         config_set_d(CONFIG_STEREO, 0);
564         return config_mode(f, w, h);
565     }
566
567     /* If the mode failed, try decreasing the level of multisampling. */
568
569     else if (buffers)
570     {
571         config_set_d(CONFIG_MULTISAMPLE, samples / 2);
572         return config_mode(f, w, h);
573     }
574
575     /* If that mode failed, try it without reflections. */
576
577     else if (stencil)
578     {
579         config_set_d(CONFIG_REFLECTION, 0);
580         return config_mode(f, w, h);
581     }
582
583     /* If THAT mode failed, punt. */
584
585     return 0;
586 }
587
588 /*---------------------------------------------------------------------------*/
589
590 static float ms     = 0;
591 static int   fps    = 0;
592 static int   last   = 0;
593 static int   ticks  = 0;
594 static int   frames = 0;
595
596 int  config_perf(void)
597 {
598     return fps;
599 }
600
601 void config_swap(void)
602 {
603     int dt;
604
605     SDL_GL_SwapBuffers();
606
607     /* Accumulate time passed and frames rendered. */
608
609     dt = (int) SDL_GetTicks() - last;
610
611     frames +=  1;
612     ticks  += dt;
613     last   += dt;
614
615     /* Average over 250ms. */
616
617     if (ticks > 1000)
618     {
619         /* Round the frames-per-second value to the nearest integer. */
620
621         double k = 1000.0 * frames / ticks;
622         double f = floor(k);
623         double c = ceil (k);
624
625         /* Compute frame time and frames-per-second stats. */
626
627         fps = (int) ((c - k < k - f) ? c : f);
628         ms  = (float) ticks / (float) frames;
629
630         /* Reset the counters for the next update. */
631
632         frames = 0;
633         ticks  = 0;
634
635         /* Output statistics if configured. */
636
637         if (option_d[CONFIG_STATS])
638             fprintf(stdout, "%4d %8.4f\n", fps, ms);
639     }
640 }
641
642 /*---------------------------------------------------------------------------*/
643
644 void config_set_d(int i, int d)
645 {
646     option_d[i] = d;
647     dirty = 1;
648 }
649
650 void config_tgl_d(int i)
651 {
652     option_d[i] = (option_d[i] ? 0 : 1);
653     dirty = 1;
654 }
655
656 int config_tst_d(int i, int d)
657 {
658     return (option_d[i] == d) ? 1 : 0;
659 }
660
661 int config_get_d(int i)
662 {
663     return option_d[i];
664 }
665
666 /*---------------------------------------------------------------------------*/
667
668 void config_set_s(int i, const char *src)
669 {
670     if (option_s[i])
671         free(option_s[i]);
672
673     option_s[i] = strdup(src);
674
675     dirty = 1;
676 }
677
678 void config_get_s(int i, char *dst, int len)
679 {
680     strncpy(dst, option_s[i], len);
681 }
682
683 /*---------------------------------------------------------------------------*/
684
685 static int grabbed = 0;
686
687 void config_set_grab(int w)
688 {
689     if (w)
690     {
691         SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
692
693         SDL_WarpMouse(config_get_d(CONFIG_WIDTH)  / 2,
694                       config_get_d(CONFIG_HEIGHT) / 2);
695
696         SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
697     }
698
699     SDL_WM_GrabInput(SDL_GRAB_ON);
700     SDL_ShowCursor(SDL_DISABLE);
701
702     grabbed = 1;
703 }
704
705 void config_clr_grab(void)
706 {
707     SDL_WM_GrabInput(SDL_GRAB_OFF);
708     SDL_ShowCursor(SDL_ENABLE);
709     grabbed = 0;
710 }
711
712 int  config_get_grab(void)
713 {
714     return grabbed;
715 }
716
717 /*---------------------------------------------------------------------------*/
718
719 int config_cheat(void)
720 {
721     return config_get_d(CONFIG_CHEAT);
722 }
723
724 void config_set_cheat(void)
725 {
726     config_set_d(CONFIG_CHEAT, 1);
727 }
728
729 void config_clr_cheat(void)
730 {
731     config_set_d(CONFIG_CHEAT, 0);
732 }
733
734 /*---------------------------------------------------------------------------*/
735
736 int config_screenshot(void)
737 {
738     return ++option_d[CONFIG_SCREENSHOT];
739 }
740
741 /*---------------------------------------------------------------------------*/
742
743 void config_push_persp(float fov, float n, float f)
744 {
745     GLdouble m[4][4];
746
747     GLdouble r = fov / 2 * V_PI / 180;
748     GLdouble s = sin(r);
749     GLdouble c = cos(r) / s;
750
751     GLdouble a = ((GLdouble) option_d[CONFIG_WIDTH] /
752                   (GLdouble) option_d[CONFIG_HEIGHT]);
753
754     glMatrixMode(GL_PROJECTION);
755     {
756         glPushMatrix();
757         glLoadIdentity();
758
759         m[0][0] =  c/a;
760         m[0][1] =  0.0;
761         m[0][2] =  0.0;
762         m[0][3] =  0.0;
763         m[1][0] =  0.0;
764         m[1][1] =    c;
765         m[1][2] =  0.0;
766         m[1][3] =  0.0;
767         m[2][0] =  0.0;
768         m[2][1] =  0.0;
769         m[2][2] = -(f + n) / (f - n);
770         m[2][3] = -1.0;
771         m[3][0] =  0.0;
772         m[3][1] =  0.0;
773         m[3][2] = -2.0 * n * f / (f - n);
774         m[3][3] =  0.0;
775
776         glMultMatrixd(&m[0][0]);
777     }
778     glMatrixMode(GL_MODELVIEW);
779 }
780
781 void config_push_ortho(void)
782 {
783     GLdouble w = (GLdouble) option_d[CONFIG_WIDTH];
784     GLdouble h = (GLdouble) option_d[CONFIG_HEIGHT];
785
786     glMatrixMode(GL_PROJECTION);
787     {
788         glPushMatrix();
789         glLoadIdentity();
790         glOrtho(0.0, w, 0.0, h, -1.0, +1.0);
791     }
792     glMatrixMode(GL_MODELVIEW);
793 }
794
795 void config_pop_matrix(void)
796 {
797     glMatrixMode(GL_PROJECTION);
798     {
799         glPopMatrix();
800     }
801     glMatrixMode(GL_MODELVIEW);
802 }
803
804 void config_clear(void)
805 {
806     if (option_d[CONFIG_REFLECTION])
807         glClear(GL_COLOR_BUFFER_BIT |
808                 GL_DEPTH_BUFFER_BIT |
809                 GL_STENCIL_BUFFER_BIT);
810     else
811         glClear(GL_COLOR_BUFFER_BIT |
812                 GL_DEPTH_BUFFER_BIT);
813 }
814
815 /*---------------------------------------------------------------------------*/