Correct logic of BSP back/front tests
[neverball] / share / config.c
index b2f3d0e..7c112d0 100644 (file)
  */
 
 #include <SDL.h>
-#include <SDL_mixer.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
 #include <math.h>
+#include <ctype.h>
 
 #include "config.h"
 #include "glext.h"
 #include "vec3.h"
+#include "sync.h"
+#include "common.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -50,7 +52,7 @@ static void config_key(const char *s, int i, int d)
     config_set_d(i, d);
 
     for (c = 0; c < SDLK_LAST; c++)
-        if (strcmp(s, SDL_GetKeyName(c)) == 0)
+        if (strcmp(s, SDL_GetKeyName((SDLKey) c)) == 0)
         {
             config_set_d(i, c);
             break;
@@ -61,8 +63,8 @@ static void config_key(const char *s, int i, int d)
 
 void config_init(void)
 {
-    memset(option_d, 0, CONFIG_OPTION_D_COUNT * sizeof (int));
-    memset(option_s, 0, CONFIG_OPTION_S_COUNT * sizeof (char *));
+    memset(option_d, 0, sizeof (option_d));
+    memset(option_s, 0, sizeof (option_s));
 
     config_set_d(CONFIG_FULLSCREEN,           DEFAULT_FULLSCREEN);
     config_set_d(CONFIG_WIDTH,                DEFAULT_WIDTH);
@@ -72,12 +74,15 @@ void config_init(void)
     config_set_d(CONFIG_TEXTURES,             DEFAULT_TEXTURES);
     config_set_d(CONFIG_GEOMETRY,             DEFAULT_GEOMETRY);
     config_set_d(CONFIG_REFLECTION,           DEFAULT_REFLECTION);
+    config_set_d(CONFIG_MULTISAMPLE,          DEFAULT_MULTISAMPLE);
+    config_set_d(CONFIG_MIPMAP,               DEFAULT_MIPMAP);
+    config_set_d(CONFIG_ANISO,                DEFAULT_ANISO);
     config_set_d(CONFIG_BACKGROUND,           DEFAULT_BACKGROUND);
     config_set_d(CONFIG_SHADOW,               DEFAULT_SHADOW);
-    config_set_d(CONFIG_AUDIO_RATE,           DEFAULT_AUDIO_RATE);
     config_set_d(CONFIG_AUDIO_BUFF,           DEFAULT_AUDIO_BUFF);
     config_set_d(CONFIG_MOUSE_SENSE,          DEFAULT_MOUSE_SENSE);
     config_set_d(CONFIG_MOUSE_INVERT,         DEFAULT_MOUSE_INVERT);
+    config_set_d(CONFIG_VSYNC,                DEFAULT_VSYNC);
     config_set_d(CONFIG_NICE,                 DEFAULT_NICE);
     config_set_d(CONFIG_FPS,                  DEFAULT_FPS);
     config_set_d(CONFIG_SOUND_VOLUME,         DEFAULT_SOUND_VOLUME);
@@ -94,6 +99,10 @@ void config_init(void)
     config_set_d(CONFIG_JOYSTICK_CAMERA_1,    DEFAULT_JOYSTICK_CAMERA_1);
     config_set_d(CONFIG_JOYSTICK_CAMERA_2,    DEFAULT_JOYSTICK_CAMERA_2);
     config_set_d(CONFIG_JOYSTICK_CAMERA_3,    DEFAULT_JOYSTICK_CAMERA_3);
+    config_set_d(CONFIG_JOYSTICK_DPAD_L,      DEFAULT_JOYSTICK_DPAD_L);
+    config_set_d(CONFIG_JOYSTICK_DPAD_R,      DEFAULT_JOYSTICK_DPAD_R);
+    config_set_d(CONFIG_JOYSTICK_DPAD_U,      DEFAULT_JOYSTICK_DPAD_U);
+    config_set_d(CONFIG_JOYSTICK_DPAD_D,      DEFAULT_JOYSTICK_DPAD_D);
     config_set_d(CONFIG_KEY_CAMERA_1,         DEFAULT_KEY_CAMERA_1);
     config_set_d(CONFIG_KEY_CAMERA_2,         DEFAULT_KEY_CAMERA_2);
     config_set_d(CONFIG_KEY_CAMERA_3,         DEFAULT_KEY_CAMERA_3);
@@ -105,11 +114,79 @@ void config_init(void)
     config_set_d(CONFIG_VIEW_DZ,              DEFAULT_VIEW_DZ);
     config_set_d(CONFIG_ROTATE_FAST,          DEFAULT_ROTATE_FAST);
     config_set_d(CONFIG_ROTATE_SLOW,          DEFAULT_ROTATE_SLOW);
-    config_set_d(CONFIG_LAST_SET,             DEFAULT_LAST_SET);
     config_set_s(CONFIG_PLAYER,               DEFAULT_PLAYER);
     config_set_s(CONFIG_BALL,                 DEFAULT_BALL);
-    config_set_s(CONFIG_COIN,                 DEFAULT_COIN);
-    config_set_s(CONFIG_LANG,                 DEFAULT_LANG);
+    config_set_s(CONFIG_WIIMOTE_ADDR,         DEFAULT_WIIMOTE_ADDR);
+    config_set_s(CONFIG_REPLAY_NAME,          DEFAULT_REPLAY_NAME);
+    config_set_d(CONFIG_CHEAT,                DEFAULT_CHEAT);
+    config_set_d(CONFIG_STATS,                DEFAULT_STATS);
+    config_set_d(CONFIG_UNIFORM,              DEFAULT_UNIFORM);
+    config_set_d(CONFIG_KEY_FORWARD,          DEFAULT_KEY_FORWARD);
+    config_set_d(CONFIG_KEY_BACKWARD,         DEFAULT_KEY_BACKWARD);
+    config_set_d(CONFIG_KEY_LEFT,             DEFAULT_KEY_LEFT);
+    config_set_d(CONFIG_KEY_RIGHT,            DEFAULT_KEY_RIGHT);
+    config_set_d(CONFIG_KEY_PAUSE,            DEFAULT_KEY_PAUSE);
+    config_set_d(CONFIG_KEY_RESTART,          DEFAULT_KEY_RESTART);
+    config_set_d(CONFIG_KEY_SCORE_NEXT,       DEFAULT_KEY_SCORE_NEXT);
+    config_set_d(CONFIG_SCREENSHOT,           DEFAULT_SCREENSHOT);
+    config_set_d(CONFIG_LOCK_GOALS,           DEFAULT_LOCK_GOALS);
+}
+
+/*
+ * Scan a NUL-terminated string LINE according to the format
+ * '^<space>?<key><space><value>$' and store pointers to the start of key and
+ * value at DST_KEY and DST_VAL, respectively.  No memory is allocated to store
+ * the strings;  instead, the memory pointed to by LINE modified in-place as
+ * needed.
+ *
+ * Return 1 if LINE matches the format, return 0 otherwise.
+ */
+
+static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
+{
+    if (line)
+    {
+        char *key, *val, *space;
+
+        for (key = line; *key && isspace(*key); key++);
+
+        if (*key)
+        {
+            if (dst_key)
+                *dst_key = key;
+        }
+        else
+            return 0;
+
+        for (space = key; *space && !isspace(*space); space++);
+
+        if (*space)
+        {
+            /* NUL-terminate the key, if necessary. */
+
+            if (dst_key)
+            {
+                *space = '\0';
+                space++;
+            }
+        }
+        else
+            return 0;
+
+        for (val = space; *val && isspace(*val); val++);
+
+        if (*val)
+        {
+            if (dst_val)
+                *dst_val = val;
+        }
+        else
+            return 0;
+
+        return 1;
+    }
+
+    return 0;
 }
 
 void config_load(void)
@@ -118,12 +195,11 @@ void config_load(void)
 
     if ((fp = fopen(config_user(USER_CONFIG_FILE), "r")))
     {
-        char buf[MAXSTR];
-        char key[MAXSTR];
-        char val[MAXSTR];
+        char *line, *key, *val;
 
-        while (fgets(buf, MAXSTR, fp))
-            if (sscanf(buf, "%s %s", key, val) == 2)
+        while (read_line(&line, fp))
+        {
+            if (scan_key_and_value(&key, &val, line))
             {
                 if      (strcmp(key, "fullscreen")            == 0)
                     config_set_d(CONFIG_FULLSCREEN,           atoi(val));
@@ -141,18 +217,24 @@ void config_load(void)
                     config_set_d(CONFIG_GEOMETRY,             atoi(val));
                 else if (strcmp(key, "reflection")            == 0)
                     config_set_d(CONFIG_REFLECTION,           atoi(val));
+                else if (strcmp(key, "multisample")           == 0)
+                    config_set_d(CONFIG_MULTISAMPLE,          atoi(val));
+                else if (strcmp(key, "mipmap")                == 0)
+                    config_set_d(CONFIG_MIPMAP,               atoi(val));
+                else if (strcmp(key, "aniso")                 == 0)
+                    config_set_d(CONFIG_ANISO,                atoi(val));
                 else if (strcmp(key, "background")            == 0)
                     config_set_d(CONFIG_BACKGROUND,           atoi(val));
                 else if (strcmp(key, "shadow")                == 0)
                     config_set_d(CONFIG_SHADOW,               atoi(val));
-                else if (strcmp(key, "audio_rate")            == 0)
-                    config_set_d(CONFIG_AUDIO_RATE,           atoi(val));
                 else if (strcmp(key, "audio_buff")            == 0)
                     config_set_d(CONFIG_AUDIO_BUFF,           atoi(val));
                 else if (strcmp(key, "mouse_sense")           == 0)
                     config_set_d(CONFIG_MOUSE_SENSE,          atoi(val));
                 else if (strcmp(key, "mouse_invert")          == 0)
                     config_set_d(CONFIG_MOUSE_INVERT,         atoi(val));
+                else if (strcmp(key, "vsync")                 == 0)
+                    config_set_d(CONFIG_VSYNC,                atoi(val));
                 else if (strcmp(key, "nice")                  == 0)
                     config_set_d(CONFIG_NICE,                 atoi(val));
                 else if (strcmp(key, "fps")                   == 0)
@@ -197,8 +279,15 @@ void config_load(void)
                     config_set_d(CONFIG_ROTATE_FAST,          atoi(val));
                 else if (strcmp(key, "rotate_slow")           == 0)
                     config_set_d(CONFIG_ROTATE_SLOW,          atoi(val));
-                else if (strcmp(key, "last_set")              == 0)
-                    config_set_d(CONFIG_LAST_SET,             atoi(val));
+
+                else if (strcmp(key, "key_forward")  == 0)
+                    config_key(val, CONFIG_KEY_FORWARD, DEFAULT_KEY_FORWARD);
+                else if (strcmp(key, "key_backward")  == 0)
+                    config_key(val, CONFIG_KEY_BACKWARD, DEFAULT_KEY_BACKWARD);
+                else if (strcmp(key, "key_left")  == 0)
+                    config_key(val, CONFIG_KEY_LEFT, DEFAULT_KEY_LEFT);
+                else if (strcmp(key, "key_right")  == 0)
+                    config_key(val, CONFIG_KEY_RIGHT, DEFAULT_KEY_RIGHT);
 
                 else if (strcmp(key, "key_camera_1")  == 0)
                     config_key(val, CONFIG_KEY_CAMERA_1, DEFAULT_KEY_CAMERA_1);
@@ -211,16 +300,38 @@ void config_load(void)
                 else if (strcmp(key, "key_camera_l")  == 0)
                     config_key(val, CONFIG_KEY_CAMERA_L, DEFAULT_KEY_CAMERA_L);
 
+                else if (strcmp(key, "key_pause")    == 0)
+                    config_key(val, CONFIG_KEY_PAUSE,   DEFAULT_KEY_PAUSE);
+                else if (strcmp(key, "key_restart")  == 0)
+                    config_key(val, CONFIG_KEY_RESTART, DEFAULT_KEY_RESTART);
+
+                else if (strcmp(key, "key_score_next") == 0)
+                    config_key(val, CONFIG_KEY_SCORE_NEXT, DEFAULT_KEY_SCORE_NEXT);
+
                 else if (strcmp(key, "player") == 0)
                     config_set_s(CONFIG_PLAYER, val);
-                else if (strcmp(key, "ball")   == 0)
-                    config_set_s(CONFIG_BALL,   val);
-                else if (strcmp(key, "coin")   == 0)
-                    config_set_s(CONFIG_COIN,   val);
-                else if (strcmp(key, "lang")   == 0)
-                    config_set_s(CONFIG_LANG,   val);
+                else if (strcmp(key, "ball_file") == 0)
+                    config_set_s(CONFIG_BALL, val);
+                else if (strcmp(key, "wiimote_addr") == 0)
+                    config_set_s(CONFIG_WIIMOTE_ADDR, val);
+                else if (strcmp(key, "replay_name") == 0)
+                    config_set_s(CONFIG_REPLAY_NAME, val);
+
+                else if (strcmp(key, "cheat")   == 0)
+                    config_set_d(CONFIG_CHEAT, atoi(val));
+                else if (strcmp(key, "stats")   == 0)
+                    config_set_d(CONFIG_STATS, atoi(val));
+                else if (strcmp(key, "uniform") == 0)
+                    config_set_d(CONFIG_UNIFORM, atoi(val));
+                else if (strcmp(key, "screenshot") == 0)
+                    config_set_d(CONFIG_SCREENSHOT, atoi(val));
+                else if (strcmp(key, "lock_goals") == 0)
+                    config_set_d(CONFIG_LOCK_GOALS, atoi(val));
             }
 
+            free(line);
+        }
+
         fclose(fp);
 
         dirty = 0;
@@ -249,18 +360,24 @@ void config_save(void)
                 option_d[CONFIG_GEOMETRY]);
         fprintf(fp, "reflection           %d\n",
                 option_d[CONFIG_REFLECTION]);
+        fprintf(fp, "multisample          %d\n",
+                option_d[CONFIG_MULTISAMPLE]);
+        fprintf(fp, "mipmap               %d\n",
+                option_d[CONFIG_MIPMAP]);
+        fprintf(fp, "aniso                %d\n",
+                option_d[CONFIG_ANISO]);
         fprintf(fp, "background           %d\n",
                 option_d[CONFIG_BACKGROUND]);
         fprintf(fp, "shadow               %d\n",
                 option_d[CONFIG_SHADOW]);
-        fprintf(fp, "audio_rate           %d\n",
-                option_d[CONFIG_AUDIO_RATE]);
         fprintf(fp, "audio_buff           %d\n",
                 option_d[CONFIG_AUDIO_BUFF]);
         fprintf(fp, "mouse_sense          %d\n",
                 option_d[CONFIG_MOUSE_SENSE]);
         fprintf(fp, "mouse_invert         %d\n",
                 option_d[CONFIG_MOUSE_INVERT]);
+        fprintf(fp, "vsync                %d\n",
+                option_d[CONFIG_VSYNC]);
         fprintf(fp, "nice                 %d\n",
                 option_d[CONFIG_NICE]);
         fprintf(fp, "fps                  %d\n",
@@ -305,24 +422,51 @@ void config_save(void)
                 option_d[CONFIG_ROTATE_FAST]);
         fprintf(fp, "rotate_slow          %d\n",
                 option_d[CONFIG_ROTATE_SLOW]);
-        fprintf(fp, "last_set             %d\n",
-                option_d[CONFIG_LAST_SET]);
+
+        fprintf(fp, "key_forward          %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_FORWARD]));
+        fprintf(fp, "key_backward         %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_BACKWARD]));
+        fprintf(fp, "key_left             %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_LEFT]));
+        fprintf(fp, "key_right            %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_RIGHT]));
 
         fprintf(fp, "key_camera_1         %s\n",
-                SDL_GetKeyName(option_d[CONFIG_KEY_CAMERA_1]));
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_1]));
         fprintf(fp, "key_camera_2         %s\n",
-                SDL_GetKeyName(option_d[CONFIG_KEY_CAMERA_2]));
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_2]));
         fprintf(fp, "key_camera_3         %s\n",
-                SDL_GetKeyName(option_d[CONFIG_KEY_CAMERA_3]));
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_3]));
         fprintf(fp, "key_camera_r         %s\n",
-                SDL_GetKeyName(option_d[CONFIG_KEY_CAMERA_R]));
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_R]));
         fprintf(fp, "key_camera_l         %s\n",
-                SDL_GetKeyName(option_d[CONFIG_KEY_CAMERA_L]));
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_CAMERA_L]));
+
+        fprintf(fp, "key_pause            %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_PAUSE]));
+        fprintf(fp, "key_restart          %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_RESTART]));
+
+        fprintf(fp, "key_score_next       %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_SCORE_NEXT]));
 
-        fprintf(fp, "player               %s\n", option_s[CONFIG_PLAYER]);
-        fprintf(fp, "ball                 %s\n", option_s[CONFIG_BALL]);
-        fprintf(fp, "coin                 %s\n", option_s[CONFIG_COIN]);
-        fprintf(fp, "lang                 %s\n", option_s[CONFIG_LANG]);
+        if (strlen(option_s[CONFIG_PLAYER]) > 0)
+            fprintf(fp, "player       %s\n", option_s[CONFIG_PLAYER]);
+        if (strlen(option_s[CONFIG_BALL]) > 0)
+            fprintf(fp, "ball_file    %s\n", option_s[CONFIG_BALL]);
+        if (strlen(option_s[CONFIG_WIIMOTE_ADDR]) > 0)
+            fprintf(fp, "wiimote_addr %s\n", option_s[CONFIG_WIIMOTE_ADDR]);
+        if (strlen(option_s[CONFIG_REPLAY_NAME]) > 0)
+            fprintf(fp, "replay_name  %s\n", option_s[CONFIG_REPLAY_NAME]);
+
+        fprintf(fp, "stats                %d\n", option_d[CONFIG_STATS]);
+        fprintf(fp, "uniform              %d\n", option_d[CONFIG_UNIFORM]);
+        fprintf(fp, "screenshot           %d\n", option_d[CONFIG_SCREENSHOT]);
+        fprintf(fp, "lock_goals           %d\n", option_d[CONFIG_LOCK_GOALS]);
+
+        if (config_cheat())
+            fprintf(fp, "cheat                %d\n", option_d[CONFIG_CHEAT]);
 
         fclose(fp);
     }
@@ -332,22 +476,46 @@ void config_save(void)
 
 /*---------------------------------------------------------------------------*/
 
-int config_mode(int f, int w, int h)
+int check_extension(const char *needle)
 {
-    int stereo  = config_get_d(CONFIG_STEREO)     ? 1 : 0;
-    int stencil = config_get_d(CONFIG_REFLECTION) ? 1 : 0;
+    const GLubyte *haystack, *c;
+
+    /* Search for the given string in the OpenGL extension strings. */
+
+    for (haystack = glGetString(GL_EXTENSIONS); *haystack; haystack++)
+    {
+        for (c = (const GLubyte *) needle; *c && *haystack; c++, haystack++)
+            if (*c != *haystack)
+                break;
+
+        if ((*c == 0) && (*haystack == ' ' || *haystack == '\0'))
+            return 1;
+    }
+
+    return 0;
+}
 
-    SDL_GL_SetAttribute(SDL_GL_STEREO,       stereo);
-    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencil);
+int config_mode(int f, int w, int h)
+{
+    int stereo  = config_get_d(CONFIG_STEREO)      ? 1 : 0;
+    int stencil = config_get_d(CONFIG_REFLECTION)  ? 1 : 0;
+    int buffers = config_get_d(CONFIG_MULTISAMPLE) ? 1 : 0;
+    int samples = config_get_d(CONFIG_MULTISAMPLE);
+    int vsync   = config_get_d(CONFIG_VSYNC)       ? 1 : 0;
+
+    SDL_GL_SetAttribute(SDL_GL_STEREO,             stereo);
+    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,       stencil);
+    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, buffers);
+    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
+    SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL,       vsync);
 
     /* Try to set the currently specified mode. */
 
     if (SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (f ? SDL_FULLSCREEN : 0)))
     {
-        option_d[CONFIG_FULLSCREEN] = f;
-        option_d[CONFIG_WIDTH]      = w;
-        option_d[CONFIG_HEIGHT]     = h;
-        option_d[CONFIG_SHADOW]     = option_d[CONFIG_SHADOW];
+        config_set_d(CONFIG_FULLSCREEN, f);
+        config_set_d(CONFIG_WIDTH,      w);
+        config_set_d(CONFIG_HEIGHT,     h);
 
         glViewport(0, 0, w, h);
         glClearColor(0.0f, 0.0f, 0.1f, 0.0f);
@@ -357,6 +525,33 @@ int config_mode(int f, int w, int h)
         glEnable(GL_DEPTH_TEST);
         glEnable(GL_TEXTURE_2D);
         glEnable(GL_LIGHTING);
+        glEnable(GL_BLEND);
+
+        glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,
+                      GL_SEPARATE_SPECULAR_COLOR);
+        glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
+
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glDepthFunc(GL_LEQUAL);
+
+        /*
+         * Mac OS X might still need this, because apparently SDL doesn't do
+         * SDL_GL_SWAP_CONTROL on OS X.  TODO: investigate.
+         */
+#if 0
+        if (vsync) sync_init();
+#endif
+
+        /* If GL supports multisample, and SDL got a multisample buffer... */
+
+#ifdef GL_ARB_multisample
+        if (check_extension("ARB_multisample"))
+        {
+            SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
+            if (buffers)
+                glEnable(GL_MULTISAMPLE_ARB);
+        }
+#endif
 
         return 1;
     }
