shithub: puzzles

Download patch

ref: cc545532268b04e05005bf65f53f0a1ba39335ba
parent: 46fa25240e2e1609c4431a639f6e9c4bb6f50b39
author: Simon Tatham <anakin@pobox.com>
date: Mon Jan 17 08:48:57 EST 2005

Rather than each game backend file exporting a whole load of
functions and a couple of variables, now each one exports a single
structure containing a load of function pointers and said variables.
This should make it easy to support platforms on which it's sensible
to compile all the puzzles into a single monolithic application. The
two existing platforms are still one-binary-per-game.

[originally from svn r5126]

--- a/cube.c
+++ b/cube.c
@@ -11,10 +11,6 @@
 
 #include "puzzles.h"
 
-const char *const game_name = "Cube";
-const char *const game_winhelp_topic = "games.cube";
-const int game_can_configure = TRUE;
-
 #define MAXVERTICES 20
 #define MAXFACES 20
 #define MAXORDER 4
@@ -215,7 +211,7 @@
     int movecount;
 };
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -226,7 +222,7 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret = snew(game_params);
     char *str;
@@ -266,12 +262,12 @@
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -278,7 +274,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
 
@@ -299,7 +295,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char data[256];
 
@@ -477,7 +473,7 @@
         return d1*d1 + d2*d2 + 4*d1*d2;
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret = snewn(4, config_item);
     char buf[80];
@@ -507,7 +503,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -533,7 +529,7 @@
     classes[thisclass]++;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     int classes[5];
     int i;
@@ -593,7 +589,7 @@
 	data->squareindex++;
 }
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     struct grid_data data;
     int i, j, k, m, area, facesperclass;
@@ -842,7 +838,7 @@
     return ret;
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     int area = grid_area(params->d1, params->d2, solids[params->solid]->order);
     int i, j;
@@ -870,7 +866,7 @@
     return NULL;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     game_state *state = snew(game_state);
     int area;
@@ -949,7 +945,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -978,21 +974,22 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state);
 }
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     return NULL;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui,
+			     int x, int y, int button)
 {
     int direction;
     int pkey[2], skey[2], dkey[2];
@@ -1309,7 +1306,7 @@
     return bb;
 }
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     struct bbox bb = find_bbox(params);
     *x = (int)((bb.r - bb.l + 2*solids[params->solid]->border) * GRID_SCALE);
@@ -1316,7 +1313,7 @@
     *y = (int)((bb.d - bb.u + 2*solids[params->solid]->border) * GRID_SCALE);
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -1334,7 +1331,7 @@
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
     struct bbox bb = find_bbox(&state->params);
@@ -1345,12 +1342,12 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -1512,17 +1509,52 @@
     }
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     return ROLLTIME;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return TRUE;
 }
+
+#ifdef COMBINED
+#define thegame cube
+#endif
+
+const struct game thegame = {
+    "Cube", "games.cube", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/fifteen.c
+++ b/fifteen.c
@@ -11,10 +11,6 @@
 
 #include "puzzles.h"
 
-const char *const game_name = "Fifteen";
-const char *const game_winhelp_topic = "games.fifteen";
-const int game_can_configure = TRUE;
-
 #define TILE_SIZE 48
 #define BORDER    (TILE_SIZE / 2)
 #define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
@@ -48,7 +44,7 @@
     int movecount;
 };
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -57,17 +53,17 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     return FALSE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -74,7 +70,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
 
@@ -88,7 +84,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char data[256];
 
@@ -97,7 +93,7 @@
     return dupstr(data);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -124,7 +120,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -134,7 +130,7 @@
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->w < 2 && params->h < 2)
 	return "Width and height must both be at least two";
@@ -142,7 +138,7 @@
     return NULL;
 }
 
-int perm_parity(int *perm, int n)
+static int perm_parity(int *perm, int n)
 {
     int i, j, ret;
 
@@ -156,7 +152,7 @@
     return ret;
 }
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     int gap, n, i, x;
     int x1, x2, p1, p2, parity;
@@ -271,7 +267,7 @@
     return ret;
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     char *p, *err;
     int i, area;
@@ -322,7 +318,7 @@
     return err;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     game_state *state = snew(game_state);
     int i;
@@ -353,7 +349,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -369,21 +365,22 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state);
 }
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     return NULL;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui,
+			     int x, int y, int button)
 {
     int gx, gy, dx, dy, ux, uy, up, p;
     game_state *ret;
@@ -457,13 +454,13 @@
     int *tiles;
 };
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = TILE_SIZE * params->w + 2 * BORDER;
     *y = TILE_SIZE * params->h + 2 * BORDER;
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
     int i;
@@ -494,7 +491,7 @@
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
     int i;
@@ -510,7 +507,7 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->tiles);
     sfree(ds);
@@ -552,7 +549,7 @@
     draw_update(fe, x, y, TILE_SIZE, TILE_SIZE);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -702,12 +699,14 @@
     }
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     return ANIM_TIME;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     if (!oldstate->completed && newstate->completed)
         return 2 * FLASH_FRAME;
@@ -715,7 +714,40 @@
         return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return TRUE;
 }
