midend_deserialise: keep deserialised data in a struct.

Lots of the local variables in midend_deserialise are now fields of a
structure which contains everything that is _going_ to be written into
the midend once we finish validating it all. This makes it easy to
keep all that data together, and (in future) pass it to other
functions all in one go.

No functional change.
This commit is contained in:
Simon Tatham
2017-10-01 09:50:22 +01:00
parent cdf1639531
commit c0503d4873

238
midend.c
View File

@ -94,6 +94,22 @@ struct midend {
} \ } \
} while (0) } while (0)
/*
* Structure storing all the decoded data from reading a serialised
* game. We keep it in one of these while we check its sanity, and
* only once we're completely satisfied do we install it all in the
* midend structure proper.
*/
struct deserialise_data {
char *seed, *parstr, *desc, *privdesc;
char *auxinfo, *uistr, *cparstr;
float elapsed;
game_params *params, *cparams;
game_ui *ui;
struct midend_state_entry *states;
int nstates, statepos;
};
void midend_reset_tilesize(midend *me) void midend_reset_tilesize(midend *me)
{ {
me->preferred_tilesize = me->ourgame->preferred_tilesize; me->preferred_tilesize = me->ourgame->preferred_tilesize;
@ -1754,7 +1770,8 @@ char *midend_deserialise(midend *me,
int (*read)(void *ctx, void *buf, int len), int (*read)(void *ctx, void *buf, int len),
void *rctx) void *rctx)
{ {
int nstates = 0, statepos = -1, gotstates = 0; struct deserialise_data data;
int gotstates = 0;
int started = FALSE; int started = FALSE;
int i; int i;
@ -1762,25 +1779,22 @@ char *midend_deserialise(midend *me,
/* Initially all errors give the same report */ /* Initially all errors give the same report */
char *ret = "Data does not appear to be a saved game file"; char *ret = "Data does not appear to be a saved game file";
/* data.seed = data.parstr = data.desc = data.privdesc = NULL;
* We construct all the new state in local variables while we data.auxinfo = data.uistr = data.cparstr = NULL;
* check its sanity. Only once we have finished reading the data.elapsed = 0.0F;
* serialised data and detected no errors at all do we start data.params = data.cparams = NULL;
* modifying stuff in the midend passed in. data.ui = NULL;
*/ data.states = NULL;
char *seed = NULL, *parstr = NULL, *desc = NULL, *privdesc = NULL; data.nstates = 0;
char *auxinfo = NULL, *uistr = NULL, *cparstr = NULL; data.statepos = -1;
float elapsed = 0.0F;
game_params *params = NULL, *cparams = NULL;
game_ui *ui = NULL;
struct midend_state_entry *states = NULL;
/* /*
* Loop round and round reading one key/value pair at a time * Loop round and round reading one key/value pair at a time
* from the serialised stream, until we have enough game states * from the serialised stream, until we have enough game states
* to finish. * to finish.
*/ */
while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) { while (data.nstates <= 0 || data.statepos < 0 ||
gotstates < data.nstates-1) {
char key[9], c; char key[9], c;
int len; int len;
@ -1852,24 +1866,24 @@ char *midend_deserialise(midend *me,
goto cleanup; goto cleanup;
} }
} else if (!strcmp(key, "PARAMS")) { } else if (!strcmp(key, "PARAMS")) {
sfree(parstr); sfree(data.parstr);
parstr = val; data.parstr = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "CPARAMS")) { } else if (!strcmp(key, "CPARAMS")) {
sfree(cparstr); sfree(data.cparstr);
cparstr = val; data.cparstr = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "SEED")) { } else if (!strcmp(key, "SEED")) {
sfree(seed); sfree(data.seed);
seed = val; data.seed = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "DESC")) { } else if (!strcmp(key, "DESC")) {
sfree(desc); sfree(data.desc);
desc = val; data.desc = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "PRIVDESC")) { } else if (!strcmp(key, "PRIVDESC")) {
sfree(privdesc); sfree(data.privdesc);
privdesc = val; data.privdesc = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "AUXINFO")) { } else if (!strcmp(key, "AUXINFO")) {
unsigned char *tmp; unsigned char *tmp;
@ -1877,49 +1891,49 @@ char *midend_deserialise(midend *me,
tmp = hex2bin(val, len); tmp = hex2bin(val, len);
obfuscate_bitmap(tmp, len*8, TRUE); obfuscate_bitmap(tmp, len*8, TRUE);
sfree(auxinfo); sfree(data.auxinfo);
auxinfo = snewn(len + 1, char); data.auxinfo = snewn(len + 1, char);
memcpy(auxinfo, tmp, len); memcpy(data.auxinfo, tmp, len);
auxinfo[len] = '\0'; data.auxinfo[len] = '\0';
sfree(tmp); sfree(tmp);
} else if (!strcmp(key, "UI")) { } else if (!strcmp(key, "UI")) {
sfree(uistr); sfree(data.uistr);
uistr = val; data.uistr = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "TIME")) { } else if (!strcmp(key, "TIME")) {
elapsed = (float)atof(val); data.elapsed = (float)atof(val);
} else if (!strcmp(key, "NSTATES")) { } else if (!strcmp(key, "NSTATES")) {
nstates = atoi(val); data.nstates = atoi(val);
if (nstates <= 0) { if (data.nstates <= 0) {
ret = "Number of states in save file was negative"; ret = "Number of states in save file was negative";
goto cleanup; goto cleanup;
} }
if (states) { if (data.states) {
ret = "Two state counts provided in save file"; ret = "Two state counts provided in save file";
goto cleanup; goto cleanup;
} }
states = snewn(nstates, struct midend_state_entry); data.states = snewn(data.nstates, struct midend_state_entry);
for (i = 0; i < nstates; i++) { for (i = 0; i < data.nstates; i++) {
states[i].state = NULL; data.states[i].state = NULL;
states[i].movestr = NULL; data.states[i].movestr = NULL;
states[i].movetype = NEWGAME; data.states[i].movetype = NEWGAME;
} }
} else if (!strcmp(key, "STATEPOS")) { } else if (!strcmp(key, "STATEPOS")) {
statepos = atoi(val); data.statepos = atoi(val);
} else if (!strcmp(key, "MOVE")) { } else if (!strcmp(key, "MOVE")) {
gotstates++; gotstates++;
states[gotstates].movetype = MOVE; data.states[gotstates].movetype = MOVE;
states[gotstates].movestr = val; data.states[gotstates].movestr = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "SOLVE")) { } else if (!strcmp(key, "SOLVE")) {
gotstates++; gotstates++;
states[gotstates].movetype = SOLVE; data.states[gotstates].movetype = SOLVE;
states[gotstates].movestr = val; data.states[gotstates].movestr = val;
val = NULL; val = NULL;
} else if (!strcmp(key, "RESTART")) { } else if (!strcmp(key, "RESTART")) {
gotstates++; gotstates++;
states[gotstates].movetype = RESTART; data.states[gotstates].movetype = RESTART;
states[gotstates].movestr = val; data.states[gotstates].movestr = val;
val = NULL; val = NULL;
} }
} }
@ -1928,68 +1942,70 @@ char *midend_deserialise(midend *me,
val = NULL; val = NULL;
} }
params = me->ourgame->default_params(); data.params = me->ourgame->default_params();
me->ourgame->decode_params(params, parstr); me->ourgame->decode_params(data.params, data.parstr);
if (me->ourgame->validate_params(params, TRUE)) { if (me->ourgame->validate_params(data.params, TRUE)) {
ret = "Long-term parameters in save file are invalid"; ret = "Long-term parameters in save file are invalid";
goto cleanup; goto cleanup;
} }
cparams = me->ourgame->default_params(); data.cparams = me->ourgame->default_params();
me->ourgame->decode_params(cparams, cparstr); me->ourgame->decode_params(data.cparams, data.cparstr);
if (me->ourgame->validate_params(cparams, FALSE)) { if (me->ourgame->validate_params(data.cparams, FALSE)) {
ret = "Short-term parameters in save file are invalid"; ret = "Short-term parameters in save file are invalid";
goto cleanup; goto cleanup;
} }
if (seed && me->ourgame->validate_params(cparams, TRUE)) { if (data.seed && me->ourgame->validate_params(data.cparams, TRUE)) {
/* /*
* The seed's no use with this version, but we can perfectly * The seed's no use with this version, but we can perfectly
* well use the rest of the data. * well use the rest of the data.
*/ */
sfree(seed); sfree(data.seed);
seed = NULL; data.seed = NULL;
} }
if (!desc) { if (!data.desc) {
ret = "Game description in save file is missing"; ret = "Game description in save file is missing";
goto cleanup; goto cleanup;
} else if (me->ourgame->validate_desc(cparams, desc)) { } else if (me->ourgame->validate_desc(data.cparams, data.desc)) {
ret = "Game description in save file is invalid"; ret = "Game description in save file is invalid";
goto cleanup; goto cleanup;
} }
if (privdesc && me->ourgame->validate_desc(cparams, privdesc)) { if (data.privdesc &&
me->ourgame->validate_desc(data.cparams, data.privdesc)) {
ret = "Game private description in save file is invalid"; ret = "Game private description in save file is invalid";
goto cleanup; goto cleanup;
} }
if (statepos < 0 || statepos >= nstates) { if (data.statepos < 0 || data.statepos >= data.nstates) {
ret = "Game position in save file is out of range"; ret = "Game position in save file is out of range";
} }
states[0].state = me->ourgame->new_game(me, cparams, data.states[0].state = me->ourgame->new_game(
privdesc ? privdesc : desc); me, data.cparams, data.privdesc ? data.privdesc : data.desc);
for (i = 1; i < nstates; i++) { for (i = 1; i < data.nstates; i++) {
assert(states[i].movetype != NEWGAME); assert(data.states[i].movetype != NEWGAME);
switch (states[i].movetype) { switch (data.states[i].movetype) {
case MOVE: case MOVE:
case SOLVE: case SOLVE:
states[i].state = me->ourgame->execute_move(states[i-1].state, data.states[i].state = me->ourgame->execute_move(
states[i].movestr); data.states[i-1].state, data.states[i].movestr);
if (states[i].state == NULL) { if (data.states[i].state == NULL) {
ret = "Save file contained an invalid move"; ret = "Save file contained an invalid move";
goto cleanup; goto cleanup;
} }
break; break;
case RESTART: case RESTART:
if (me->ourgame->validate_desc(cparams, states[i].movestr)) { if (me->ourgame->validate_desc(
data.cparams, data.states[i].movestr)) {
ret = "Save file contained an invalid restart move"; ret = "Save file contained an invalid restart move";
goto cleanup; goto cleanup;
} }
states[i].state = me->ourgame->new_game(me, cparams, data.states[i].state = me->ourgame->new_game(
states[i].movestr); me, data.cparams, data.states[i].movestr);
break; break;
} }
} }
ui = me->ourgame->new_ui(states[0].state); data.ui = me->ourgame->new_ui(data.states[0].state);
me->ourgame->decode_ui(ui, uistr); me->ourgame->decode_ui(data.ui, data.uistr);
/* /*
* Now we've run out of possible error conditions, so we're * Now we've run out of possible error conditions, so we're
@ -2002,45 +2018,45 @@ char *midend_deserialise(midend *me,
char *tmp; char *tmp;
tmp = me->desc; tmp = me->desc;
me->desc = desc; me->desc = data.desc;
desc = tmp; data.desc = tmp;
tmp = me->privdesc; tmp = me->privdesc;
me->privdesc = privdesc; me->privdesc = data.privdesc;
privdesc = tmp; data.privdesc = tmp;
tmp = me->seedstr; tmp = me->seedstr;
me->seedstr = seed; me->seedstr = data.seed;
seed = tmp; data.seed = tmp;
tmp = me->aux_info; tmp = me->aux_info;
me->aux_info = auxinfo; me->aux_info = data.auxinfo;
auxinfo = tmp; data.auxinfo = tmp;
} }
me->genmode = GOT_NOTHING; me->genmode = GOT_NOTHING;
me->statesize = nstates; me->statesize = data.nstates;
nstates = me->nstates; data.nstates = me->nstates;
me->nstates = me->statesize; me->nstates = me->statesize;
{ {
struct midend_state_entry *tmp; struct midend_state_entry *tmp;
tmp = me->states; tmp = me->states;
me->states = states; me->states = data.states;
states = tmp; data.states = tmp;
} }
me->statepos = statepos; me->statepos = data.statepos;
{ {
game_params *tmp; game_params *tmp;
tmp = me->params; tmp = me->params;
me->params = params; me->params = data.params;
params = tmp; data.params = tmp;
tmp = me->curparams; tmp = me->curparams;
me->curparams = cparams; me->curparams = data.cparams;
cparams = tmp; data.cparams = tmp;
} }
me->oldstate = NULL; me->oldstate = NULL;
@ -2051,11 +2067,11 @@ char *midend_deserialise(midend *me,
game_ui *tmp; game_ui *tmp;
tmp = me->ui; tmp = me->ui;
me->ui = ui; me->ui = data.ui;
ui = tmp; data.ui = tmp;
} }
me->elapsed = elapsed; me->elapsed = data.elapsed;
me->pressed_mouse_button = 0; me->pressed_mouse_button = 0;
midend_set_timer(me); midend_set_timer(me);
@ -2073,28 +2089,28 @@ char *midend_deserialise(midend *me,
cleanup: cleanup:
sfree(val); sfree(val);
sfree(seed); sfree(data.seed);
sfree(parstr); sfree(data.parstr);
sfree(cparstr); sfree(data.cparstr);
sfree(desc); sfree(data.desc);
sfree(privdesc); sfree(data.privdesc);
sfree(auxinfo); sfree(data.auxinfo);
sfree(uistr); sfree(data.uistr);
if (params) if (data.params)
me->ourgame->free_params(params); me->ourgame->free_params(data.params);
if (cparams) if (data.cparams)
me->ourgame->free_params(cparams); me->ourgame->free_params(data.cparams);
if (ui) if (data.ui)
me->ourgame->free_ui(ui); me->ourgame->free_ui(data.ui);
if (states) { if (data.states) {
int i; int i;
for (i = 0; i < nstates; i++) { for (i = 0; i < data.nstates; i++) {
if (states[i].state) if (data.states[i].state)
me->ourgame->free_game(states[i].state); me->ourgame->free_game(data.states[i].state);
sfree(states[i].movestr); sfree(data.states[i].movestr);
} }
sfree(states); sfree(data.states);
} }
return ret; return ret;