@@ -369,6 +564,14 @@ int config_mode(int f, int w, int h)
         return config_mode(f, w, h);
     }
 
+    /* If the mode failed, try decreasing the level of multisampling. */
+
+    else if (buffers)
+    {
+        config_set_d(CONFIG_MULTISAMPLE, samples / 2);
+        return config_mode(f, w, h);
+    }
+
     /* If that mode failed, try it without reflections. */
 
     else if (stencil)
@@ -384,123 +587,56 @@ int config_mode(int f, int w, int h)
 
 /*---------------------------------------------------------------------------*/
 
-static char data_path[MAXSTR];
-static char user_path[MAXSTR];
+static float ms     = 0;
+static int   fps    = 0;
+static int   last   = 0;
+static int   ticks  = 0;
+static int   frames = 0;
 
-/*
- * Given  a path  and a  file name  relative to  that path,  create an
- * absolute path name and return a temporary pointer to it.
- */
-static const char *config_file(const char *path, const char *file)
+int  config_perf(void)
 {
-    static char absolute[MAXSTR];
-
-    size_t d = strlen(path);
-
-    strncpy(absolute, path, MAXSTR - 1);
-    strncat(absolute, "/",  MAXSTR - d - 1);
-    strncat(absolute, file, MAXSTR - d - 2);
-
-    return absolute;
+    return fps;
 }
 