+
+#ifdef COMBINED
+#define thegame fifteen
+#endif
+
+const struct game thegame = {
+    "Fifteen", "games.fifteen", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/gtk.c
+++ b/gtk.c
@@ -783,7 +783,7 @@
 
     fe = snew(frontend);
 
-    fe->me = midend_new(fe);
+    fe->me = midend_new(fe, &thegame);
     if (game_id) {
         *error = midend_game_id(fe->me, game_id, FALSE);
         if (*error) {
@@ -795,7 +795,7 @@
     midend_new_game(fe->me);
 
     fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-    gtk_window_set_title(GTK_WINDOW(fe->window), game_name);
+    gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name);
 #if 0
     gtk_window_set_resizable(GTK_WINDOW(fe->window), FALSE);
 #else
@@ -827,7 +827,7 @@
 		       GTK_SIGNAL_FUNC(menu_config_event), fe);
     gtk_widget_show(menuitem);
 
-    if ((n = midend_num_presets(fe->me)) > 0 || game_can_configure) {
+    if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) {
         GtkWidget *submenu;
         int i;
 
@@ -852,7 +852,7 @@
             gtk_widget_show(menuitem);
         }
 
-	if (game_can_configure) {
+	if (thegame.can_configure) {
             menuitem = gtk_menu_item_new_with_label("Custom...");
             gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
 				GPOINTER_TO_INT(CFG_SETTINGS));
--- a/midend.c
+++ b/midend.c
@@ -14,6 +14,7 @@
 struct midend_data {
     frontend *frontend;
     random_state *random;
+    const game *ourgame;
 
     char *seed;
     int fresh_seed;
@@ -40,7 +41,7 @@
     } \
 } while (0)
 
-midend_data *midend_new(frontend *fe)
+midend_data *midend_new(frontend *fe, const game *ourgame)
 {
     midend_data *me = snew(midend_data);
     void *randseed;
@@ -49,10 +50,11 @@
     get_random_seed(&randseed, &randseedsize);
 
     me->frontend = fe;
+    me->ourgame = ourgame;
     me->random = random_init(randseed, randseedsize);
     me->nstates = me->statesize = me->statepos = 0;
     me->states = NULL;
-    me->params = default_params();
+    me->params = ourgame->default_params();
     me->seed = NULL;
     me->fresh_seed = FALSE;
     me->drawstate = NULL;
@@ -74,53 +76,53 @@
 {
     sfree(me->states);
     sfree(me->seed);
-    free_params(me->params);
+    me->ourgame->free_params(me->params);
     sfree(me);
 }
 
 void midend_size(midend_data *me, int *x, int *y)
 {
-    game_size(me->params, x, y);
+    me->ourgame->size(me->params, x, y);
 }
 
 void midend_set_params(midend_data *me, game_params *params)
 {
-    free_params(me->params);
-    me->params = dup_params(params);
+    me->ourgame->free_params(me->params);
+    me->params = me->ourgame->dup_params(params);
 }
 
 void midend_new_game(midend_data *me)
 {
     while (me->nstates > 0)
-	free_game(me->states[--me->nstates]);
+	me->ourgame->free_game(me->states[--me->nstates]);
 
     if (me->drawstate)
-        game_free_drawstate(me->drawstate);
+        me->ourgame->free_drawstate(me->drawstate);
 
     assert(me->nstates == 0);
 
     if (!me->fresh_seed) {
 	sfree(me->seed);
-	me->seed = new_game_seed(me->params, me->random);
+	me->seed = me->ourgame->new_seed(me->params, me->random);
     } else
 	me->fresh_seed = FALSE;
 
     ensure(me);
-    me->states[me->nstates++] = new_game(me->params, me->seed);
+    me->states[me->nstates++] = me->ourgame->new_game(me->params, me->seed);
     me->statepos = 1;
-    me->drawstate = game_new_drawstate(me->states[0]);
+    me->drawstate = me->ourgame->new_drawstate(me->states[0]);
     if (me->ui)
-        free_ui(me->ui);
-    me->ui = new_ui(me->states[0]);
+        me->ourgame->free_ui(me->ui);
+    me->ui = me->ourgame->new_ui(me->states[0]);
 }
 
 void midend_restart_game(midend_data *me)
 {
     while (me->nstates > 1)
-	free_game(me->states[--me->nstates]);
+	me->ourgame->free_game(me->states[--me->nstates]);
     me->statepos = me->nstates;
-    free_ui(me->ui);
-    me->ui = new_ui(me->states[0]);
+    me->ourgame->free_ui(me->ui);
+    me->ui = me->ourgame->new_ui(me->states[0]);
 }
 
 static int midend_undo(midend_data *me)
@@ -148,10 +150,10 @@
     float flashtime;
 
     if (me->oldstate || me->statepos > 1) {
-	flashtime = game_flash_length(me->oldstate ? me->oldstate :
-				      me->states[me->statepos-2],
-				      me->states[me->statepos-1],
-                                      me->oldstate ? me->dir : +1);
+	flashtime = me->ourgame->flash_length(me->oldstate ? me->oldstate :
+					      me->states[me->statepos-2],
+					      me->states[me->statepos-1],
+					      me->oldstate ? me->dir : +1);
 	if (flashtime > 0) {
 	    me->flash_pos = 0.0F;
 	    me->flash_time = flashtime;
@@ -159,7 +161,7 @@
     }
 
     if (me->oldstate)
-	free_game(me->oldstate);
+	me->ourgame->free_game(me->oldstate);
     me->oldstate = NULL;
     me->anim_pos = me->anim_time = 0;
     me->dir = 0;
@@ -180,7 +182,7 @@
 
 int midend_process_key(midend_data *me, int x, int y, int button)
 {
-    game_state *oldstate = dup_game(me->states[me->statepos - 1]);
+    game_state *oldstate = me->ourgame->dup_game(me->states[me->statepos - 1]);
     float anim_time;
 
     if (button == 'n' || button == 'N' || button == '\x0E') {
@@ -203,11 +205,11 @@
 	if (!midend_redo(me))
             return 1;
     } else if (button == 'q' || button == 'Q' || button == '\x11') {
-	free_game(oldstate);
+	me->ourgame->free_game(oldstate);
         return 0;
     } else {
-        game_state *s = make_move(me->states[me->statepos-1], me->ui,
-                                  x, y, button);
+        game_state *s = me->ourgame->make_move(me->states[me->statepos-1],
+					       me->ui, x, y, button);
 
         if (s == me->states[me->statepos-1]) {
             /*
@@ -220,13 +222,13 @@
         } else if (s) {
 	    midend_stop_anim(me);
             while (me->nstates > me->statepos)
-                free_game(me->states[--me->nstates]);
+                me->ourgame->free_game(me->states[--me->nstates]);
             ensure(me);
             me->states[me->nstates] = s;
             me->statepos = ++me->nstates;
             me->dir = +1;
         } else {
-            free_game(oldstate);
+            me->ourgame->free_game(oldstate);
             return 1;
         }
     }
@@ -234,7 +236,8 @@
     /*
      * See if this move requires an animation.
      */
-    anim_time = game_anim_length(oldstate, me->states[me->statepos-1], me->dir);
+    anim_time = me->ourgame->anim_length(oldstate, me->states[me->statepos-1],
+					 me->dir);
 
     me->oldstate = oldstate;
     if (anim_time > 0) {
@@ -259,13 +262,13 @@
         if (me->oldstate && me->anim_time > 0 &&
             me->anim_pos < me->anim_time) {
             assert(me->dir != 0);
-            game_redraw(me->frontend, me->drawstate, me->oldstate,
-                        me->states[me->statepos-1], me->dir,
-                        me->ui, me->anim_pos, me->flash_pos);
+            me->ourgame->redraw(me->frontend, me->drawstate, me->oldstate,
+				me->states[me->statepos-1], me->dir,
+				me->ui, me->anim_pos, me->flash_pos);
         } else {
-            game_redraw(me->frontend, me->drawstate, NULL,
-                        me->states[me->statepos-1], +1 /*shrug*/,
-                        me->ui, 0.0, me->flash_pos);
+            me->ourgame->redraw(me->frontend, me->drawstate, NULL,
+				me->states[me->statepos-1], +1 /*shrug*/,
+				me->ui, 0.0, me->flash_pos);
         }
         end_draw(me->frontend);
     }
@@ -294,16 +297,16 @@
     float *ret;
 
     if (me->nstates == 0) {
-        char *seed = new_game_seed(me->params, me->random);
-        state = new_game(me->params, seed);
+        char *seed = me->ourgame->new_seed(me->params, me->random);
+        state = me->ourgame->new_game(me->params, seed);
         sfree(seed);
     } else
         state = me->states[0];
 
-    ret = game_colours(me->frontend, state, ncolours);
+    ret = me->ourgame->colours(me->frontend, state, ncolours);
 
     if (me->nstates == 0)
-        free_game(state);
+        me->ourgame->free_game(state);
 
     return ret;
 }
@@ -314,7 +317,7 @@
         char *name;
         game_params *preset;
 
-        while (game_fetch_preset(me->npresets, &name, &preset)) {
+        while (me->ourgame->fetch_preset(me->npresets, &name, &preset)) {
             if (me->presetsize <= me->npresets) {
                 me->presetsize = me->npresets + 10;
                 me->presets = sresize(me->presets, me->presetsize,
@@ -342,7 +345,7 @@
 
 int midend_wants_statusbar(midend_data *me)
 {
-    return game_wants_statusbar();
+    return me->ourgame->wants_statusbar();
 }
 
 config_item *midend_get_config(midend_data *me, int which, char **wintitle)
@@ -350,15 +353,15 @@
     char *titlebuf, *parstr;
     config_item *ret;
 
-    titlebuf = snewn(40 + strlen(game_name), char);
+    titlebuf = snewn(40 + strlen(me->ourgame->name), char);
 
     switch (which) {
       case CFG_SETTINGS:
-	sprintf(titlebuf, "%s configuration", game_name);
+	sprintf(titlebuf, "%s configuration", me->ourgame->name);
 	*wintitle = dupstr(titlebuf);
-	return game_configure(me->params);
+	return me->ourgame->configure(me->params);
       case CFG_SEED:
-	sprintf(titlebuf, "%s game selection", game_name);
+	sprintf(titlebuf, "%s game selection", me->ourgame->name);
 	*wintitle = dupstr(titlebuf);
 
 	ret = snewn(2, config_item);
@@ -371,7 +374,7 @@
          * parameters, plus a colon, plus the game seed. This is a
          * full game ID.
          */
-        parstr = encode_params(me->params);
+        parstr = me->ourgame->encode_params(me->params);
         ret[0].sval = snewn(strlen(parstr) + strlen(me->seed) + 2, char);
         sprintf(ret[0].sval, "%s:%s", parstr, me->seed);
         sfree(parstr);
@@ -417,18 +420,18 @@
     }
 
     if (par) {
-        params = decode_params(par);
-        error = validate_params(params);
+        params = me->ourgame->decode_params(par);
+        error = me->ourgame->validate_params(params);
         if (error) {
-            free_params(params);
+            me->ourgame->free_params(params);
             return error;
         }
-        free_params(me->params);
+        me->ourgame->free_params(me->params);
         me->params = params;
     }
 
     if (seed) {
-        error = validate_seed(me->params, seed);
+        error = me->ourgame->validate_seed(me->params, seed);
         if (error)
             return error;
 
@@ -447,15 +450,15 @@
 
     switch (which) {
       case CFG_SETTINGS:
-	params = custom_params(cfg);
-	error = validate_params(params);
+	params = me->ourgame->custom_params(cfg);
+	error = me->ourgame->validate_params(params);
 
 	if (error) {
-	    free_params(params);
+	    me->ourgame->free_params(params);
 	    return error;
 	}
 
-	free_params(me->params);
+	me->ourgame->free_params(me->params);
 	me->params = params;
 	break;
 
--- a/net.c
+++ b/net.c
@@ -12,10 +12,6 @@
 #include "puzzles.h"
 #include "tree234.h"
 
-const char *const game_name = "Net";
-const char *const game_winhelp_topic = "games.net";
-const int game_can_configure = TRUE;
-
 #define PI 3.141592653589793238462643383279502884197169399
 
 #define MATMUL(xr,yr,m,x,y) do { \
@@ -127,7 +123,7 @@
 /* ----------------------------------------------------------------------
  * Manage game parameters.
  */
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -139,7 +135,7 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     char str[80];
@@ -173,12 +169,12 @@
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -185,7 +181,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
     char const *p = string;
@@ -207,7 +203,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char ret[400];
     int len;
@@ -223,7 +219,7 @@
     return dupstr(ret);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -261,7 +257,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -273,7 +269,7 @@
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->width <= 0 && params->height <= 0)
 	return "Width and height must both be greater than zero";
@@ -294,7 +290,7 @@
  * Randomly select a new game seed.
  */
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     /*
      * The full description of a Net game is far too large to
@@ -312,7 +308,7 @@
     return dupstr(buf);
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     /*
      * Since any string at all will suffice to seed the RNG, there
@@ -325,7 +321,7 @@
  * Construct an initial game state, given a seed and parameters.
  */
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     random_state *rs;
     game_state *state;
@@ -676,7 +672,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret;
 
@@ -696,7 +692,7 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state->tiles);
     sfree(state->barriers);
@@ -770,7 +766,7 @@
     random_state *rs; /* used for jumbling */
 };
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     void *seed;
     int seedsize;
@@ -785,7 +781,7 @@
     return ui;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
     random_free(ui->rs);
     sfree(ui);
@@ -794,7 +790,8 @@
 /* ----------------------------------------------------------------------
  * Process a move.
  */
-game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *state, game_ui *ui,
+			     int x, int y, int button)
 {
     game_state *ret, *nullret;
     int tx, ty, orig;
@@ -951,7 +948,7 @@
     unsigned char *visible;
 };
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     game_drawstate *ds = snew(game_drawstate);
 
@@ -964,19 +961,19 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
 }
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER;
     *y = WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER;
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret;
 
@@ -1284,7 +1281,7 @@
     draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui, float t, float ft)
 {
     int x, y, tx, ty, frame, last_rotate_dir;
@@ -1436,7 +1433,8 @@
     sfree(active);
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     int x, y, last_rotate_dir;
 
@@ -1462,7 +1460,8 @@
     return 0.0F;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     /*
      * If the game has just been completed, we display a completion
@@ -1485,7 +1484,40 @@
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return TRUE;
 }
+
+#ifdef COMBINED
+#define thegame net
+#endif
+
+const struct game thegame = {
+    "Net", "games.net", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/netslide.c
+++ b/netslide.c
@@ -13,10 +13,6 @@
 #include "puzzles.h"
 #include "tree234.h"
 
-const char *const game_name = "Netslide";
-const char *const game_winhelp_topic = "games.netslide";
-const int game_can_configure = TRUE;
-
 #define PI 3.141592653589793238462643383279502884197169399
 
 #define MATMUL(xr,yr,m,x,y) do { \
@@ -139,13 +135,13 @@
     return xyd;
 }
 
-void slide_col(game_state *state, int dir, int col);
-void slide_row(game_state *state, int dir, int row);
+static void slide_col(game_state *state, int dir, int col);
+static void slide_row(game_state *state, int dir, int row);
 
 /* ----------------------------------------------------------------------
  * Manage game parameters.
  */
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -157,7 +153,7 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     char str[80];
@@ -190,12 +186,12 @@
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -202,7 +198,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
     char const *p = string;
@@ -227,7 +223,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char ret[400];
     int len;
@@ -243,7 +239,7 @@
     return dupstr(ret);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -281,7 +277,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -293,7 +289,7 @@
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->width <= 1 && params->height <= 1)
 	return "Width and height must both be greater than one";
@@ -312,7 +308,7 @@
  * Randomly select a new game seed.
  */
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     /*
      * The full description of a Net game is far too large to
@@ -330,7 +326,7 @@
     return dupstr(buf);
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     /*
      * Since any string at all will suffice to seed the RNG, there
@@ -343,7 +339,7 @@
  * Construct an initial game state, given a seed and parameters.
  */
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     random_state *rs;
     game_state *state;
@@ -714,7 +710,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret;
 
@@ -737,7 +733,7 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state->tiles);
     sfree(state->barriers);
@@ -815,7 +811,7 @@
     int cur_visible;
 };
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     game_ui *ui = snew(game_ui);
     ui->cur_x = state->width / 2;
@@ -825,7 +821,7 @@
     return ui;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
     sfree(ui);
 }
@@ -834,7 +830,7 @@
  * Process a move.
  */
 
-void slide_row(game_state *state, int dir, int row)
+static void slide_row(game_state *state, int dir, int row)
 {
     int x = dir > 0 ? -1 : state->width;
     int tx = x + dir;
@@ -848,7 +844,7 @@
     state->tiles[T(state, tx, row)] = endtile;
 }
 
-void slide_col(game_state *state, int dir, int col)
+static void slide_col(game_state *state, int dir, int col)
 {
     int y = dir > 0 ? -1 : state->height;
     int ty = y + dir;
@@ -862,7 +858,8 @@
     state->tiles[T(state, col, ty)] = endtile;
 }
 
-game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *state, game_ui *ui,
+			     int x, int y, int button)
 {
     int cx, cy;
     int n, dx, dy;
@@ -945,7 +942,7 @@
     unsigned char *visible;
 };
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     game_drawstate *ds = snew(game_drawstate);
 
@@ -958,19 +955,19 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
 }
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER;
     *y = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER;
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret;
 
