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