-static int config_test(const char *path, const char *file)
+void config_swap(void)
 {
-    if (file)
-    {
-        FILE *fp;
+    int dt;
 
-        if ((fp = fopen(config_file(path, file), "r")))
-        {
-            fclose(fp);
-            return 1;
-        }
-        return 0;
-    }
-    return 1;
-}
+    SDL_GL_SwapBuffers();
 
-const char *config_data(const char *file)
-{
-    return config_file(data_path, file);
-}
+    /* Accumulate time passed and frames rendered. */
 
-const char *config_user(const char *file)
-{
-    return config_file(user_path, file);
-}
+    dt = (int) SDL_GetTicks() - last;
 
-/*---------------------------------------------------------------------------*/
+    frames +=  1;
+    ticks  += dt;
+    last   += dt;
 
-/*
- * Attempt to find  the game data directory.  Search  the command line
- * paramater,  the environment,  and the  hard-coded default,  in that
- * order.  Confirm it by checking for presense of the named file.
- */
-int config_data_path(const char *path, const char *file)
-{
-    char *dir;
+    /* Average over 250ms. */
 
-    if (path && config_test(path, file))
+    if (ticks > 1000)
     {
-        strncpy(data_path, path, MAXSTR);
-        return 1;
-    }
+        /* Round the frames-per-second value to the nearest integer. */
 
-    if ((dir = getenv("NEVERBALL_DATA")) && config_test(dir, file))
-    {
-        strncpy(data_path, dir, MAXSTR);
-        return 1;
-    }
+        double k = 1000.0 * frames / ticks;
+        double f = floor(k);
+        double c = ceil (k);
 
-    if (CONFIG_DATA && config_test(CONFIG_DATA, file))
-    {
-        strncpy(data_path, CONFIG_DATA, MAXSTR);
-        return 1;
-    }
+        /* Compute frame time and frames-per-second stats. */
 
-    return 0;
-}
+        fps = (int) ((c - k < k - f) ? c : f);
+        ms  = (float) ticks / (float) frames;
 