@@ -1286,7 +1283,7 @@
     draw_polygon(fe, coords, 7, FALSE, COL_TEXT);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui, float t, float ft)
 {
     int x, y, tx, ty, frame;
@@ -1481,12 +1478,14 @@
     sfree(active);
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     return ANIM_TIME;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     /*
      * If the game has just been completed, we display a completion
@@ -1509,7 +1508,40 @@
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return TRUE;
 }
+
+#ifdef COMBINED
+#define thegame netslide
+#endif
+
+const struct game thegame = {
+    "Netslide", "games.netslide", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/nullgame.c
+++ b/nullgame.c
@@ -20,10 +20,6 @@
 
 #include "puzzles.h"
 
-const char *const game_name = "Null Game";
-const char *const game_winhelp_topic = NULL;
-const int game_can_configure = FALSE;
-
 enum {
     COL_BACKGROUND,
     NCOLOURS
@@ -37,7 +33,7 @@
     int FIXME;
 };
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -46,17 +42,17 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     return FALSE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -63,7 +59,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = snew(game_params);
 
@@ -72,37 +68,37 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     return dupstr("FIXME");
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     return NULL;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     return NULL;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     return NULL;
 }
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     return dupstr("FIXME");
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     return NULL;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     game_state *state = snew(game_state);
 
@@ -111,7 +107,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -120,21 +116,22 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state);
 }
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     return NULL;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui, int x, int y,
+			     int button)
 {
     return NULL;
 }
@@ -147,12 +144,12 @@
     int FIXME;
 };
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = *y = 200;                     /* FIXME */
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -162,7 +159,7 @@
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
 
@@ -171,14 +168,14 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
-                 game_state *state, int dir, game_ui *ui,
-                 float animtime, float flashtime)
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+			game_state *state, int dir, game_ui *ui,
+			float animtime, float flashtime)
 {
     /*
      * The initial contents of the window are not guaranteed and
@@ -189,17 +186,52 @@
     draw_rect(fe, 0, 0, 200, 200, COL_BACKGROUND);
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate, game_state *newstate,
+			      int dir)
 {
     return 0.0F;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate, game_state *newstate,
+			       int dir)
 {
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return FALSE;
 }
+
+#ifdef COMBINED
+#define thegame nullgame
+#endif
+
+const struct game thegame = {
+    "Null Game", NULL, FALSE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/pattern.c
+++ b/pattern.c
@@ -18,10 +18,6 @@
 #define max(x,y) ( (x)>(y) ? (x):(y) )
 #define min(x,y) ( (x)<(y) ? (x):(y) )
 
-const char *const game_name = "Pattern";
-const char *const game_winhelp_topic = "games.pattern";
-const int game_can_configure = TRUE;
-
 enum {
     COL_BACKGROUND,
     COL_EMPTY,
@@ -61,7 +57,7 @@
 
 #define FLASH_TIME 0.13F
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -70,7 +66,7 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     char str[80];
@@ -96,12 +92,12 @@
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -108,7 +104,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
     char const *p = string;
@@ -126,7 +122,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char ret[400];
     int len;
@@ -138,7 +134,7 @@
     return dupstr(ret);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -165,7 +161,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -175,7 +171,7 @@
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->w <= 0 && params->h <= 0)
 	return "Width and height must both be greater than zero";
@@ -315,7 +311,7 @@
     sfree(fgrid);
 }
 
-int compute_rowdata(int *ret, unsigned char *start, int len, int step)
+static int compute_rowdata(int *ret, unsigned char *start, int len, int step)
 {
     int i, n;
 
@@ -443,7 +439,7 @@
     return grid;
 }
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     unsigned char *grid;
     int i, j, max, rowlen, *rowdata;
@@ -507,7 +503,7 @@
     return seed;
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     int i, n, rowspace;
     char *p;
@@ -549,7 +545,7 @@
     return NULL;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     int i;
     char *p;
@@ -584,7 +580,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -607,7 +603,7 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state->rowdata);
     sfree(state->rowlen);
@@ -624,7 +620,7 @@
     int drag, release, state;
 };
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     game_ui *ret;
 
@@ -634,12 +630,13 @@
     return ret;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
     sfree(ui);
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui,
+			     int x, int y, int button)
 {
     game_state *ret;
 
@@ -775,13 +772,13 @@
     unsigned char *visible;
 };
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = SIZE(params->w);
     *y = SIZE(params->h);
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -807,7 +804,7 @@
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
 
@@ -820,7 +817,7 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
@@ -848,7 +845,7 @@
                 TILE_SIZE, TILE_SIZE);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -957,12 +954,14 @@
     }
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     return 0.0F;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     if (!oldstate->completed && newstate->completed)
         return FLASH_TIME;
@@ -969,7 +968,40 @@
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return FALSE;
 }
+
+#ifdef COMBINED
+#define thegame pattern
+#endif
+
+const struct game thegame = {
+    "Pattern", "games.pattern", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/puzzles.h
+++ b/puzzles.h
@@ -44,6 +44,7 @@
 typedef struct game_state game_state;
 typedef struct game_ui game_ui;
 typedef struct game_drawstate game_drawstate;
+typedef struct game game;
 
 #define ALIGN_VNORMAL 0x000
 #define ALIGN_VCENTRE 0x100
@@ -111,7 +112,7 @@
 /*
  * midend.c
  */
-midend_data *midend_new(frontend *fe);
+midend_data *midend_new(frontend *fe, const game *ourgame);
 void midend_free(midend_data *me);
 void midend_set_params(midend_data *me, game_params *params);
 void midend_size(midend_data *me, int *x, int *y);
@@ -158,37 +159,52 @@
 void random_free(random_state *state);
 
 /*
- * Game-specific routines
+ * Data structure containing the function calls and data specific
+ * to a particular game. This is enclosed in a data structure so
+ * that a particular platform can choose, if it wishes, to compile
+ * all the games into a single combined executable rather than
+ * having lots of little ones.
  */
-extern const char *const game_name;
-extern const char *const game_winhelp_topic;
-const int game_can_configure;
-game_params *default_params(void);
-int game_fetch_preset(int i, char **name, game_params **params);
-game_params *decode_params(char const *string);
-char *encode_params(game_params *);
-void free_params(game_params *params);
-game_params *dup_params(game_params *params);
-config_item *game_configure(game_params *params);
-game_params *custom_params(config_item *cfg);
-char *validate_params(game_params *params);
-char *new_game_seed(game_params *params, random_state *rs);
-char *validate_seed(game_params *params, char *seed);
-game_state *new_game(game_params *params, char *seed);
-game_state *dup_game(game_state *state);
-void free_game(game_state *state);
-game_ui *new_ui(game_state *state);
-void free_ui(game_ui *ui);
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button);
-void game_size(game_params *params, int *x, int *y);
-float *game_colours(frontend *fe, game_state *state, int *ncolours);
-game_drawstate *game_new_drawstate(game_state *state);
-void game_free_drawstate(game_drawstate *ds);
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
-                 game_state *newstate, int dir, game_ui *ui, float anim_time,
-                 float flash_time);
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir);
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir);
-int game_wants_statusbar(void);
+struct game {
+    const char *name;
+    const char *winhelp_topic;
+    int can_configure;
+    game_params *(*default_params)(void);
+    int (*fetch_preset)(int i, char **name, game_params **params);
+    game_params *(*decode_params)(char const *string);
+    char *(*encode_params)(game_params *);
+    void (*free_params)(game_params *params);
+    game_params *(*dup_params)(game_params *params);
+    config_item *(*configure)(game_params *params);
+    game_params *(*custom_params)(config_item *cfg);
+    char *(*validate_params)(game_params *params);
+    char *(*new_seed)(game_params *params, random_state *rs);
+    char *(*validate_seed)(game_params *params, char *seed);
+    game_state *(*new_game)(game_params *params, char *seed);
+    game_state *(*dup_game)(game_state *state);
+    void (*free_game)(game_state *state);
+    game_ui *(*new_ui)(game_state *state);
+    void (*free_ui)(game_ui *ui);
+    game_state *(*make_move)(game_state *from, game_ui *ui, int x, int y,
+			     int button);
+    void (*size)(game_params *params, int *x, int *y);
+    float *(*colours)(frontend *fe, game_state *state, int *ncolours);
+    game_drawstate *(*new_drawstate)(game_state *state);
+    void (*free_drawstate)(game_drawstate *ds);
+    void (*redraw)(frontend *fe, game_drawstate *ds, game_state *oldstate,
+		   game_state *newstate, int dir, game_ui *ui, float anim_time,
+		   float flash_time);
+    float (*anim_length)(game_state *oldstate, game_state *newstate, int dir);
+    float (*flash_length)(game_state *oldstate, game_state *newstate, int dir);
+    int (*wants_statusbar)(void);
+};
+
+/*
+ * For one-game-at-a-time platforms, there's a single structure
+ * like the above, under a fixed name.
+ */
+#ifndef COMBINED
+extern const game thegame;
+#endif
 
 #endif /* PUZZLES_PUZZLES_H */
