chiark / gitweb /
midend_deserialise: keep deserialised data in a struct.
[sgt-puzzles.git] / midend.c
1 /*
2  * midend.c: general middle fragment sitting between the
3  * platform-specific front end and game-specific back end.
4  * Maintains a move list, takes care of Undo and Redo commands, and
5  * processes standard keystrokes for undo/redo/new/quit.
6  */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13
14 #include "puzzles.h"
15
16 enum { DEF_PARAMS, DEF_SEED, DEF_DESC };   /* for midend_game_id_int */
17
18 enum { NEWGAME, MOVE, SOLVE, RESTART };/* for midend_state_entry.movetype */
19
20 #define special(type) ( (type) != MOVE )
21
22 struct midend_state_entry {
23     game_state *state;
24     char *movestr;
25     int movetype;
26 };
27
28 struct midend {
29     frontend *frontend;
30     random_state *random;
31     const game *ourgame;
32
33     struct preset_menu *preset_menu;
34     char **encoded_presets; /* for midend_which_preset to check against */
35     int n_encoded_presets;
36
37     /*
38      * `desc' and `privdesc' deserve a comment.
39      * 
40      * `desc' is the game description as presented to the user when
41      * they ask for Game -> Specific. `privdesc', if non-NULL, is a
42      * different game description used to reconstruct the initial
43      * game_state when de-serialising. If privdesc is NULL, `desc'
44      * is used for both.
45      * 
46      * For almost all games, `privdesc' is NULL and never used. The
47      * exception (as usual) is Mines: the initial game state has no
48      * squares open at all, but after the first click `desc' is
49      * rewritten to describe a game state with an initial click and
50      * thus a bunch of squares open. If we used that desc to
51      * serialise and deserialise, then the initial game state after
52      * deserialisation would look unlike the initial game state
53      * beforehand, and worse still execute_move() might fail on the
54      * attempted first click. So `privdesc' is also used in this
55      * case, to provide a game description describing the same
56      * fixed mine layout _but_ no initial click. (These game IDs
57      * may also be typed directly into Mines if you like.)
58      */
59     char *desc, *privdesc, *seedstr;
60     char *aux_info;
61     enum { GOT_SEED, GOT_DESC, GOT_NOTHING } genmode;
62
63     int nstates, statesize, statepos;
64     struct midend_state_entry *states;
65
66     game_params *params, *curparams;
67     game_drawstate *drawstate;
68     game_ui *ui;
69
70     game_state *oldstate;
71     float anim_time, anim_pos;
72     float flash_time, flash_pos;
73     int dir;
74
75     int timing;
76     float elapsed;
77     char *laststatus;
78
79     drawing *drawing;
80
81     int pressed_mouse_button;
82
83     int preferred_tilesize, tilesize, winwidth, winheight;
84
85     void (*game_id_change_notify_function)(void *);
86     void *game_id_change_notify_ctx;
87 };
88
89 #define ensure(me) do { \
90     if ((me)->nstates >= (me)->statesize) { \
91         (me)->statesize = (me)->nstates + 128; \
92         (me)->states = sresize((me)->states, (me)->statesize, \
93                                struct midend_state_entry); \
94     } \
95 } while (0)
96
97 /*
98  * Structure storing all the decoded data from reading a serialised
99  * game. We keep it in one of these while we check its sanity, and
100  * only once we're completely satisfied do we install it all in the
101  * midend structure proper.
102  */
103 struct deserialise_data {
104     char *seed, *parstr, *desc, *privdesc;
105     char *auxinfo, *uistr, *cparstr;
106     float elapsed;
107     game_params *params, *cparams;
108     game_ui *ui;
109     struct midend_state_entry *states;
110     int nstates, statepos;
111 };
112
113 void midend_reset_tilesize(midend *me)
114 {
115     me->preferred_tilesize = me->ourgame->preferred_tilesize;
116     {
117         /*
118          * Allow an environment-based override for the default tile
119          * size by defining a variable along the lines of
120          * `NET_TILESIZE=15'.
121          */
122
123         char buf[80], *e;
124         int j, k, ts;
125
126         sprintf(buf, "%s_TILESIZE", me->ourgame->name);
127         for (j = k = 0; buf[j]; j++)
128             if (!isspace((unsigned char)buf[j]))
129                 buf[k++] = toupper((unsigned char)buf[j]);
130         buf[k] = '\0';
131         if ((e = getenv(buf)) != NULL && sscanf(e, "%d", &ts) == 1 && ts > 0)
132             me->preferred_tilesize = ts;
133     }
134 }
135
136 midend *midend_new(frontend *fe, const game *ourgame,
137                    const drawing_api *drapi, void *drhandle)
138 {
139     midend *me = snew(midend);
140     void *randseed;
141     int randseedsize;
142
143     get_random_seed(&randseed, &randseedsize);
144
145     me->frontend = fe;
146     me->ourgame = ourgame;
147     me->random = random_new(randseed, randseedsize);
148     me->nstates = me->statesize = me->statepos = 0;
149     me->states = NULL;
150     me->params = ourgame->default_params();
151     me->game_id_change_notify_function = NULL;
152     me->game_id_change_notify_ctx = NULL;
153
154     /*
155      * Allow environment-based changing of the default settings by
156      * defining a variable along the lines of `NET_DEFAULT=25x25w'
157      * in which the value is an encoded parameter string.
158      */
159     {
160         char buf[80], *e;
161         int j, k;
162         sprintf(buf, "%s_DEFAULT", me->ourgame->name);
163         for (j = k = 0; buf[j]; j++)
164             if (!isspace((unsigned char)buf[j]))
165                 buf[k++] = toupper((unsigned char)buf[j]);
166         buf[k] = '\0';
167         if ((e = getenv(buf)) != NULL)
168             me->ourgame->decode_params(me->params, e);
169     }
170     me->curparams = NULL;
171     me->desc = me->privdesc = NULL;
172     me->seedstr = NULL;
173     me->aux_info = NULL;
174     me->genmode = GOT_NOTHING;
175     me->drawstate = NULL;
176     me->oldstate = NULL;
177     me->preset_menu = NULL;
178     me->anim_time = me->anim_pos = 0.0F;
179     me->flash_time = me->flash_pos = 0.0F;
180     me->dir = 0;
181     me->ui = NULL;
182     me->pressed_mouse_button = 0;
183     me->laststatus = NULL;
184     me->timing = FALSE;
185     me->elapsed = 0.0F;
186     me->tilesize = me->winwidth = me->winheight = 0;
187     if (drapi)
188         me->drawing = drawing_new(drapi, me, drhandle);
189     else
190         me->drawing = NULL;
191
192     midend_reset_tilesize(me);
193
194     sfree(randseed);
195
196     return me;
197 }
198
199 const game *midend_which_game(midend *me)
200 {
201     return me->ourgame;
202 }
203
204 static void midend_purge_states(midend *me)
205 {
206     while (me->nstates > me->statepos) {
207         me->ourgame->free_game(me->states[--me->nstates].state);
208         if (me->states[me->nstates].movestr)
209             sfree(me->states[me->nstates].movestr);
210     }
211 }
212
213 static void midend_free_game(midend *me)
214 {
215     while (me->nstates > 0) {
216         me->nstates--;
217         me->ourgame->free_game(me->states[me->nstates].state);
218         sfree(me->states[me->nstates].movestr);
219     }
220
221     if (me->drawstate)
222         me->ourgame->free_drawstate(me->drawing, me->drawstate);
223 }
224
225 static void midend_free_preset_menu(midend *me, struct preset_menu *menu)
226 {
227     if (menu) {
228         int i;
229         for (i = 0; i < menu->n_entries; i++) {
230             sfree(menu->entries[i].title);
231             if (menu->entries[i].params)
232                 me->ourgame->free_params(menu->entries[i].params);
233             midend_free_preset_menu(me, menu->entries[i].submenu);
234         }
235         sfree(menu->entries);
236         sfree(menu);
237     }
238 }
239
240 void midend_free(midend *me)
241 {
242     midend_free_game(me);
243
244     if (me->drawing)
245         drawing_free(me->drawing);
246     random_free(me->random);
247     sfree(me->states);
248     sfree(me->desc);
249     sfree(me->privdesc);
250     sfree(me->seedstr);
251     sfree(me->aux_info);
252     me->ourgame->free_params(me->params);
253     midend_free_preset_menu(me, me->preset_menu);
254     if (me->ui)
255         me->ourgame->free_ui(me->ui);
256     if (me->curparams)
257         me->ourgame->free_params(me->curparams);
258     sfree(me->laststatus);
259     sfree(me);
260 }
261
262 static void midend_size_new_drawstate(midend *me)
263 {
264     /*
265      * Don't even bother, if we haven't worked out our tile size
266      * anyway yet.
267      */
268     if (me->tilesize > 0) {
269         me->ourgame->compute_size(me->params, me->tilesize,
270                                   &me->winwidth, &me->winheight);
271         me->ourgame->set_size(me->drawing, me->drawstate,
272                               me->params, me->tilesize);
273     }
274 }
275
276 void midend_size(midend *me, int *x, int *y, int user_size)
277 {
278     int min, max;
279     int rx, ry;
280
281     /*
282      * We can't set the size on the same drawstate twice. So if
283      * we've already sized one drawstate, we must throw it away and
284      * create a new one.
285      */
286     if (me->drawstate && me->tilesize > 0) {
287         me->ourgame->free_drawstate(me->drawing, me->drawstate);
288         me->drawstate = me->ourgame->new_drawstate(me->drawing,
289                                                    me->states[0].state);
290     }
291
292     /*
293      * Find the tile size that best fits within the given space. If
294      * `user_size' is TRUE, we must actually find the _largest_ such
295      * tile size, in order to get as close to the user's explicit
296      * request as possible; otherwise, we bound above at the game's
297      * preferred tile size, so that the game gets what it wants
298      * provided that this doesn't break the constraint from the
299      * front-end (which is likely to be a screen size or similar).
300      */
301     if (user_size) {
302         max = 1;
303         do {
304             max *= 2;
305             me->ourgame->compute_size(me->params, max, &rx, &ry);
306         } while (rx <= *x && ry <= *y);
307     } else
308         max = me->preferred_tilesize + 1;
309     min = 1;
310
311     /*
312      * Now binary-search between min and max. We're looking for a
313      * boundary rather than a value: the point at which tile sizes
314      * stop fitting within the given dimensions. Thus, we stop when
315      * max and min differ by exactly 1.
316      */
317     while (max - min > 1) {
318         int mid = (max + min) / 2;
319         me->ourgame->compute_size(me->params, mid, &rx, &ry);
320         if (rx <= *x && ry <= *y)
321             min = mid;
322         else
323             max = mid;
324     }
325
326     /*
327      * Now `min' is a valid size, and `max' isn't. So use `min'.
328      */
329
330     me->tilesize = min;
331     if (user_size)
332         /* If the user requested a change in size, make it permanent. */
333         me->preferred_tilesize = me->tilesize;
334     midend_size_new_drawstate(me);
335     *x = me->winwidth;
336     *y = me->winheight;
337 }
338
339 int midend_tilesize(midend *me) { return me->tilesize; }
340
341 void midend_set_params(midend *me, game_params *params)
342 {
343     me->ourgame->free_params(me->params);
344     me->params = me->ourgame->dup_params(params);
345 }
346
347 game_params *midend_get_params(midend *me)
348 {
349     return me->ourgame->dup_params(me->params);
350 }
351
352 static void midend_set_timer(midend *me)
353 {
354     me->timing = (me->ourgame->is_timed &&
355                   me->ourgame->timing_state(me->states[me->statepos-1].state,
356                                             me->ui));
357     if (me->timing || me->flash_time || me->anim_time)
358         activate_timer(me->frontend);
359     else
360         deactivate_timer(me->frontend);
361 }
362
363 void midend_force_redraw(midend *me)
364 {
365     if (me->drawstate)
366         me->ourgame->free_drawstate(me->drawing, me->drawstate);
367     me->drawstate = me->ourgame->new_drawstate(me->drawing,
368                                                me->states[0].state);
369     midend_size_new_drawstate(me);
370     midend_redraw(me);
371 }
372
373 void midend_new_game(midend *me)
374 {
375     midend_stop_anim(me);
376     midend_free_game(me);
377
378     assert(me->nstates == 0);
379
380     if (me->genmode == GOT_DESC) {
381         me->genmode = GOT_NOTHING;
382     } else {
383         random_state *rs;
384
385         if (me->genmode == GOT_SEED) {
386             me->genmode = GOT_NOTHING;
387         } else {
388             /*
389              * Generate a new random seed. 15 digits comes to about
390              * 48 bits, which should be more than enough.
391              * 
392              * I'll avoid putting a leading zero on the number,
393              * just in case it confuses anybody who thinks it's
394              * processed as an integer rather than a string.
395              */
396             char newseed[16];
397             int i;
398             newseed[15] = '\0';
399             newseed[0] = '1' + (char)random_upto(me->random, 9);
400             for (i = 1; i < 15; i++)
401                 newseed[i] = '0' + (char)random_upto(me->random, 10);
402             sfree(me->seedstr);
403             me->seedstr = dupstr(newseed);
404
405             if (me->curparams)
406                 me->ourgame->free_params(me->curparams);
407             me->curparams = me->ourgame->dup_params(me->params);
408         }
409
410         sfree(me->desc);
411         sfree(me->privdesc);
412         sfree(me->aux_info);
413         me->aux_info = NULL;
414
415         rs = random_new(me->seedstr, strlen(me->seedstr));
416         /*
417          * If this midend has been instantiated without providing a
418          * drawing API, it is non-interactive. This means that it's
419          * being used for bulk game generation, and hence we should
420          * pass the non-interactive flag to new_desc.
421          */
422         me->desc = me->ourgame->new_desc(me->curparams, rs,
423                                          &me->aux_info, (me->drawing != NULL));
424         me->privdesc = NULL;
425         random_free(rs);
426     }
427
428     ensure(me);
429
430     /*
431      * It might seem a bit odd that we're using me->params to
432      * create the initial game state, rather than me->curparams
433      * which is better tailored to this specific game and which we
434      * always know.
435      * 
436      * It's supposed to be an invariant in the midend that
437      * me->params and me->curparams differ in no aspect that is
438      * important after generation (i.e. after new_desc()). By
439      * deliberately passing the _less_ specific of these two
440      * parameter sets, we provoke play-time misbehaviour in the
441      * case where a game has failed to encode a play-time parameter
442      * in the non-full version of encode_params().
443      */
444     me->states[me->nstates].state =
445         me->ourgame->new_game(me, me->params, me->desc);
446
447     /*
448      * As part of our commitment to self-testing, test the aux
449      * string to make sure nothing ghastly went wrong.
450      */
451     if (me->ourgame->can_solve && me->aux_info) {
452         game_state *s;
453         char *msg, *movestr;
454
455         msg = NULL;
456         movestr = me->ourgame->solve(me->states[0].state,
457                                      me->states[0].state,
458                                      me->aux_info, &msg);
459         assert(movestr && !msg);
460         s = me->ourgame->execute_move(me->states[0].state, movestr);
461         assert(s);
462         me->ourgame->free_game(s);
463         sfree(movestr);
464     }
465
466     me->states[me->nstates].movestr = NULL;
467     me->states[me->nstates].movetype = NEWGAME;
468     me->nstates++;
469     me->statepos = 1;
470     me->drawstate = me->ourgame->new_drawstate(me->drawing,
471                                                me->states[0].state);
472     midend_size_new_drawstate(me);
473     me->elapsed = 0.0F;
474     me->flash_pos = me->flash_time = 0.0F;
475     me->anim_pos = me->anim_time = 0.0F;
476     if (me->ui)
477         me->ourgame->free_ui(me->ui);
478     me->ui = me->ourgame->new_ui(me->states[0].state);
479     midend_set_timer(me);
480     me->pressed_mouse_button = 0;
481
482     if (me->game_id_change_notify_function)
483         me->game_id_change_notify_function(me->game_id_change_notify_ctx);
484 }
485
486 int midend_can_undo(midend *me)
487 {
488     return (me->statepos > 1);
489 }
490
491 int midend_can_redo(midend *me)
492 {
493     return (me->statepos < me->nstates);
494 }
495
496 static int midend_undo(midend *me)
497 {
498     if (me->statepos > 1) {
499         if (me->ui)
500             me->ourgame->changed_state(me->ui,
501                                        me->states[me->statepos-1].state,
502                                        me->states[me->statepos-2].state);
503         me->statepos--;
504         me->dir = -1;
505         return 1;
506     } else
507         return 0;
508 }
509
510 static int midend_redo(midend *me)
511 {
512     if (me->statepos < me->nstates) {
513         if (me->ui)
514             me->ourgame->changed_state(me->ui,
515                                        me->states[me->statepos-1].state,
516                                        me->states[me->statepos].state);
517         me->statepos++;
518         me->dir = +1;
519         return 1;
520     } else
521         return 0;
522 }
523
524 static void midend_finish_move(midend *me)
525 {
526     float flashtime;
527
528     /*
529      * We do not flash if the later of the two states is special.
530      * This covers both forward Solve moves and backward (undone)
531      * Restart moves.
532      */
533     if ((me->oldstate || me->statepos > 1) &&
534         ((me->dir > 0 && !special(me->states[me->statepos-1].movetype)) ||
535          (me->dir < 0 && me->statepos < me->nstates &&
536           !special(me->states[me->statepos].movetype)))) {
537         flashtime = me->ourgame->flash_length(me->oldstate ? me->oldstate :
538                                               me->states[me->statepos-2].state,
539                                               me->states[me->statepos-1].state,
540                                               me->oldstate ? me->dir : +1,
541                                               me->ui);
542         if (flashtime > 0) {
543             me->flash_pos = 0.0F;
544             me->flash_time = flashtime;
545         }
546     }
547
548     if (me->oldstate)
549         me->ourgame->free_game(me->oldstate);
550     me->oldstate = NULL;
551     me->anim_pos = me->anim_time = 0;
552     me->dir = 0;
553
554     midend_set_timer(me);
555 }
556
557 void midend_stop_anim(midend *me)
558 {
559     if (me->oldstate || me->anim_time != 0) {
560         midend_finish_move(me);
561         midend_redraw(me);
562     }
563 }
564
565 void midend_restart_game(midend *me)
566 {
567     game_state *s;
568
569     assert(me->statepos >= 1);
570     if (me->statepos == 1)
571         return;                        /* no point doing anything at all! */
572
573     /*
574      * During restart, we reconstruct the game from the (public)
575      * game description rather than from states[0], because that
576      * way Mines gets slightly more sensible behaviour (restart
577      * goes to _after_ the first click so you don't have to
578      * remember where you clicked).
579      */
580     s = me->ourgame->new_game(me, me->params, me->desc);
581
582     /*
583      * Now enter the restarted state as the next move.
584      */
585     midend_stop_anim(me);
586     midend_purge_states(me);
587     ensure(me);
588     me->states[me->nstates].state = s;
589     me->states[me->nstates].movestr = dupstr(me->desc);
590     me->states[me->nstates].movetype = RESTART;
591     me->statepos = ++me->nstates;
592     if (me->ui)
593         me->ourgame->changed_state(me->ui,
594                                    me->states[me->statepos-2].state,
595                                    me->states[me->statepos-1].state);
596     me->flash_pos = me->flash_time = 0.0F;
597     midend_finish_move(me);
598     midend_redraw(me);
599     midend_set_timer(me);
600 }
601
602 static int midend_really_process_key(midend *me, int x, int y, int button)
603 {
604     game_state *oldstate =
605         me->ourgame->dup_game(me->states[me->statepos - 1].state);
606     int type = MOVE, gottype = FALSE, ret = 1;
607     float anim_time;
608     game_state *s;
609     char *movestr = NULL;
610
611     if (!IS_UI_FAKE_KEY(button)) {
612         movestr = me->ourgame->interpret_move(
613             me->states[me->statepos-1].state,
614             me->ui, me->drawstate, x, y, button);
615     }
616
617     if (!movestr) {
618         if (button == 'n' || button == 'N' || button == '\x0E' ||
619             button == UI_NEWGAME) {
620             midend_new_game(me);
621             midend_redraw(me);
622             goto done;                 /* never animate */
623         } else if (button == 'u' || button == 'U' ||
624                    button == '\x1A' || button == '\x1F' ||
625                    button == UI_UNDO) {
626             midend_stop_anim(me);
627             type = me->states[me->statepos-1].movetype;
628             gottype = TRUE;
629             if (!midend_undo(me))
630                 goto done;
631         } else if (button == 'r' || button == 'R' ||
632                    button == '\x12' || button == '\x19' ||
633                    button == UI_REDO) {
634             midend_stop_anim(me);
635             if (!midend_redo(me))
636                 goto done;
637         } else if ((button == '\x13' || button == UI_SOLVE) &&
638                    me->ourgame->can_solve) {
639             if (midend_solve(me))
640                 goto done;
641         } else if (button == 'q' || button == 'Q' || button == '\x11' ||
642                    button == UI_QUIT) {
643             ret = 0;
644             goto done;
645         } else
646             goto done;
647     } else {
648         if (!*movestr)
649             s = me->states[me->statepos-1].state;
650         else {
651             s = me->ourgame->execute_move(me->states[me->statepos-1].state,
652                                           movestr);
653             assert(s != NULL);
654         }
655
656         if (s == me->states[me->statepos-1].state) {
657             /*
658              * make_move() is allowed to return its input state to
659              * indicate that although no move has been made, the UI
660              * state has been updated and a redraw is called for.
661              */
662             midend_redraw(me);
663             midend_set_timer(me);
664             goto done;
665         } else if (s) {
666             midend_stop_anim(me);
667             midend_purge_states(me);
668             ensure(me);
669             assert(movestr != NULL);
670             me->states[me->nstates].state = s;
671             me->states[me->nstates].movestr = movestr;
672             me->states[me->nstates].movetype = MOVE;
673             me->statepos = ++me->nstates;
674             me->dir = +1;
675             if (me->ui)
676                 me->ourgame->changed_state(me->ui,
677                                            me->states[me->statepos-2].state,
678                                            me->states[me->statepos-1].state);
679         } else {
680             goto done;
681         }
682     }
683
684     if (!gottype)
685         type = me->states[me->statepos-1].movetype;
686
687     /*
688      * See if this move requires an animation.
689      */
690     if (special(type) && !(type == SOLVE &&
691                            (me->ourgame->flags & SOLVE_ANIMATES))) {
692         anim_time = 0;
693     } else {
694         anim_time = me->ourgame->anim_length(oldstate,
695                                              me->states[me->statepos-1].state,
696                                              me->dir, me->ui);
697     }
698
699     me->oldstate = oldstate; oldstate = NULL;
700     if (anim_time > 0) {
701         me->anim_time = anim_time;
702     } else {
703         me->anim_time = 0.0;
704         midend_finish_move(me);
705     }
706     me->anim_pos = 0.0;
707
708     midend_redraw(me);
709
710     midend_set_timer(me);
711
712     done:
713     if (oldstate) me->ourgame->free_game(oldstate);
714     return ret;
715 }
716
717 int midend_process_key(midend *me, int x, int y, int button)
718 {
719     int ret = 1;
720
721     /*
722      * Harmonise mouse drag and release messages.
723      * 
724      * Some front ends might accidentally switch from sending, say,
725      * RIGHT_DRAG messages to sending LEFT_DRAG, half way through a
726      * drag. (This can happen on the Mac, for example, since
727      * RIGHT_DRAG is usually done using Command+drag, and if the
728      * user accidentally releases Command half way through the drag
729      * then there will be trouble.)
730      * 
731      * It would be an O(number of front ends) annoyance to fix this
732      * in the front ends, but an O(number of back ends) annoyance
733      * to have each game capable of dealing with it. Therefore, we
734      * fix it _here_ in the common midend code so that it only has
735      * to be done once.
736      * 
737      * The possible ways in which things can go screwy in the front
738      * end are:
739      * 
740      *  - in a system containing multiple physical buttons button
741      *    presses can inadvertently overlap. We can see ABab (caps
742      *    meaning button-down and lowercase meaning button-up) when
743      *    the user had semantically intended AaBb.
744      * 
745      *  - in a system where one button is simulated by means of a
746      *    modifier key and another button, buttons can mutate
747      *    between press and release (possibly during drag). So we
748      *    can see Ab instead of Aa.
749      * 
750      * Definite requirements are:
751      * 
752      *  - button _presses_ must never be invented or destroyed. If
753      *    the user presses two buttons in succession, the button
754      *    presses must be transferred to the backend unchanged. So
755      *    if we see AaBb , that's fine; if we see ABab (the button
756      *    presses inadvertently overlapped) we must somehow
757      *    `correct' it to AaBb.
758      * 
759      *  - every mouse action must end up looking like a press, zero
760      *    or more drags, then a release. This allows back ends to
761      *    make the _assumption_ that incoming mouse data will be
762      *    sane in this regard, and not worry about the details.
763      * 
764      * So my policy will be:
765      * 
766      *  - treat any button-up as a button-up for the currently
767      *    pressed button, or ignore it if there is no currently
768      *    pressed button.
769      * 
770      *  - treat any drag as a drag for the currently pressed
771      *    button, or ignore it if there is no currently pressed
772      *    button.
773      * 
774      *  - if we see a button-down while another button is currently
775      *    pressed, invent a button-up for the first one and then
776      *    pass the button-down through as before.
777      * 
778      * 2005-05-31: An addendum to the above. Some games might want
779      * a `priority order' among buttons, such that if one button is
780      * pressed while another is down then a fixed one of the
781      * buttons takes priority no matter what order they're pressed
782      * in. Mines, in particular, wants to treat a left+right click
783      * like a left click for the benefit of users of other
784      * implementations. So the last of the above points is modified
785      * in the presence of an (optional) button priority order.
786      *
787      * A further addition: we translate certain keyboard presses to
788      * cursor key 'select' buttons, so that a) frontends don't have
789      * to translate these themselves (like they do for CURSOR_UP etc),
790      * and b) individual games don't have to hard-code button presses
791      * of '\n' etc for keyboard-based cursors. The choice of buttons
792      * here could eventually be controlled by a runtime configuration
793      * option.
794      */
795     if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
796         if (me->pressed_mouse_button) {
797             if (IS_MOUSE_DRAG(button)) {
798                 button = me->pressed_mouse_button +
799                     (LEFT_DRAG - LEFT_BUTTON);
800             } else {
801                 button = me->pressed_mouse_button +
802                     (LEFT_RELEASE - LEFT_BUTTON);
803             }
804         } else
805             return ret;                /* ignore it */
806     } else if (IS_MOUSE_DOWN(button) && me->pressed_mouse_button) {
807         /*
808          * If the new button has lower priority than the old one,
809          * don't bother doing this.
810          */
811         if (me->ourgame->flags &
812             BUTTON_BEATS(me->pressed_mouse_button, button))
813             return ret;                /* just ignore it */
814
815         /*
816          * Fabricate a button-up for the previously pressed button.
817          */
818         ret = ret && midend_really_process_key
819             (me, x, y, (me->pressed_mouse_button +
820                         (LEFT_RELEASE - LEFT_BUTTON)));
821     }
822
823     /*
824      * Translate keyboard presses to cursor selection.
825      */
826     if (button == '\n' || button == '\r')
827       button = CURSOR_SELECT;
828     if (button == ' ')
829       button = CURSOR_SELECT2;
830
831     /*
832      * Normalise both backspace characters (8 and 127) to \b. Easier
833      * to do this once, here, than to require all front ends to
834      * carefully generate the same one - now each front end can
835      * generate whichever is easiest.
836      */
837     if (button == '\177')
838         button = '\b';
839
840     /*
841      * Now send on the event we originally received.
842      */
843     ret = ret && midend_really_process_key(me, x, y, button);
844
845     /*
846      * And update the currently pressed button.
847      */
848     if (IS_MOUSE_RELEASE(button))
849         me->pressed_mouse_button = 0;
850     else if (IS_MOUSE_DOWN(button))
851         me->pressed_mouse_button = button;
852
853     return ret;
854 }
855
856 void midend_redraw(midend *me)
857 {
858     assert(me->drawing);
859
860     if (me->statepos > 0 && me->drawstate) {
861         start_draw(me->drawing);
862         if (me->oldstate && me->anim_time > 0 &&
863             me->anim_pos < me->anim_time) {
864             assert(me->dir != 0);
865             me->ourgame->redraw(me->drawing, me->drawstate, me->oldstate,
866                                 me->states[me->statepos-1].state, me->dir,
867                                 me->ui, me->anim_pos, me->flash_pos);
868         } else {
869             me->ourgame->redraw(me->drawing, me->drawstate, NULL,
870                                 me->states[me->statepos-1].state, +1 /*shrug*/,
871                                 me->ui, 0.0, me->flash_pos);
872         }
873         end_draw(me->drawing);
874     }
875 }
876
877 /*
878  * Nasty hacky function used to implement the --redo option in
879  * gtk.c. Only used for generating the puzzles' icons.
880  */
881 void midend_freeze_timer(midend *me, float tprop)
882 {
883     me->anim_pos = me->anim_time * tprop;
884     midend_redraw(me);
885     deactivate_timer(me->frontend);
886 }
887
888 void midend_timer(midend *me, float tplus)
889 {
890     int need_redraw = (me->anim_time > 0 || me->flash_time > 0);
891
892     me->anim_pos += tplus;
893     if (me->anim_pos >= me->anim_time ||
894         me->anim_time == 0 || !me->oldstate) {
895         if (me->anim_time > 0)
896             midend_finish_move(me);
897     }
898
899     me->flash_pos += tplus;
900     if (me->flash_pos >= me->flash_time || me->flash_time == 0) {
901         me->flash_pos = me->flash_time = 0;
902     }
903
904     if (need_redraw)
905         midend_redraw(me);
906
907     if (me->timing) {
908         float oldelapsed = me->elapsed;
909         me->elapsed += tplus;
910         if ((int)oldelapsed != (int)me->elapsed)
911             status_bar(me->drawing, me->laststatus ? me->laststatus : "");
912     }
913
914     midend_set_timer(me);
915 }
916
917 float *midend_colours(midend *me, int *ncolours)
918 {
919     float *ret;
920
921     ret = me->ourgame->colours(me->frontend, ncolours);
922
923     {
924         int i;
925
926         /*
927          * Allow environment-based overrides for the standard
928          * colours by defining variables along the lines of
929          * `NET_COLOUR_4=6000c0'.
930          */
931
932         for (i = 0; i < *ncolours; i++) {
933             char buf[80], *e;
934             unsigned int r, g, b;
935             int j, k;
936
937             sprintf(buf, "%s_COLOUR_%d", me->ourgame->name, i);
938             for (j = k = 0; buf[j]; j++)
939                 if (!isspace((unsigned char)buf[j]))
940                     buf[k++] = toupper((unsigned char)buf[j]);
941             buf[k] = '\0';
942             if ((e = getenv(buf)) != NULL &&
943                 sscanf(e, "%2x%2x%2x", &r, &g, &b) == 3) {
944                 ret[i*3 + 0] = r / 255.0F;
945                 ret[i*3 + 1] = g / 255.0F;
946                 ret[i*3 + 2] = b / 255.0F;
947             }
948         }
949     }
950
951     return ret;
952 }
953
954 struct preset_menu *preset_menu_new(void)
955 {
956     struct preset_menu *menu = snew(struct preset_menu);
957     menu->n_entries = 0;
958     menu->entries_size = 0;
959     menu->entries = NULL;
960     return menu;
961 }
962
963 static struct preset_menu_entry *preset_menu_add(struct preset_menu *menu,
964                                                  char *title)
965 {
966     struct preset_menu_entry *toret;
967     if (menu->n_entries >= menu->entries_size) {
968         menu->entries_size = menu->n_entries * 5 / 4 + 10;
969         menu->entries = sresize(menu->entries, menu->entries_size,
970                                 struct preset_menu_entry);
971     }
972     toret = &menu->entries[menu->n_entries++];
973     toret->title = title;
974     toret->params = NULL;
975     toret->submenu = NULL;
976     return toret;
977 }
978
979 struct preset_menu *preset_menu_add_submenu(struct preset_menu *parent,
980                                             char *title)
981 {
982     struct preset_menu_entry *entry = preset_menu_add(parent, title);
983     entry->submenu = preset_menu_new();
984     return entry->submenu;
985 }
986
987 void preset_menu_add_preset(struct preset_menu *parent,
988                             char *title, game_params *params)
989 {
990     struct preset_menu_entry *entry = preset_menu_add(parent, title);
991     entry->params = params;
992 }
993
994 game_params *preset_menu_lookup_by_id(struct preset_menu *menu, int id)
995 {
996     int i;
997     game_params *retd;
998
999     for (i = 0; i < menu->n_entries; i++) {
1000         if (id == menu->entries[i].id)
1001             return menu->entries[i].params;
1002         if (menu->entries[i].submenu &&
1003             (retd = preset_menu_lookup_by_id(
1004                  menu->entries[i].submenu, id)) != NULL)
1005             return retd;
1006     }
1007
1008     return NULL;
1009 }
1010
1011 static char *preset_menu_add_from_user_env(
1012     midend *me, struct preset_menu *menu, char *p, int top_level)
1013 {
1014     while (*p) {
1015         char *name, *val;
1016         game_params *preset;
1017
1018         name = p;
1019         while (*p && *p != ':') p++;
1020         if (*p) *p++ = '\0';
1021         val = p;
1022         while (*p && *p != ':') p++;
1023         if (*p) *p++ = '\0';
1024
1025         if (!strcmp(val, "#")) {
1026             /*
1027              * Special case: either open a new submenu with the given
1028              * title, or terminate the current submenu.
1029              */
1030             if (*name) {
1031                 struct preset_menu *submenu =
1032                     preset_menu_add_submenu(menu, dupstr(name));
1033                 p = preset_menu_add_from_user_env(me, submenu, p, FALSE);
1034             } else {
1035                 /*
1036                  * If we get a 'close submenu' indication at the top
1037                  * level, there's not much we can do but quietly
1038                  * ignore it.
1039                  */
1040                 if (!top_level)
1041                     return p;
1042             }
1043             continue;
1044         }
1045
1046         preset = me->ourgame->default_params();
1047         me->ourgame->decode_params(preset, val);
1048
1049         if (me->ourgame->validate_params(preset, TRUE)) {
1050             /* Drop this one from the list. */
1051             me->ourgame->free_params(preset);
1052             continue;
1053         }
1054
1055         preset_menu_add_preset(menu, dupstr(name), preset);
1056     }
1057
1058     return p;
1059 }
1060
1061 static void preset_menu_alloc_ids(midend *me, struct preset_menu *menu)
1062 {
1063     int i;
1064
1065     for (i = 0; i < menu->n_entries; i++)
1066         menu->entries[i].id = me->n_encoded_presets++;
1067
1068     for (i = 0; i < menu->n_entries; i++)
1069         if (menu->entries[i].submenu)
1070             preset_menu_alloc_ids(me, menu->entries[i].submenu);
1071 }
1072
1073 static void preset_menu_encode_params(midend *me, struct preset_menu *menu)
1074 {
1075     int i;
1076
1077     for (i = 0; i < menu->n_entries; i++) {
1078         if (menu->entries[i].params) {
1079             me->encoded_presets[menu->entries[i].id] =
1080                 me->ourgame->encode_params(menu->entries[i].params, TRUE);
1081         } else {
1082             preset_menu_encode_params(me, menu->entries[i].submenu);
1083         }
1084     }
1085 }
1086
1087 struct preset_menu *midend_get_presets(midend *me, int *id_limit)
1088 {
1089     int i;
1090
1091     if (me->preset_menu)
1092         return me->preset_menu;
1093
1094 #if 0
1095     /* Expect the game to implement exactly one of the two preset APIs */
1096     assert(me->ourgame->fetch_preset || me->ourgame->preset_menu);
1097     assert(!(me->ourgame->fetch_preset && me->ourgame->preset_menu));
1098 #endif
1099
1100     if (me->ourgame->fetch_preset) {
1101         char *name;
1102         game_params *preset;
1103
1104         /* Simple one-level menu */
1105         assert(!me->ourgame->preset_menu);
1106         me->preset_menu = preset_menu_new();
1107         for (i = 0; me->ourgame->fetch_preset(i, &name, &preset); i++)
1108             preset_menu_add_preset(me->preset_menu, name, preset);
1109
1110     } else {
1111         /* Hierarchical menu provided by the game backend */
1112         me->preset_menu = me->ourgame->preset_menu();
1113     }
1114
1115     {
1116         /*
1117          * Allow user extensions to the preset list by defining an
1118          * environment variable <gamename>_PRESETS whose value is a
1119          * colon-separated list of items, alternating between textual
1120          * titles in the menu and encoded parameter strings. For
1121          * example, "SOLO_PRESETS=2x3 Advanced:2x3da" would define
1122          * just one additional preset for Solo.
1123          */
1124         char buf[80], *e;
1125         int j, k;
1126
1127         sprintf(buf, "%s_PRESETS", me->ourgame->name);
1128         for (j = k = 0; buf[j]; j++)
1129             if (!isspace((unsigned char)buf[j]))
1130                 buf[k++] = toupper((unsigned char)buf[j]);
1131         buf[k] = '\0';
1132
1133         if ((e = getenv(buf)) != NULL) {
1134             e = dupstr(e);
1135             preset_menu_add_from_user_env(me, me->preset_menu, e, TRUE);
1136             sfree(e);
1137         }
1138     }
1139
1140     /*
1141      * Finalise the menu: allocate an integer id to each entry, and
1142      * store string encodings of the presets' parameters in
1143      * me->encoded_presets.
1144      */
1145     me->n_encoded_presets = 0;
1146     preset_menu_alloc_ids(me, me->preset_menu);
1147     me->encoded_presets = snewn(me->n_encoded_presets, char *);
1148     for (i = 0; i < me->n_encoded_presets; i++)
1149         me->encoded_presets[i] = NULL;
1150     preset_menu_encode_params(me, me->preset_menu);
1151
1152     if (id_limit)
1153         *id_limit = me->n_encoded_presets;
1154     return me->preset_menu;
1155 }
1156
1157 int midend_which_preset(midend *me)
1158 {
1159     char *encoding = me->ourgame->encode_params(me->params, TRUE);
1160     int i, ret;
1161
1162     ret = -1;
1163     for (i = 0; i < me->n_encoded_presets; i++)
1164         if (me->encoded_presets[i] &&
1165             !strcmp(encoding, me->encoded_presets[i])) {
1166             ret = i;
1167             break;
1168         }
1169
1170     sfree(encoding);
1171     return ret;
1172 }
1173
1174 int midend_wants_statusbar(midend *me)
1175 {
1176     return me->ourgame->wants_statusbar;
1177 }
1178
1179 void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx)
1180 {
1181     me->game_id_change_notify_function = notify;
1182     me->game_id_change_notify_ctx = ctx;
1183 }
1184
1185 void midend_supersede_game_desc(midend *me, char *desc, char *privdesc)
1186 {
1187     sfree(me->desc);
1188     sfree(me->privdesc);
1189     me->desc = dupstr(desc);
1190     me->privdesc = privdesc ? dupstr(privdesc) : NULL;
1191     if (me->game_id_change_notify_function)
1192         me->game_id_change_notify_function(me->game_id_change_notify_ctx);
1193 }
1194
1195 config_item *midend_get_config(midend *me, int which, char **wintitle)
1196 {
1197     char *titlebuf, *parstr, *rest;
1198     config_item *ret;
1199     char sep;
1200
1201     assert(wintitle);
1202     titlebuf = snewn(40 + strlen(me->ourgame->name), char);
1203
1204     switch (which) {
1205       case CFG_SETTINGS:
1206         sprintf(titlebuf, "%s configuration", me->ourgame->name);
1207         *wintitle = titlebuf;
1208         return me->ourgame->configure(me->params);
1209       case CFG_SEED:
1210       case CFG_DESC:
1211         if (!me->curparams) {
1212           sfree(titlebuf);
1213           return NULL;
1214         }
1215         sprintf(titlebuf, "%s %s selection", me->ourgame->name,
1216                 which == CFG_SEED ? "random" : "game");
1217         *wintitle = titlebuf;
1218
1219         ret = snewn(2, config_item);
1220
1221         ret[0].type = C_STRING;
1222         if (which == CFG_SEED)
1223             ret[0].name = "Game random seed";
1224         else
1225             ret[0].name = "Game ID";
1226         ret[0].ival = 0;
1227         /*
1228          * For CFG_DESC the text going in here will be a string
1229          * encoding of the restricted parameters, plus a colon,
1230          * plus the game description. For CFG_SEED it will be the
1231          * full parameters, plus a hash, plus the random seed data.
1232          * Either of these is a valid full game ID (although only
1233          * the former is likely to persist across many code
1234          * changes).
1235          */
1236         parstr = me->ourgame->encode_params(me->curparams, which == CFG_SEED);
1237         assert(parstr);
1238         if (which == CFG_DESC) {
1239             rest = me->desc ? me->desc : "";
1240             sep = ':';
1241         } else {
1242             rest = me->seedstr ? me->seedstr : "";
1243             sep = '#';
1244         }
1245         ret[0].sval = snewn(strlen(parstr) + strlen(rest) + 2, char);
1246         sprintf(ret[0].sval, "%s%c%s", parstr, sep, rest);
1247         sfree(parstr);
1248
1249         ret[1].type = C_END;
1250         ret[1].name = ret[1].sval = NULL;
1251         ret[1].ival = 0;
1252
1253         return ret;
1254     }
1255
1256     assert(!"We shouldn't be here");
1257     return NULL;
1258 }
1259
1260 static char *midend_game_id_int(midend *me, char *id, int defmode)
1261 {
1262     char *error, *par, *desc, *seed;
1263     game_params *newcurparams, *newparams, *oldparams1, *oldparams2;
1264     int free_params;
1265
1266     seed = strchr(id, '#');
1267     desc = strchr(id, ':');
1268
1269     if (desc && (!seed || desc < seed)) {
1270         /*
1271          * We have a colon separating parameters from game
1272          * description. So `par' now points to the parameters
1273          * string, and `desc' to the description string.
1274          */
1275         *desc++ = '\0';
1276         par = id;
1277         seed = NULL;
1278     } else if (seed && (!desc || seed < desc)) {
1279         /*
1280          * We have a hash separating parameters from random seed.
1281          * So `par' now points to the parameters string, and `seed'
1282          * to the seed string.
1283          */
1284         *seed++ = '\0';
1285         par = id;
1286         desc = NULL;
1287     } else {
1288         /*
1289          * We only have one string. Depending on `defmode', we take
1290          * it to be either parameters, seed or description.
1291          */
1292         if (defmode == DEF_SEED) {
1293             seed = id;
1294             par = desc = NULL;
1295         } else if (defmode == DEF_DESC) {
1296             desc = id;
1297             par = seed = NULL;
1298         } else {
1299             par = id;
1300             seed = desc = NULL;
1301         }
1302     }
1303
1304     /*
1305      * We must be reasonably careful here not to modify anything in
1306      * `me' until we have finished validating things. This function
1307      * must either return an error and do nothing to the midend, or
1308      * return success and do everything; nothing in between is
1309      * acceptable.
1310      */
1311     newcurparams = newparams = oldparams1 = oldparams2 = NULL;
1312
1313     if (par) {
1314         /*
1315          * The params string may underspecify the game parameters, so
1316          * we must first initialise newcurparams with a full set of
1317          * params from somewhere else before we decode_params the
1318          * input string over the top.
1319          *
1320          * But which set? It depends on what other data we have.
1321          *
1322          * If we've been given a _descriptive_ game id, then that may
1323          * well underspecify by design, e.g. Solo game descriptions
1324          * often start just '3x3:' without specifying one of Solo's
1325          * difficulty settings, because it isn't necessary once a game
1326          * has been generated (and you might not even know it, if
1327          * you're manually transcribing a game description). In that
1328          * situation, I've always felt that the best thing to set the
1329          * difficulty to (for use if the user hits 'New Game' after
1330          * pasting in that game id) is whatever it was previously set
1331          * to. That is, we use whatever is already in me->params as
1332          * the basis for our decoding of this input string.
1333          *
1334          * A random-seed based game id, however, should use the real,
1335          * built-in default params, and not even check the
1336          * <game>_DEFAULT environment setting, because when people
1337          * paste each other random seeds - whether it's two users
1338          * arranging to generate the same game at the same time to
1339          * race solving them, or a user sending a bug report upstream
1340          * - the whole point is for the random game id to always be
1341          * interpreted the same way, even if it does underspecify.
1342          *
1343          * A parameter string typed in on its own, with no seed _or_
1344          * description, gets treated the same way as a random seed,
1345          * because again I think the most likely reason for doing that
1346          * is to have a portable representation of a set of params.
1347          */
1348         if (desc) {
1349             newcurparams = me->ourgame->dup_params(me->params);
1350         } else {
1351             newcurparams = me->ourgame->default_params();
1352         }
1353         me->ourgame->decode_params(newcurparams, par);
1354         error = me->ourgame->validate_params(newcurparams, desc == NULL);
1355         if (error) {
1356             me->ourgame->free_params(newcurparams);
1357             return error;
1358         }
1359         oldparams1 = me->curparams;
1360
1361         /*
1362          * Now filter only the persistent parts of this state into
1363          * the long-term params structure, unless we've _only_
1364          * received a params string in which case the whole lot is
1365          * persistent.
1366          */
1367         oldparams2 = me->params;
1368         if (seed || desc) {
1369             char *tmpstr;
1370
1371             newparams = me->ourgame->dup_params(me->params);
1372
1373             tmpstr = me->ourgame->encode_params(newcurparams, FALSE);
1374             me->ourgame->decode_params(newparams, tmpstr);
1375
1376             sfree(tmpstr);
1377         } else {
1378             newparams = me->ourgame->dup_params(newcurparams);
1379         }
1380         free_params = TRUE;
1381     } else {
1382         newcurparams = me->curparams;
1383         newparams = me->params;
1384         free_params = FALSE;
1385     }
1386
1387     if (desc) {
1388         error = me->ourgame->validate_desc(newparams, desc);
1389         if (error) {
1390             if (free_params) {
1391                 if (newcurparams)
1392                     me->ourgame->free_params(newcurparams);
1393                 if (newparams)
1394                     me->ourgame->free_params(newparams);
1395             }
1396             return error;
1397         }
1398     }
1399
1400     /*
1401      * Now we've got past all possible error points. Update the
1402      * midend itself.
1403      */
1404     me->params = newparams;
1405     me->curparams = newcurparams;
1406     if (oldparams1)
1407         me->ourgame->free_params(oldparams1);
1408     if (oldparams2)
1409         me->ourgame->free_params(oldparams2);
1410
1411     sfree(me->desc);
1412     sfree(me->privdesc);
1413     me->desc = me->privdesc = NULL;
1414     sfree(me->seedstr);
1415     me->seedstr = NULL;
1416
1417     if (desc) {
1418         me->desc = dupstr(desc);
1419         me->genmode = GOT_DESC;
1420         sfree(me->aux_info);
1421         me->aux_info = NULL;
1422     }
1423
1424     if (seed) {
1425         me->seedstr = dupstr(seed);
1426         me->genmode = GOT_SEED;
1427     }
1428
1429     return NULL;
1430 }
1431
1432 char *midend_game_id(midend *me, char *id)
1433 {
1434     return midend_game_id_int(me, id, DEF_PARAMS);
1435 }
1436
1437 char *midend_get_game_id(midend *me)
1438 {
1439     char *parstr, *ret;
1440
1441     parstr = me->ourgame->encode_params(me->curparams, FALSE);
1442     assert(parstr);
1443     assert(me->desc);
1444     ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char);
1445     sprintf(ret, "%s:%s", parstr, me->desc);
1446     sfree(parstr);
1447     return ret;
1448 }
1449
1450 char *midend_get_random_seed(midend *me)
1451 {
1452     char *parstr, *ret;
1453
1454     if (!me->seedstr)
1455         return NULL;
1456
1457     parstr = me->ourgame->encode_params(me->curparams, TRUE);
1458     assert(parstr);
1459     ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char);
1460     sprintf(ret, "%s#%s", parstr, me->seedstr);
1461     sfree(parstr);
1462     return ret;
1463 }
1464
1465 char *midend_set_config(midend *me, int which, config_item *cfg)
1466 {
1467     char *error;
1468     game_params *params;
1469
1470     switch (which) {
1471       case CFG_SETTINGS:
1472         params = me->ourgame->custom_params(cfg);
1473         error = me->ourgame->validate_params(params, TRUE);
1474
1475         if (error) {
1476             me->ourgame->free_params(params);
1477             return error;
1478         }
1479
1480         me->ourgame->free_params(me->params);
1481         me->params = params;
1482         break;
1483
1484       case CFG_SEED:
1485       case CFG_DESC:
1486         error = midend_game_id_int(me, cfg[0].sval,
1487                                    (which == CFG_SEED ? DEF_SEED : DEF_DESC));
1488         if (error)
1489             return error;
1490         break;
1491     }
1492
1493     return NULL;
1494 }
1495
1496 int midend_can_format_as_text_now(midend *me)
1497 {
1498     if (me->ourgame->can_format_as_text_ever)
1499         return me->ourgame->can_format_as_text_now(me->params);
1500     else
1501         return FALSE;
1502 }
1503
1504 char *midend_text_format(midend *me)
1505 {
1506     if (me->ourgame->can_format_as_text_ever && me->statepos > 0 &&
1507         me->ourgame->can_format_as_text_now(me->params))
1508         return me->ourgame->text_format(me->states[me->statepos-1].state);
1509     else
1510         return NULL;
1511 }
1512
1513 char *midend_solve(midend *me)
1514 {
1515     game_state *s;
1516     char *msg, *movestr;
1517
1518     if (!me->ourgame->can_solve)
1519         return "This game does not support the Solve operation";
1520
1521     if (me->statepos < 1)
1522         return "No game set up to solve";   /* _shouldn't_ happen! */
1523
1524     msg = NULL;
1525     movestr = me->ourgame->solve(me->states[0].state,
1526                                  me->states[me->statepos-1].state,
1527                                  me->aux_info, &msg);
1528     if (!movestr) {
1529         if (!msg)
1530             msg = "Solve operation failed";   /* _shouldn't_ happen, but can */
1531         return msg;
1532     }
1533     s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
1534     assert(s);
1535
1536     /*
1537      * Now enter the solved state as the next move.
1538      */
1539     midend_stop_anim(me);
1540     midend_purge_states(me);
1541     ensure(me);
1542     me->states[me->nstates].state = s;
1543     me->states[me->nstates].movestr = movestr;
1544     me->states[me->nstates].movetype = SOLVE;
1545     me->statepos = ++me->nstates;
1546     if (me->ui)
1547         me->ourgame->changed_state(me->ui,
1548                                    me->states[me->statepos-2].state,
1549                                    me->states[me->statepos-1].state);
1550     me->dir = +1;
1551     if (me->ourgame->flags & SOLVE_ANIMATES) {
1552         me->oldstate = me->ourgame->dup_game(me->states[me->statepos-2].state);
1553         me->anim_time =
1554             me->ourgame->anim_length(me->states[me->statepos-2].state,
1555                                      me->states[me->statepos-1].state,
1556                                      +1, me->ui);
1557         me->anim_pos = 0.0;
1558     } else {
1559         me->anim_time = 0.0;
1560         midend_finish_move(me);
1561     }
1562     if (me->drawing)
1563         midend_redraw(me);
1564     midend_set_timer(me);
1565     return NULL;
1566 }
1567
1568 int midend_status(midend *me)
1569 {
1570     /*
1571      * We should probably never be called when the state stack has no
1572      * states on it at all - ideally, midends should never be left in
1573      * that state for long enough to get put down and forgotten about.
1574      * But if we are, I think we return _true_ - pedantically speaking
1575      * a midend in that state is 'vacuously solved', and more
1576      * practically, a user whose midend has been left in that state
1577      * probably _does_ want the 'new game' option to be prominent.
1578      */
1579     if (me->statepos == 0)
1580         return +1;
1581
1582     return me->ourgame->status(me->states[me->statepos-1].state);
1583 }
1584
1585 char *midend_rewrite_statusbar(midend *me, char *text)
1586 {
1587     /*
1588      * An important special case is that we are occasionally called
1589      * with our own laststatus, to update the timer.
1590      */
1591     if (me->laststatus != text) {
1592         sfree(me->laststatus);
1593         me->laststatus = dupstr(text);
1594     }
1595
1596     if (me->ourgame->is_timed) {
1597         char timebuf[100], *ret;
1598         int min, sec;
1599
1600         sec = (int)me->elapsed;
1601         min = sec / 60;
1602         sec %= 60;
1603         sprintf(timebuf, "[%d:%02d] ", min, sec);
1604
1605         ret = snewn(strlen(timebuf) + strlen(text) + 1, char);
1606         strcpy(ret, timebuf);
1607         strcat(ret, text);
1608         return ret;
1609
1610     } else {
1611         return dupstr(text);
1612     }
1613 }
1614
1615 #define SERIALISE_MAGIC "Simon Tatham's Portable Puzzle Collection"
1616 #define SERIALISE_VERSION "1"
1617
1618 void midend_serialise(midend *me,
1619                       void (*write)(void *ctx, void *buf, int len),
1620                       void *wctx)
1621 {
1622     int i;
1623
1624     /*
1625      * Each line of the save file contains three components. First
1626      * exactly 8 characters of header word indicating what type of
1627      * data is contained on the line; then a colon followed by a
1628      * decimal integer giving the length of the main string on the
1629      * line; then a colon followed by the string itself (exactly as
1630      * many bytes as previously specified, no matter what they
1631      * contain). Then a newline (of reasonably flexible form).
1632      */
1633 #define wr(h,s) do { \
1634     char hbuf[80]; \
1635     char *str = (s); \
1636     char lbuf[9];                               \
1637     copy_left_justified(lbuf, sizeof(lbuf), h); \
1638     sprintf(hbuf, "%s:%d:", lbuf, (int)strlen(str)); \
1639     write(wctx, hbuf, strlen(hbuf)); \
1640     write(wctx, str, strlen(str)); \
1641     write(wctx, "\n", 1); \
1642 } while (0)
1643
1644     /*
1645      * Magic string identifying the file, and version number of the
1646      * file format.
1647      */
1648     wr("SAVEFILE", SERIALISE_MAGIC);
1649     wr("VERSION", SERIALISE_VERSION);
1650
1651     /*
1652      * The game name. (Copied locally to avoid const annoyance.)
1653      */
1654     {
1655         char *s = dupstr(me->ourgame->name);
1656         wr("GAME", s);
1657         sfree(s);
1658     }
1659
1660     /*
1661      * The current long-term parameters structure, in full.
1662      */
1663     if (me->params) {
1664         char *s = me->ourgame->encode_params(me->params, TRUE);
1665         wr("PARAMS", s);
1666         sfree(s);
1667     }
1668
1669     /*
1670      * The current short-term parameters structure, in full.
1671      */
1672     if (me->curparams) {
1673         char *s = me->ourgame->encode_params(me->curparams, TRUE);
1674         wr("CPARAMS", s);
1675         sfree(s);
1676     }
1677
1678     /*
1679      * The current game description, the privdesc, and the random seed.
1680      */
1681     if (me->seedstr)
1682         wr("SEED", me->seedstr);
1683     if (me->desc)
1684         wr("DESC", me->desc);
1685     if (me->privdesc)
1686         wr("PRIVDESC", me->privdesc);
1687
1688     /*
1689      * The game's aux_info. We obfuscate this to prevent spoilers
1690      * (people are likely to run `head' or similar on a saved game
1691      * file simply to find out what it is, and don't necessarily
1692      * want to be told the answer to the puzzle!)
1693      */
1694     if (me->aux_info) {
1695         unsigned char *s1;
1696         char *s2;
1697         int len;
1698
1699         len = strlen(me->aux_info);
1700         s1 = snewn(len, unsigned char);
1701         memcpy(s1, me->aux_info, len);
1702         obfuscate_bitmap(s1, len*8, FALSE);
1703         s2 = bin2hex(s1, len);
1704
1705         wr("AUXINFO", s2);
1706
1707         sfree(s2);
1708         sfree(s1);
1709     }
1710
1711     /*
1712      * Any required serialisation of the game_ui.
1713      */
1714     if (me->ui) {
1715         char *s = me->ourgame->encode_ui(me->ui);
1716         if (s) {
1717             wr("UI", s);
1718             sfree(s);
1719         }
1720     }
1721
1722     /*
1723      * The game time, if it's a timed game.
1724      */
1725     if (me->ourgame->is_timed) {
1726         char buf[80];
1727         sprintf(buf, "%g", me->elapsed);
1728         wr("TIME", buf);
1729     }
1730
1731     /*
1732      * The length of, and position in, the states list.
1733      */
1734     {
1735         char buf[80];
1736         sprintf(buf, "%d", me->nstates);
1737         wr("NSTATES", buf);
1738         sprintf(buf, "%d", me->statepos);
1739         wr("STATEPOS", buf);
1740     }
1741
1742     /*
1743      * For each state after the initial one (which we know is
1744      * constructed from either privdesc or desc), enough
1745      * information for execute_move() to reconstruct it from the
1746      * previous one.
1747      */
1748     for (i = 1; i < me->nstates; i++) {
1749         assert(me->states[i].movetype != NEWGAME);   /* only state 0 */
1750         switch (me->states[i].movetype) {
1751           case MOVE:
1752             wr("MOVE", me->states[i].movestr);
1753             break;
1754           case SOLVE:
1755             wr("SOLVE", me->states[i].movestr);
1756             break;
1757           case RESTART:
1758             wr("RESTART", me->states[i].movestr);
1759             break;
1760         }
1761     }
1762
1763 #undef wr
1764 }
1765
1766 /*
1767  * This function returns NULL on success, or an error message.
1768  */
1769 char *midend_deserialise(midend *me,
1770                          int (*read)(void *ctx, void *buf, int len),
1771                          void *rctx)
1772 {
1773     struct deserialise_data data;
1774     int gotstates = 0;
1775     int started = FALSE;
1776     int i;
1777
1778     char *val = NULL;
1779     /* Initially all errors give the same report */
1780     char *ret = "Data does not appear to be a saved game file";
1781
1782     data.seed = data.parstr = data.desc = data.privdesc = NULL;
1783     data.auxinfo = data.uistr = data.cparstr = NULL;
1784     data.elapsed = 0.0F;
1785     data.params = data.cparams = NULL;
1786     data.ui = NULL;
1787     data.states = NULL;
1788     data.nstates = 0;
1789     data.statepos = -1;
1790
1791     /*
1792      * Loop round and round reading one key/value pair at a time
1793      * from the serialised stream, until we have enough game states
1794      * to finish.
1795      */
1796     while (data.nstates <= 0 || data.statepos < 0 ||
1797            gotstates < data.nstates-1) {
1798         char key[9], c;
1799         int len;
1800
1801         do {
1802             if (!read(rctx, key, 1)) {
1803                 /* unexpected EOF */
1804                 goto cleanup;
1805             }
1806         } while (key[0] == '\r' || key[0] == '\n');
1807
1808         if (!read(rctx, key+1, 8)) {
1809             /* unexpected EOF */
1810             goto cleanup;
1811         }
1812
1813         if (key[8] != ':') {
1814             if (started)
1815                 ret = "Data was incorrectly formatted for a saved game file";
1816             goto cleanup;
1817         }
1818         len = strcspn(key, ": ");
1819         assert(len <= 8);
1820         key[len] = '\0';
1821
1822         len = 0;
1823         while (1) {
1824             if (!read(rctx, &c, 1)) {
1825                 /* unexpected EOF */
1826                 goto cleanup;
1827             }
1828
1829             if (c == ':') {
1830                 break;
1831             } else if (c >= '0' && c <= '9') {
1832                 len = (len * 10) + (c - '0');
1833             } else {
1834                 if (started)
1835                     ret = "Data was incorrectly formatted for a"
1836                     " saved game file";
1837                 goto cleanup;
1838             }
1839         }
1840
1841         val = snewn(len+1, char);
1842         if (!read(rctx, val, len)) {
1843             if (started)
1844             goto cleanup;
1845         }
1846         val[len] = '\0';
1847
1848         if (!started) {
1849             if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
1850                 /* ret already has the right message in it */
1851                 goto cleanup;
1852             }
1853             /* Now most errors are this one, unless otherwise specified */
1854             ret = "Saved data ended unexpectedly";
1855             started = TRUE;
1856         } else {
1857             if (!strcmp(key, "VERSION")) {
1858                 if (strcmp(val, SERIALISE_VERSION)) {
1859                     ret = "Cannot handle this version of the saved game"
1860                         " file format";
1861                     goto cleanup;
1862                 }
1863             } else if (!strcmp(key, "GAME")) {
1864                 if (strcmp(val, me->ourgame->name)) {
1865                     ret = "Save file is from a different game";
1866                     goto cleanup;
1867                 }
1868             } else if (!strcmp(key, "PARAMS")) {
1869                 sfree(data.parstr);
1870                 data.parstr = val;
1871                 val = NULL;
1872             } else if (!strcmp(key, "CPARAMS")) {
1873                 sfree(data.cparstr);
1874                 data.cparstr = val;
1875                 val = NULL;
1876             } else if (!strcmp(key, "SEED")) {
1877                 sfree(data.seed);
1878                 data.seed = val;
1879                 val = NULL;
1880             } else if (!strcmp(key, "DESC")) {
1881                 sfree(data.desc);
1882                 data.desc = val;
1883                 val = NULL;
1884             } else if (!strcmp(key, "PRIVDESC")) {
1885                 sfree(data.privdesc);
1886                 data.privdesc = val;
1887                 val = NULL;
1888             } else if (!strcmp(key, "AUXINFO")) {
1889                 unsigned char *tmp;
1890                 int len = strlen(val) / 2;   /* length in bytes */
1891                 tmp = hex2bin(val, len);
1892                 obfuscate_bitmap(tmp, len*8, TRUE);
1893
1894                 sfree(data.auxinfo);
1895                 data.auxinfo = snewn(len + 1, char);
1896                 memcpy(data.auxinfo, tmp, len);
1897                 data.auxinfo[len] = '\0';
1898                 sfree(tmp);
1899             } else if (!strcmp(key, "UI")) {
1900                 sfree(data.uistr);
1901                 data.uistr = val;
1902                 val = NULL;
1903             } else if (!strcmp(key, "TIME")) {
1904                 data.elapsed = (float)atof(val);
1905             } else if (!strcmp(key, "NSTATES")) {
1906                 data.nstates = atoi(val);
1907                 if (data.nstates <= 0) {
1908                     ret = "Number of states in save file was negative";
1909                     goto cleanup;
1910                 }
1911                 if (data.states) {
1912                     ret = "Two state counts provided in save file";
1913                     goto cleanup;
1914                 }
1915                 data.states = snewn(data.nstates, struct midend_state_entry);
1916                 for (i = 0; i < data.nstates; i++) {
1917                     data.states[i].state = NULL;
1918                     data.states[i].movestr = NULL;
1919                     data.states[i].movetype = NEWGAME;
1920                 }
1921             } else if (!strcmp(key, "STATEPOS")) {
1922                 data.statepos = atoi(val);
1923             } else if (!strcmp(key, "MOVE")) {
1924                 gotstates++;
1925                 data.states[gotstates].movetype = MOVE;
1926                 data.states[gotstates].movestr = val;
1927                 val = NULL;
1928             } else if (!strcmp(key, "SOLVE")) {
1929                 gotstates++;
1930                 data.states[gotstates].movetype = SOLVE;
1931                 data.states[gotstates].movestr = val;
1932                 val = NULL;
1933             } else if (!strcmp(key, "RESTART")) {
1934                 gotstates++;
1935                 data.states[gotstates].movetype = RESTART;
1936                 data.states[gotstates].movestr = val;
1937                 val = NULL;
1938             }
1939         }
1940
1941         sfree(val);
1942         val = NULL;
1943     }
1944
1945     data.params = me->ourgame->default_params();
1946     me->ourgame->decode_params(data.params, data.parstr);
1947     if (me->ourgame->validate_params(data.params, TRUE)) {
1948         ret = "Long-term parameters in save file are invalid";
1949         goto cleanup;
1950     }
1951     data.cparams = me->ourgame->default_params();
1952     me->ourgame->decode_params(data.cparams, data.cparstr);
1953     if (me->ourgame->validate_params(data.cparams, FALSE)) {
1954         ret = "Short-term parameters in save file are invalid";
1955         goto cleanup;
1956     }
1957     if (data.seed && me->ourgame->validate_params(data.cparams, TRUE)) {
1958         /*
1959          * The seed's no use with this version, but we can perfectly
1960          * well use the rest of the data.
1961          */
1962         sfree(data.seed);
1963         data.seed = NULL;
1964     }
1965     if (!data.desc) {
1966         ret = "Game description in save file is missing";
1967         goto cleanup;
1968     } else if (me->ourgame->validate_desc(data.cparams, data.desc)) {
1969         ret = "Game description in save file is invalid";
1970         goto cleanup;
1971     }
1972     if (data.privdesc &&
1973         me->ourgame->validate_desc(data.cparams, data.privdesc)) {
1974         ret = "Game private description in save file is invalid";
1975         goto cleanup;
1976     }
1977     if (data.statepos < 0 || data.statepos >= data.nstates) {
1978         ret = "Game position in save file is out of range";
1979     }
1980
1981     data.states[0].state = me->ourgame->new_game(
1982         me, data.cparams, data.privdesc ? data.privdesc : data.desc);
1983     for (i = 1; i < data.nstates; i++) {
1984         assert(data.states[i].movetype != NEWGAME);
1985         switch (data.states[i].movetype) {
1986           case MOVE:
1987           case SOLVE:
1988             data.states[i].state = me->ourgame->execute_move(
1989                 data.states[i-1].state, data.states[i].movestr);
1990             if (data.states[i].state == NULL) {
1991                 ret = "Save file contained an invalid move";
1992                 goto cleanup;
1993             }
1994             break;
1995           case RESTART:
1996             if (me->ourgame->validate_desc(
1997                     data.cparams, data.states[i].movestr)) {
1998                 ret = "Save file contained an invalid restart move";
1999                 goto cleanup;
2000             }
2001             data.states[i].state = me->ourgame->new_game(
2002                 me, data.cparams, data.states[i].movestr);
2003             break;
2004         }
2005     }
2006
2007     data.ui = me->ourgame->new_ui(data.states[0].state);
2008     me->ourgame->decode_ui(data.ui, data.uistr);
2009
2010     /*
2011      * Now we've run out of possible error conditions, so we're
2012      * ready to start overwriting the real data in the current
2013      * midend. We'll do this by swapping things with the local
2014      * variables, so that the same cleanup code will free the old
2015      * stuff.
2016      */
2017     {
2018         char *tmp;
2019
2020         tmp = me->desc;
2021         me->desc = data.desc;
2022         data.desc = tmp;
2023
2024         tmp = me->privdesc;
2025         me->privdesc = data.privdesc;
2026         data.privdesc = tmp;
2027
2028         tmp = me->seedstr;
2029         me->seedstr = data.seed;
2030         data.seed = tmp;
2031
2032         tmp = me->aux_info;
2033         me->aux_info = data.auxinfo;
2034         data.auxinfo = tmp;
2035     }
2036
2037     me->genmode = GOT_NOTHING;
2038
2039     me->statesize = data.nstates;
2040     data.nstates = me->nstates;
2041     me->nstates = me->statesize;
2042     {
2043         struct midend_state_entry *tmp;
2044         tmp = me->states;
2045         me->states = data.states;
2046         data.states = tmp;
2047     }
2048     me->statepos = data.statepos;
2049
2050     {
2051         game_params *tmp;
2052
2053         tmp = me->params;
2054         me->params = data.params;
2055         data.params = tmp;
2056
2057         tmp = me->curparams;
2058         me->curparams = data.cparams;
2059         data.cparams = tmp;
2060     }
2061
2062     me->oldstate = NULL;
2063     me->anim_time = me->anim_pos = me->flash_time = me->flash_pos = 0.0F;
2064     me->dir = 0;
2065
2066     {
2067         game_ui *tmp;
2068
2069         tmp = me->ui;
2070         me->ui = data.ui;
2071         data.ui = tmp;
2072     }
2073
2074     me->elapsed = data.elapsed;
2075     me->pressed_mouse_button = 0;
2076
2077     midend_set_timer(me);
2078
2079     if (me->drawstate)
2080         me->ourgame->free_drawstate(me->drawing, me->drawstate);
2081     me->drawstate =
2082         me->ourgame->new_drawstate(me->drawing,
2083                                    me->states[me->statepos-1].state);
2084     midend_size_new_drawstate(me);
2085     if (me->game_id_change_notify_function)
2086         me->game_id_change_notify_function(me->game_id_change_notify_ctx);
2087
2088     ret = NULL;                        /* success! */
2089
2090     cleanup:
2091     sfree(val);
2092     sfree(data.seed);
2093     sfree(data.parstr);
2094     sfree(data.cparstr);
2095     sfree(data.desc);
2096     sfree(data.privdesc);
2097     sfree(data.auxinfo);
2098     sfree(data.uistr);
2099     if (data.params)
2100         me->ourgame->free_params(data.params);
2101     if (data.cparams)
2102         me->ourgame->free_params(data.cparams);
2103     if (data.ui)
2104         me->ourgame->free_ui(data.ui);
2105     if (data.states) {
2106         int i;
2107
2108         for (i = 0; i < data.nstates; i++) {
2109             if (data.states[i].state)
2110                 me->ourgame->free_game(data.states[i].state);
2111             sfree(data.states[i].movestr);
2112         }
2113         sfree(data.states);
2114     }
2115
2116     return ret;
2117 }
2118
2119 /*
2120  * This function examines a saved game file just far enough to
2121  * determine which game type it contains. It returns NULL on success
2122  * and the game name string in 'name' (which will be dynamically
2123  * allocated and should be caller-freed), or an error message on
2124  * failure.
2125  */
2126 char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len),
2127                     void *rctx)
2128 {
2129     int nstates = 0, statepos = -1, gotstates = 0;
2130     int started = FALSE;
2131
2132     char *val = NULL;
2133     /* Initially all errors give the same report */
2134     char *ret = "Data does not appear to be a saved game file";
2135
2136     *name = NULL;
2137
2138     /*
2139      * Loop round and round reading one key/value pair at a time from
2140      * the serialised stream, until we've found the game name.
2141      */
2142     while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) {
2143         char key[9], c;
2144         int len;
2145
2146         do {
2147             if (!read(rctx, key, 1)) {
2148                 /* unexpected EOF */
2149                 goto cleanup;
2150             }
2151         } while (key[0] == '\r' || key[0] == '\n');
2152
2153         if (!read(rctx, key+1, 8)) {
2154             /* unexpected EOF */
2155             goto cleanup;
2156         }
2157
2158         if (key[8] != ':') {
2159             if (started)
2160                 ret = "Data was incorrectly formatted for a saved game file";
2161             goto cleanup;
2162         }
2163         len = strcspn(key, ": ");
2164         assert(len <= 8);
2165         key[len] = '\0';
2166
2167         len = 0;
2168         while (1) {
2169             if (!read(rctx, &c, 1)) {
2170                 /* unexpected EOF */
2171                 goto cleanup;
2172             }
2173
2174             if (c == ':') {
2175                 break;
2176             } else if (c >= '0' && c <= '9') {
2177                 len = (len * 10) + (c - '0');
2178             } else {
2179                 if (started)
2180                     ret = "Data was incorrectly formatted for a"
2181                     " saved game file";
2182                 goto cleanup;
2183             }
2184         }
2185
2186         val = snewn(len+1, char);
2187         if (!read(rctx, val, len)) {
2188             if (started)
2189             goto cleanup;
2190         }
2191         val[len] = '\0';
2192
2193         if (!started) {
2194             if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
2195                 /* ret already has the right message in it */
2196                 goto cleanup;
2197             }
2198             /* Now most errors are this one, unless otherwise specified */
2199             ret = "Saved data ended unexpectedly";
2200             started = TRUE;
2201         } else {
2202             if (!strcmp(key, "VERSION")) {
2203                 if (strcmp(val, SERIALISE_VERSION)) {
2204                     ret = "Cannot handle this version of the saved game"
2205                         " file format";
2206                     goto cleanup;
2207                 }
2208             } else if (!strcmp(key, "GAME")) {
2209                 *name = dupstr(val);
2210                 ret = NULL;
2211                 goto cleanup;
2212             }
2213         }
2214
2215         sfree(val);
2216         val = NULL;
2217     }
2218
2219     cleanup:
2220     sfree(val);
2221     return ret;
2222 }
2223
2224 char *midend_print_puzzle(midend *me, document *doc, int with_soln)
2225 {
2226     game_state *soln = NULL;
2227
2228     if (me->statepos < 1)
2229         return "No game set up to print";/* _shouldn't_ happen! */
2230
2231     if (with_soln) {
2232         char *msg, *movestr;
2233
2234         if (!me->ourgame->can_solve)
2235             return "This game does not support the Solve operation";
2236
2237         msg = "Solve operation failed";/* game _should_ overwrite on error */
2238         movestr = me->ourgame->solve(me->states[0].state,
2239                                      me->states[me->statepos-1].state,
2240                                      me->aux_info, &msg);
2241         if (!movestr)
2242             return msg;
2243         soln = me->ourgame->execute_move(me->states[me->statepos-1].state,
2244                                          movestr);
2245         assert(soln);
2246
2247         sfree(movestr);
2248     } else
2249         soln = NULL;
2250
2251     /*
2252      * This call passes over ownership of the two game_states and
2253      * the game_params. Hence we duplicate the ones we want to
2254      * keep, and we don't have to bother freeing soln if it was
2255      * non-NULL.
2256      */
2257     document_add_puzzle(doc, me->ourgame,
2258                         me->ourgame->dup_params(me->curparams),
2259                         me->ourgame->dup_game(me->states[0].state), soln);
2260
2261     return NULL;
2262 }