-/*
- * Determine the location of  the user's home directory.  Ensure there
- * is a  directory there for  storing configuration, high  scores, and
- * replays.
- *
- * HACK: under Windows just assume the user has permission to write to
- * the data  directory.  This is  more reliable than trying  to devine
- * anything reasonable from the environment.
- */
-int config_user_path(const char *file)
-{
-#ifdef _WIN32
-    size_t d = strlen(CONFIG_USER);
-
-    strncpy(user_path, data_path,   MAXSTR - 1);
-    strncat(user_path, "\\",        MAXSTR - d - 1);
-    strncat(user_path, CONFIG_USER, MAXSTR - d - 2);
+        /* Reset the counters for the next update. */
 
-    if ((mkdir(user_path) == 0) || (errno = EEXIST))
-        if (config_test(user_path, file))
-            return 1;
-#else
-    char *dir;
+        frames = 0;
+        ticks  = 0;
 
-    if ((dir = getenv("HOME")))
-    {
-        size_t d = strlen(dir);
+        /* Output statistics if configured. */
 
-        strncpy(user_path, getenv("HOME"), MAXSTR - 1);
-        strncat(user_path, "/",            MAXSTR - d - 1);
-        strncat(user_path, CONFIG_USER,    MAXSTR - d - 2);
+        if (option_d[CONFIG_STATS])
+            fprintf(stdout, "%4d %8.4f\n", fps, ms);
     }
-
-    if ((mkdir(user_path, 0777) == 0) || (errno = EEXIST))
-        if (config_test(user_path, file))
-            return 1;
-#endif
-
-    return 0;
 }
 
 /*---------------------------------------------------------------------------*/