--- a/rect.c
+++ b/rect.c
@@ -39,10 +39,6 @@
 
 #include "puzzles.h"
 
-const char *const game_name = "Rectangles";
-const char *const game_winhelp_topic = "games.rectangles";
-const int game_can_configure = TRUE;
-
 enum {
     COL_BACKGROUND,
     COL_CORRECT,
@@ -89,7 +85,7 @@
     int completed;
 };
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -99,7 +95,7 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     int w, h;
@@ -122,12 +118,12 @@
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -134,7 +130,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
 
@@ -154,7 +150,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char data[256];
 
@@ -163,7 +159,7 @@
     return dupstr(data);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -196,7 +192,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -207,7 +203,7 @@
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->w <= 0 && params->h <= 0)
 	return "Width and height must both be greater than zero";
@@ -390,7 +386,7 @@
 }
 #endif
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     int *grid, *numbers;
     struct rectlist *list;
@@ -902,7 +898,7 @@
     return seed;
 }
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     int area = params->w * params->h;
     int squares = 0;
@@ -930,7 +926,7 @@
     return NULL;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     game_state *state = snew(game_state);
     int x, y, i, area;
@@ -973,7 +969,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -993,7 +989,7 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state->grid);
     sfree(state->vedge);
@@ -1118,7 +1114,7 @@
     int dragged;
 };
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     game_ui *ui = snew(game_ui);
     ui->drag_start_x = -1;
