summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/midend.c
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2018-04-17 18:18:40 -0400
committerFranklin Wei <git@fwei.tk>2018-04-17 18:20:12 -0400
commit6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd (patch)
treef6c9abf4a05f640134aa3d248a9498a06f596712 /apps/plugins/puzzles/src/midend.c
parent7a12e796a62b916dae5de1b2986e8cb11ac5be4c (diff)
downloadrockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.zip
rockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.tar.gz
rockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.tar.bz2
rockbox-6dc9d1d7309613fdc0518c03b2cdf3253d4c52fd.tar.xz
puzzles: enable all the remaining games
They all work now :). I merged in part of Chris Boyle's Android port of Puzzles to give the front end a way to know what keys the back end needs. This also re-syncs to the latest upstream sources. Change-Id: Ie0409bbb32f617ae5abf4f81be3b45d1552db9bb
Diffstat (limited to 'apps/plugins/puzzles/src/midend.c')
-rw-r--r--apps/plugins/puzzles/src/midend.c173
1 files changed, 138 insertions, 35 deletions
diff --git a/apps/plugins/puzzles/src/midend.c b/apps/plugins/puzzles/src/midend.c
index 4ccb1f9..0c91388 100644
--- a/apps/plugins/puzzles/src/midend.c
+++ b/apps/plugins/puzzles/src/midend.c
@@ -25,6 +25,11 @@ struct midend_state_entry {
int movetype;
};
+struct midend_serialise_buf {
+ char *buf;
+ int len, size;
+};
+
struct midend {
frontend *frontend;
random_state *random;
@@ -63,8 +68,8 @@ struct midend {
int nstates, statesize, statepos;
struct midend_state_entry *states;
- char *newgame_undo_buf;
- int newgame_undo_len, newgame_undo_size;
+ struct midend_serialise_buf newgame_undo, newgame_redo;
+ int newgame_can_store_undo;
game_params *params, *curparams;
game_drawstate *drawstate;
@@ -158,8 +163,11 @@ midend *midend_new(frontend *fe, const game *ourgame,
me->random = random_new(randseed, randseedsize);
me->nstates = me->statesize = me->statepos = 0;
me->states = NULL;
- me->newgame_undo_buf = NULL;
- me->newgame_undo_size = me->newgame_undo_len = 0;
+ me->newgame_undo.buf = NULL;
+ me->newgame_undo.size = me->newgame_undo.len = 0;
+ me->newgame_redo.buf = NULL;
+ me->newgame_redo.size = me->newgame_redo.len = 0;
+ me->newgame_can_store_undo = FALSE;
me->params = ourgame->default_params();
me->game_id_change_notify_function = NULL;
me->game_id_change_notify_ctx = NULL;
@@ -221,6 +229,7 @@ static void midend_purge_states(midend *me)
if (me->states[me->nstates].movestr)
sfree(me->states[me->nstates].movestr);
}
+ me->newgame_redo.len = 0;
}
static void midend_free_game(midend *me)
@@ -257,7 +266,8 @@ void midend_free(midend *me)
if (me->drawing)
drawing_free(me->drawing);
random_free(me->random);
- sfree(me->newgame_undo_buf);
+ sfree(me->newgame_undo.buf);
+ sfree(me->newgame_redo.buf);
sfree(me->states);
sfree(me->desc);
sfree(me->privdesc);
@@ -386,36 +396,39 @@ void midend_force_redraw(midend *me)
static void newgame_serialise_write(void *ctx, const void *buf, int len)
{
- midend *const me = ctx;
+ struct midend_serialise_buf *ser = (struct midend_serialise_buf *)ctx;
int new_len;
- assert(len < INT_MAX - me->newgame_undo_len);
- new_len = me->newgame_undo_len + len;
- if (new_len > me->newgame_undo_size) {
- me->newgame_undo_size = new_len + new_len / 4 + 1024;
- me->newgame_undo_buf = sresize(me->newgame_undo_buf,
- me->newgame_undo_size, char);
+ assert(len < INT_MAX - ser->len);
+ new_len = ser->len + len;
+ if (new_len > ser->size) {
+ ser->size = new_len + new_len / 4 + 1024;
+ ser->buf = sresize(ser->buf, ser->size, char);
}
- memcpy(me->newgame_undo_buf + me->newgame_undo_len, buf, len);
- me->newgame_undo_len = new_len;
+ memcpy(ser->buf + ser->len, buf, len);
+ ser->len = new_len;
}
void midend_new_game(midend *me)
{
- me->newgame_undo_len = 0;
- if (me->nstates != 0) {
+ me->newgame_undo.len = 0;
+ if (me->newgame_can_store_undo) {
/*
* Serialise the whole of the game that we're about to
* supersede, so that the 'New Game' action can be undone
- * later. But if nstates == 0, that means there _isn't_ a
- * current game (not even a starting position), because this
- * is the initial call to midend_new_game when the midend is
- * first set up; in that situation, we want to avoid writing
- * out any serialisation, because it would be useless anyway
- * and just confuse us into thinking we had something to undo
- * to.
+ * later.
+ *
+ * We omit this in various situations, such as if there
+ * _isn't_ a current game (not even a starting position)
+ * because this is the initial call to midend_new_game when
+ * the midend is first set up, or if the midend state has
+ * already begun to be overwritten by midend_set_config. In
+ * those situations, we want to avoid writing out any
+ * serialisation, because they will be either invalid, or
+ * worse, valid but wrong.
*/
- midend_serialise(me, newgame_serialise_write, me);
+ midend_purge_states(me);
+ midend_serialise(me, newgame_serialise_write, &me->newgame_undo);
}
midend_stop_anim(me);
@@ -528,30 +541,31 @@ void midend_new_game(midend *me)
if (me->game_id_change_notify_function)
me->game_id_change_notify_function(me->game_id_change_notify_ctx);
+
+ me->newgame_can_store_undo = TRUE;
}
int midend_can_undo(midend *me)
{
- return (me->statepos > 1 || me->newgame_undo_len);
+ return (me->statepos > 1 || me->newgame_undo.len);
}
int midend_can_redo(midend *me)
{
- return (me->statepos < me->nstates);
+ return (me->statepos < me->nstates || me->newgame_redo.len);
}
struct newgame_undo_deserialise_read_ctx {
- midend *me;
+ struct midend_serialise_buf *ser;
int len, pos;
};
static int newgame_undo_deserialise_read(void *ctx, void *buf, int len)
{
struct newgame_undo_deserialise_read_ctx *const rctx = ctx;
- midend *const me = rctx->me;
int use = min(len, rctx->len - rctx->pos);
- memcpy(buf, me->newgame_undo_buf + rctx->pos, use);
+ memcpy(buf, rctx->ser->buf + rctx->pos, use);
rctx->pos += use;
return use;
}
@@ -624,12 +638,22 @@ static int midend_undo(midend *me)
me->statepos--;
me->dir = -1;
return 1;
- } else if (me->newgame_undo_len) {
- /* This undo cannot be undone with redo */
+ } else if (me->newgame_undo.len) {
struct newgame_undo_deserialise_read_ctx rctx;
struct newgame_undo_deserialise_check_ctx cctx;
- rctx.me = me;
- rctx.len = me->newgame_undo_len; /* copy for reentrancy safety */
+ struct midend_serialise_buf serbuf;
+
+ /*
+ * Serialise the current game so that you can later redo past
+ * this undo. Once we're committed to the undo actually
+ * happening, we'll copy this data into place.
+ */
+ serbuf.buf = NULL;
+ serbuf.len = serbuf.size = 0;
+ midend_serialise(me, newgame_serialise_write, &serbuf);
+
+ rctx.ser = &me->newgame_undo;
+ rctx.len = me->newgame_undo.len; /* copy for reentrancy safety */
rctx.pos = 0;
cctx.refused = FALSE;
deserialise_error = midend_deserialise_internal(
@@ -642,6 +666,7 @@ static int midend_undo(midend *me)
* contain the dummy error message generated by our check
* function, which we ignore.)
*/
+ sfree(serbuf.buf);
return 0;
} else {
/*
@@ -652,6 +677,22 @@ static int midend_undo(midend *me)
* replaced by the wrong file, etc., by user error.
*/
assert(!deserialise_error);
+
+ /*
+ * Clear the old newgame_undo serialisation, so that we
+ * don't try to undo past the beginning of the game we've
+ * just gone back to and end up at the front of it again.
+ */
+ me->newgame_undo.len = 0;
+
+ /*
+ * Copy the serialisation of the game we've just left into
+ * the midend so that we can redo back into it later.
+ */
+ me->newgame_redo.len = 0;
+ newgame_serialise_write(&me->newgame_redo, serbuf.buf, serbuf.len);
+
+ sfree(serbuf.buf);
return 1;
}
} else
@@ -660,6 +701,8 @@ static int midend_undo(midend *me)
static int midend_redo(midend *me)
{
+ const char *deserialise_error;
+
if (me->statepos < me->nstates) {
if (me->ui)
me->ourgame->changed_state(me->ui,
@@ -668,6 +711,63 @@ static int midend_redo(midend *me)
me->statepos++;
me->dir = +1;
return 1;
+ } else if (me->newgame_redo.len) {
+ struct newgame_undo_deserialise_read_ctx rctx;
+ struct newgame_undo_deserialise_check_ctx cctx;
+ struct midend_serialise_buf serbuf;
+
+ /*
+ * Serialise the current game so that you can later undo past
+ * this redo. Once we're committed to the undo actually
+ * happening, we'll copy this data into place.
+ */
+ serbuf.buf = NULL;
+ serbuf.len = serbuf.size = 0;
+ midend_serialise(me, newgame_serialise_write, &serbuf);
+
+ rctx.ser = &me->newgame_redo;
+ rctx.len = me->newgame_redo.len; /* copy for reentrancy safety */
+ rctx.pos = 0;
+ cctx.refused = FALSE;
+ deserialise_error = midend_deserialise_internal(
+ me, newgame_undo_deserialise_read, &rctx,
+ newgame_undo_deserialise_check, &cctx);
+ if (cctx.refused) {
+ /*
+ * Our post-deserialisation check shows that we can't use
+ * this saved game after all. (deserialise_error will
+ * contain the dummy error message generated by our check
+ * function, which we ignore.)
+ */
+ sfree(serbuf.buf);
+ return 0;
+ } else {
+ /*
+ * There should never be any _other_ deserialisation
+ * error, because this serialised data has been held in
+ * our memory since it was created, and hasn't had any
+ * opportunity to be corrupted on disk, accidentally
+ * replaced by the wrong file, etc., by user error.
+ */
+ assert(!deserialise_error);
+
+ /*
+ * Clear the old newgame_redo serialisation, so that we
+ * don't try to redo past the end of the game we've just
+ * come into and end up at the back of it again.
+ */
+ me->newgame_redo.len = 0;
+
+ /*
+ * Copy the serialisation of the game we've just left into
+ * the midend so that we can undo back into it later.
+ */
+ me->newgame_undo.len = 0;
+ newgame_serialise_write(&me->newgame_undo, serbuf.buf, serbuf.len);
+
+ sfree(serbuf.buf);
+ return 1;
+ }
} else
return 0;
}
@@ -1587,6 +1687,8 @@ static const char *midend_game_id_int(midend *me, const char *id, int defmode)
sfree(par);
+ me->newgame_can_store_undo = FALSE;
+
return NULL;
}
@@ -2224,13 +2326,14 @@ static const char *midend_deserialise_internal(
me->statepos = data.statepos;
/*
- * Don't save the "new game undo" state. So "new game" twice or
+ * Don't save the "new game undo/redo" state. So "new game" twice or
* (in some environments) switching away and back, will make a
* "new game" irreversible. Maybe in the future we will have a
* more sophisticated way to decide when to discard the previous
* game state.
*/
- me->newgame_undo_len = 0;
+ me->newgame_undo.len = 0;
+ me->newgame_redo.len = 0;
{
game_params *tmp;