@@ -514,6 +650,7 @@ void config_set_d(int i, int d)
 void config_tgl_d(int i)
 {
     option_d[i] = (option_d[i] ? 0 : 1);
+    dirty = 1;
 }
 
 int config_tst_d(int i, int d)
@@ -546,22 +683,25 @@ void config_get_s(int i, char *dst, int len)
     strncpy(dst, option_s[i], len);
 }
 
-const char * config_simple_get_s(int i)
-{
-       return option_s[i];
-}
-
 /*---------------------------------------------------------------------------*/
 
 static int grabbed = 0;
-static int paused  = 0;
 
-void config_set_grab(void)
+void config_set_grab(int w)
 {
-    SDL_WarpMouse(config_get_d(CONFIG_WIDTH)  / 2,
-                  config_get_d(CONFIG_HEIGHT) / 2);
+    if (w)
+    {
+        SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+
+        SDL_WarpMouse(config_get_d(CONFIG_WIDTH)  / 2,
+                      config_get_d(CONFIG_HEIGHT) / 2);
+
+        SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
+    }
+
     SDL_WM_GrabInput(SDL_GRAB_ON);
     SDL_ShowCursor(SDL_DISABLE);
+
     grabbed = 1;
 }
 
@@ -577,42 +717,30 @@ int  config_get_grab(void)
     return grabbed;
 }
 