@@ -1129,12 +1125,12 @@
     return ui;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
     sfree(ui);
 }
 
-void coord_round(float x, float y, int *xr, int *yr)
+static void coord_round(float x, float y, int *xr, int *yr)
 {
     float xs, ys, xv, yv, dx, dy, dist;
 
@@ -1265,7 +1261,8 @@
             }
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui,
+			     int x, int y, int button)
 {
     int xc, yc;
     int startdrag = FALSE, enddrag = FALSE, active = FALSE;
@@ -1374,13 +1371,13 @@
     unsigned int *visible;
 };
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = params->w * TILE_SIZE + 2*BORDER + 1;
     *y = params->h * TILE_SIZE + 2*BORDER + 1;
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
 
@@ -1410,7 +1407,7 @@
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
     int i;
@@ -1425,13 +1422,13 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
 }
 
-void draw_tile(frontend *fe, game_state *state, int x, int y,
+static void draw_tile(frontend *fe, game_state *state, int x, int y,
                unsigned char *hedge, unsigned char *vedge,
 	       unsigned char *corners, int correct)
 {
@@ -1487,7 +1484,7 @@
     draw_update(fe, cx, cy, TILE_SIZE+1, TILE_SIZE+1);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -1579,12 +1576,14 @@
     sfree(correct);
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     return 0.0F;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     if (!oldstate->completed && newstate->completed)
         return FLASH_TIME;
@@ -1591,7 +1590,40 @@
     return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return FALSE;
 }
+
+#ifdef COMBINED
+#define thegame rect
+#endif
+
+const struct game thegame = {
+    "Rectangles", "games.rectangles", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/sixteen.c
+++ b/sixteen.c
@@ -13,10 +13,6 @@
 
 #include "puzzles.h"
 
-const char *const game_name = "Sixteen";
-const char *const game_winhelp_topic = "games.sixteen";
-const int game_can_configure = TRUE;
-
 #define TILE_SIZE 48
 #define BORDER    TILE_SIZE            /* big border to fill with arrows */
 #define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
@@ -50,7 +46,7 @@
     int last_movement_sense;
 };
 
-game_params *default_params(void)
+static game_params *default_params(void)
 {
     game_params *ret = snew(game_params);
 
@@ -59,7 +55,7 @@
     return ret;
 }
 
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
 {
     game_params *ret;
     int w, h;
@@ -82,12 +78,12 @@
     return TRUE;
 }
 
-void free_params(game_params *params)
+static void free_params(game_params *params)
 {
     sfree(params);
 }
 
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
 {
     game_params *ret = snew(game_params);
     *ret = *params;		       /* structure copy */
@@ -94,7 +90,7 @@
     return ret;
 }
 
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
 {
     game_params *ret = default_params();
 
@@ -108,7 +104,7 @@
     return ret;
 }
 
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
 {
     char data[256];
 
@@ -117,7 +113,7 @@
     return dupstr(data);
 }
 
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
 {
     config_item *ret;
     char buf[80];
@@ -144,7 +140,7 @@
     return ret;
 }
 
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
 {
     game_params *ret = snew(game_params);
 
@@ -154,7 +150,7 @@
     return ret;
 }
 
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
 {
     if (params->w < 2 && params->h < 2)
 	return "Width and height must both be at least two";
@@ -162,7 +158,7 @@
     return NULL;
 }
 
-int perm_parity(int *perm, int n)
+static int perm_parity(int *perm, int n)
 {
     int i, j, ret;
 
@@ -176,7 +172,7 @@
     return ret;
 }
 
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
 {
     int stop, n, i, x;
     int x1, x2, p1, p2;
@@ -283,7 +279,7 @@
 }
 
 
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
 {
     char *p, *err;
     int i, area;
@@ -334,7 +330,7 @@
     return err;
 }
 
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
 {
     game_state *state = snew(game_state);
     int i;
@@ -362,7 +358,7 @@
     return state;
 }
 
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
 {
     game_state *ret = snew(game_state);
 
@@ -378,21 +374,22 @@
     return ret;
 }
 
-void free_game(game_state *state)
+static void free_game(game_state *state)
 {
     sfree(state);
 }
 
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
 {
     return NULL;
 }
 
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
 {
 }
 
-game_state *make_move(game_state *from, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *from, game_ui *ui,
+			     int x, int y, int button)
 {
     int cx, cy;
     int dx, dy, tx, ty, n;
@@ -458,13 +455,13 @@
     int *tiles;
 };
 
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
 {
     *x = TILE_SIZE * params->w + 2 * BORDER;
     *y = TILE_SIZE * params->h + 2 * BORDER;
 }
 
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
     int i;
@@ -495,7 +492,7 @@
     return ret;
 }
 
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
 {
     struct game_drawstate *ds = snew(struct game_drawstate);
     int i;
@@ -511,7 +508,7 @@
     return ds;
 }
 
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
 {
     sfree(ds->tiles);
     sfree(ds);
@@ -574,7 +571,7 @@
     draw_polygon(fe, coords, 7, FALSE, COL_TEXT);
 }
 
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui,
                  float animtime, float flashtime)
 {
@@ -752,12 +749,14 @@
     }
 }
 
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_anim_length(game_state *oldstate,
+			      game_state *newstate, int dir)
 {
     return ANIM_TIME;
 }
 
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+			       game_state *newstate, int dir)
 {
     if (!oldstate->completed && newstate->completed)
         return 2 * FLASH_FRAME;
@@ -765,7 +764,36 @@
         return 0.0F;
 }
 
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
 {
     return TRUE;
 }
