X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;ds=sidebyside;f=midend.c;h=2f09bb528c7a9a1fb38e4262f21867b5f782326d;hb=db313b3948d27244dd7c34c2609c66d6204d8931;hp=5dd896f5b66fb9a162c186660e1248754eb69133;hpb=9f6114e27236105d64e23e063a17db84389059ba;p=sgt-puzzles.git diff --git a/midend.c b/midend.c index 5dd896f..2f09bb5 100644 --- a/midend.c +++ b/midend.c @@ -542,6 +542,62 @@ static int newgame_undo_deserialise_read(void *ctx, void *buf, int len) return use; } +struct newgame_undo_deserialise_check_ctx { + int refused; +}; + +static char *newgame_undo_deserialise_check( + void *vctx, midend *me, const struct deserialise_data *data) +{ + struct newgame_undo_deserialise_check_ctx *ctx = + (struct newgame_undo_deserialise_check_ctx *)vctx; + char *old, *new; + + /* + * Undoing a New Game operation is only permitted if it doesn't + * change the game parameters. The point of having the ability at + * all is to recover from the momentary finger error of having hit + * the 'n' key (perhaps in place of some other nearby key), or hit + * the New Game menu item by mistake when aiming for the adjacent + * Restart; in both those situations, the game params are the same + * before and after the new-game operation. + * + * In principle, we could generalise this so that _any_ call to + * midend_new_game could be undone, but that would need all front + * ends to be alert to the possibility that any keystroke passed + * to midend_process_key might (if it turns out to have been one + * of the synonyms for undo, which the frontend doesn't + * necessarily check for) have various knock-on effects like + * needing to select a different preset in the game type menu, or + * even resizing the window. At least for the moment, it's easier + * not to do that, and to simply disallow any newgame-undo that is + * disruptive in either of those ways. + * + * We check both params and cparams, to be as safe as possible. + */ + + old = me->ourgame->encode_params(me->params, TRUE); + new = me->ourgame->encode_params(data->params, TRUE); + if (strcmp(old, new)) { + /* Set a flag to distinguish this deserialise failure + * from one due to faulty decoding */ + ctx->refused = TRUE; + return "Undoing this new-game operation would change params"; + } + + old = me->ourgame->encode_params(me->curparams, TRUE); + new = me->ourgame->encode_params(data->cparams, TRUE); + if (strcmp(old, new)) { + ctx->refused = TRUE; + return "Undoing this new-game operation would change params"; + } + + /* + * Otherwise, fine, go ahead. + */ + return NULL; +} + static int midend_undo(midend *me) { char *deserialise_error; @@ -557,13 +613,33 @@ static int midend_undo(midend *me) } else if (me->newgame_undo_len) { /* This undo cannot be undone with redo */ 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 */ rctx.pos = 0; - deserialise_error = - midend_deserialise(me, newgame_undo_deserialise_read, &rctx); - assert(!deserialise_error); - return 1; + 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.) + */ + 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); + return 1; + } } else return 0; }