-int  config_get_pause(void)
+/*---------------------------------------------------------------------------*/
+
+int config_cheat(void)
 {
-    return paused;
+    return config_get_d(CONFIG_CHEAT);
 }
 
-void config_set_pause(void)
+void config_set_cheat(void)
 {
-    Mix_PauseMusic();
-    paused = 1;
-
-    if (grabbed)
-    {
-        SDL_ShowCursor(SDL_ENABLE);
-        SDL_WM_GrabInput(SDL_GRAB_OFF);
-    }
+    config_set_d(CONFIG_CHEAT, 1);
 }
 
-void config_clr_pause(void)
+void config_clr_cheat(void)
 {
-    Mix_ResumeMusic();
-    paused = 0;
-
-    if (grabbed)
-    {
-        SDL_WM_GrabInput(SDL_GRAB_ON);
-        SDL_ShowCursor(SDL_DISABLE);
-    }
+    config_set_d(CONFIG_CHEAT, 0);
 }
 
-void config_tgl_pause(void)
+/*---------------------------------------------------------------------------*/
+
+int config_screenshot(void)
 {
-    if (paused)
-        config_clr_pause();
-    else
-        config_set_pause();
+    return ++option_d[CONFIG_SCREENSHOT];
 }
+
 /*---------------------------------------------------------------------------*/
 
 void config_push_persp(float fov, float n, float f)
@@ -623,7 +751,7 @@ void config_push_persp(float fov, float n, float f)
     GLdouble s = sin(r);
     GLdouble c = cos(r) / s;
 
-    GLdouble a = ((GLdouble) option_d[CONFIG_WIDTH] / 
+    GLdouble a = ((GLdouble) option_d[CONFIG_WIDTH] /
                   (GLdouble) option_d[CONFIG_HEIGHT]);
 
     glMatrixMode(GL_PROJECTION);