+
+const struct game thegame = {
+    "Sixteen", "games.sixteen", TRUE,
+    default_params,
+    game_fetch_preset,
+    decode_params,
+    encode_params,
+    free_params,
+    dup_params,
+    game_configure,
+    custom_params,
+    validate_params,
+    new_game_seed,
+    validate_seed,
+    new_game,
+    dup_game,
+    free_game,
+    new_ui,
+    free_ui,
+    make_move,
+    game_size,
+    game_colours,
+    game_new_drawstate,
+    game_free_drawstate,
+    game_redraw,
+    game_anim_length,
+    game_flash_length,
+    game_wants_statusbar,
+};
--- a/windows.c
+++ b/windows.c
@@ -1,5 +1,14 @@
 /*
  * windows.c: Windows front end for my puzzle collection.
+ * 
+ * TODO:
+ * 
+ *  - Figure out what to do if a puzzle requests a size bigger than
+ *    the screen will take. In principle we could put scrollbars in
+ *    the window, although that would be pretty horrid. Another
+ *    option is to detect in advance that this will be a problem -
+ *    we can probably tell this using midend_size() before actually
+ *    generating the puzzle - and simply refuse to do it.
  */
 
 #include <windows.h>
@@ -363,7 +372,7 @@
 
     fe = snew(frontend);
 
