+
+char *midend_text_format(midend_data *me)
+{
+ if (me->ourgame->can_format_as_text && me->statepos > 0)
+ return me->ourgame->text_format(me->states[me->statepos-1].state);
+ else
+ return NULL;
+}
+
+char *midend_solve(midend_data *me)
+{
+ game_state *s;
+ char *msg;
+
+ if (!me->ourgame->can_solve)
+ return "This game does not support the Solve operation";
+
+ if (me->statepos < 1)
+ return "No game set up to solve"; /* _shouldn't_ happen! */
+
+ msg = "Solve operation failed"; /* game _should_ overwrite on error */
+ s = me->ourgame->solve(me->states[0].state,
+ me->states[me->statepos-1].state,
+ me->aux_info, &msg);
+ if (!s)
+ return msg;
+
+ /*
+ * Now enter the solved state as the next move.
+ */
+ midend_stop_anim(me);
+ while (me->nstates > me->statepos)
+ me->ourgame->free_game(me->states[--me->nstates].state);
+ ensure(me);
+ me->states[me->nstates].state = s;
+ me->states[me->nstates].special = TRUE; /* created using solve */
+ me->statepos = ++me->nstates;
+ if (me->ui)
+ me->ourgame->changed_state(me->ui,
+ me->states[me->statepos-2].state,
+ me->states[me->statepos-1].state);
+ me->anim_time = 0.0;
+ midend_finish_move(me);
+ midend_redraw(me);
+ midend_set_timer(me);
+ return NULL;
+}
+
+char *midend_rewrite_statusbar(midend_data *me, char *text)
+{
+ /*
+ * An important special case is that we are occasionally called
+ * with our own laststatus, to update the timer.
+ */
+ if (me->laststatus != text) {
+ sfree(me->laststatus);
+ me->laststatus = dupstr(text);
+ }
+
+ if (me->ourgame->is_timed) {
+ char timebuf[100], *ret;
+ int min, sec;
+
+ sec = me->elapsed;
+ min = sec / 60;
+ sec %= 60;
+ sprintf(timebuf, "[%d:%02d] ", min, sec);
+
+ ret = snewn(strlen(timebuf) + strlen(text) + 1, char);
+ strcpy(ret, timebuf);
+ strcat(ret, text);
+ return ret;
+
+ } else {
+ return dupstr(text);
+ }
+}