-    fe->me = midend_new(fe);
+    fe->me = midend_new(fe, &thegame);
 
     if (game_id) {
         *error = midend_game_id(fe->me, game_id, FALSE);
@@ -412,7 +421,7 @@
 		       (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED),
 		       TRUE, 0);
 
-    fe->hwnd = CreateWindowEx(0, game_name, game_name,
+    fe->hwnd = CreateWindowEx(0, thegame.name, thegame.name,
 			      WS_OVERLAPPEDWINDOW &~
 			      (WS_THICKFRAME | WS_MAXIMIZEBOX),
 			      CW_USEDEFAULT, CW_USEDEFAULT,
@@ -429,7 +438,7 @@
 	AppendMenu(menu, MF_ENABLED, IDM_SEED, "Specific...");
 
 	if ((fe->npresets = midend_num_presets(fe->me)) > 0 ||
-	    game_can_configure) {
+	    thegame.can_configure) {
 	    HMENU sub = CreateMenu();
 	    int i;
 
@@ -450,7 +459,7 @@
 		AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name);
 	    }
 
-	    if (game_can_configure) {
+	    if (thegame.can_configure) {
 		AppendMenu(sub, MF_ENABLED, IDM_CONFIG, "Custom...");
 	    }
 	}
@@ -464,11 +473,11 @@
             HMENU hmenu = CreateMenu();
             AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)hmenu, "Help");
             AppendMenu(hmenu, MF_ENABLED, IDM_HELPC, "Contents");
-            if (game_winhelp_topic) {
+            if (thegame.winhelp_topic) {
                 char *item;
-                assert(game_name);
-                item = snewn(9+strlen(game_name), char); /*ick*/
-                sprintf(item, "Help on %s", game_name);
+                assert(thegame.name);
+                item = snewn(9+strlen(thegame.name), char); /*ick*/
+                sprintf(item, "Help on %s", thegame.name);
                 AppendMenu(hmenu, MF_ENABLED, IDM_GAMEHELP, item);
                 sfree(item);
             }
@@ -911,10 +920,10 @@
             break;
           case IDM_GAMEHELP:
             assert(fe->help_path);
-            assert(game_winhelp_topic);
+            assert(thegame.winhelp_topic);
             {
-                char *cmd = snewn(10+strlen(game_winhelp_topic), char); /*ick*/
-                sprintf(cmd, "JI(`',`%s')", game_winhelp_topic);
+                char *cmd = snewn(10+strlen(thegame.winhelp_topic), char);
+                sprintf(cmd, "JI(`',`%s')", thegame.winhelp_topic);
                 WinHelp(hwnd, fe->help_path, HELP_COMMAND, (DWORD)cmd);
                 sfree(cmd);
             }
@@ -1107,7 +1116,7 @@
 	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 	wndclass.hbrBackground = NULL;
 	wndclass.lpszMenuName = NULL;
-	wndclass.lpszClassName = game_name;
+	wndclass.lpszClassName = thegame.name;
 
 	RegisterClass(&wndclass);
     }
@@ -1117,7 +1126,7 @@
 
     if (!new_window(inst, *cmdline ? cmdline : NULL, &error)) {
 	char buf[128];
-	sprintf(buf, "%.100s Error", game_name);
+	sprintf(buf, "%.100s Error", thegame.name);
 	MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR);
 	return 1;
     }