mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-20 15:41:30 -07:00
Keep a set of preferences in the midend.
This commit introduces a serialisation format for the user preferences stored in game_ui, using the keyword identifiers that get_prefs is required to write into its list of config_item. As a result, the serialisation format looks enough like an ordinary config file that a user could write one by hand. The preferences for the game backend are kept in serialised form in me->be_prefs. The typical use of this is to apply it to a just-created game_ui by calling midend_apply_prefs(), which deserialises the prefs buffer into a list of config_item and passes it to the backend's set_prefs function, overwriting the preference fields (but no others) of the game_ui. This is duly done when creating a new game, when loading a game from a save file, and also when printing a puzzle. To make the latter work, document_add_puzzle now takes a game_ui (and keeps ownership of it afterwards), and passes that to the backend's compute_size and print functions. The backend's own get_prefs and set_prefs functions are wrapped by midend_get_prefs and midend_set_prefs. This is partly as a convenience (it deals with optionally constructing a game_ui specially to call the backend with), but mostly so that there will be a convenient place in the midend to add standard preferences applying across all puzzles. No cross-puzzle preferences are provided yet. There are two external interfaces to all this, and in this commit, neither one is yet called by any frontend: A new pair of midend functions is exposed to the front end, called midend_load_prefs and midend_save_prefs. These have a similar API to midend_serialise and midend_deserialise, taking a read/write function pointer and a context. So front ends that can already load/save a game to a file on disk should find it easy to add a similar set of functions loading/saving user preferences. Secondly, a new value CFG_PREFS is added to the enumeration of configuration dialog types, alongside the ones for the Custom game type, entering a game description and entering a random seed. This should make it easy for frontends to offer a Preferences dialog, because it will operate almost exactly like three dialogs they already handle.
This commit is contained in:
32
devel.but
32
devel.but
@ -3542,7 +3542,11 @@ viewing the existing one). The mid-end generates this dialog box
|
|||||||
description itself. This should be used when the user selects
|
description itself. This should be used when the user selects
|
||||||
\q{Random Seed} from the game menu (or equivalent).
|
\q{Random Seed} from the game menu (or equivalent).
|
||||||
|
|
||||||
(A fourth value \cw{CFG_FRONTEND_SPECIFIC} is provided in this
|
\dt \cw{CFG_PREFS}
|
||||||
|
|
||||||
|
\dd Requests a box suitable for configuring user preferences.
|
||||||
|
|
||||||
|
(An additional value \cw{CFG_FRONTEND_SPECIFIC} is provided in this
|
||||||
enumeration, so that frontends can extend it for their own internal
|
enumeration, so that frontends can extend it for their own internal
|
||||||
use. For example, you might wrap this function with a
|
use. For example, you might wrap this function with a
|
||||||
\cw{frontend_get_config} which handles some values of \c{which} itself
|
\cw{frontend_get_config} which handles some values of \c{which} itself
|
||||||
@ -3796,6 +3800,32 @@ application is a monolithic one containing all the puzzles. See
|
|||||||
identify a save file before you instantiate your mid-end in the first
|
identify a save file before you instantiate your mid-end in the first
|
||||||
place.
|
place.
|
||||||
|
|
||||||
|
\H{midend-save-prefs} \cw{midend_save_prefs()}
|
||||||
|
|
||||||
|
\c void midend_save_prefs(
|
||||||
|
\c midend *me, void (*write)(void *ctx, const void *buf, int len),
|
||||||
|
\c void *wctx);
|
||||||
|
|
||||||
|
Calling this function causes the mid-end to write out the states of
|
||||||
|
all user-settable preference options, including its own cross-platform
|
||||||
|
preferences and ones exported by a particular game via
|
||||||
|
\cw{get_prefs()} and \cw{set_prefs()} (\k{backend-get-prefs},
|
||||||
|
\k{backend-set-prefs}). The output is a textual format suitable for
|
||||||
|
writing into a configuration file on disk.
|
||||||
|
|
||||||
|
The \c{write} and \c{wctx} parameters have the same semantics as for
|
||||||
|
\cw{midend_serialise()} (\k{midend-serialise}).
|
||||||
|
|
||||||
|
\H{midend-load-prefs} \cw{midend_load_prefs()}
|
||||||
|
|
||||||
|
\c const char *midend_load_prefs(
|
||||||
|
\c midend *me, bool (*read)(void *ctx, void *buf, int len),
|
||||||
|
\c void *rctx);
|
||||||
|
|
||||||
|
This function is used to load a configuration file in the same format
|
||||||
|
emitted by \cw{midend_save_prefs()}, and import all the preferences
|
||||||
|
described in the file into the current mid-end.
|
||||||
|
|
||||||
\H{identify-game} \cw{identify_game()}
|
\H{identify-game} \cw{identify_game()}
|
||||||
|
|
||||||
\c const char *identify_game(char **name,
|
\c const char *identify_game(char **name,
|
||||||
|
362
midend.c
362
midend.c
@ -94,6 +94,8 @@ struct midend {
|
|||||||
|
|
||||||
int pressed_mouse_button;
|
int pressed_mouse_button;
|
||||||
|
|
||||||
|
struct midend_serialise_buf be_prefs;
|
||||||
|
|
||||||
int preferred_tilesize, preferred_tilesize_dpr, tilesize;
|
int preferred_tilesize, preferred_tilesize_dpr, tilesize;
|
||||||
int winwidth, winheight;
|
int winwidth, winheight;
|
||||||
|
|
||||||
@ -126,12 +128,21 @@ struct deserialise_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forward reference.
|
* Forward references.
|
||||||
*/
|
*/
|
||||||
static const char *midend_deserialise_internal(
|
static const char *midend_deserialise_internal(
|
||||||
midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx,
|
midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx,
|
||||||
const char *(*check)(void *ctx, midend *, const struct deserialise_data *),
|
const char *(*check)(void *ctx, midend *, const struct deserialise_data *),
|
||||||
void *cctx);
|
void *cctx);
|
||||||
|
static void midend_serialise_prefs(
|
||||||
|
midend *me, game_ui *ui,
|
||||||
|
void (*write)(void *ctx, const void *buf, int len), void *wctx);
|
||||||
|
static const char *midend_deserialise_prefs(
|
||||||
|
midend *me, game_ui *ui,
|
||||||
|
bool (*read)(void *ctx, void *buf, int len), void *rctx);
|
||||||
|
static config_item *midend_get_prefs(midend *me, game_ui *ui);
|
||||||
|
static void midend_set_prefs(midend *me, game_ui *ui, config_item *all_prefs);
|
||||||
|
static void midend_apply_prefs(midend *me, game_ui *ui);
|
||||||
|
|
||||||
void midend_reset_tilesize(midend *me)
|
void midend_reset_tilesize(midend *me)
|
||||||
{
|
{
|
||||||
@ -223,6 +234,9 @@ midend *midend_new(frontend *fe, const game *ourgame,
|
|||||||
else
|
else
|
||||||
me->drawing = NULL;
|
me->drawing = NULL;
|
||||||
|
|
||||||
|
me->be_prefs.buf = NULL;
|
||||||
|
me->be_prefs.size = me->be_prefs.len = 0;
|
||||||
|
|
||||||
midend_reset_tilesize(me);
|
midend_reset_tilesize(me);
|
||||||
|
|
||||||
sfree(randseed);
|
sfree(randseed);
|
||||||
@ -638,6 +652,7 @@ void midend_new_game(midend *me)
|
|||||||
if (me->ui)
|
if (me->ui)
|
||||||
me->ourgame->free_ui(me->ui);
|
me->ourgame->free_ui(me->ui);
|
||||||
me->ui = me->ourgame->new_ui(me->states[0].state);
|
me->ui = me->ourgame->new_ui(me->states[0].state);
|
||||||
|
midend_apply_prefs(me, me->ui);
|
||||||
midend_set_timer(me);
|
midend_set_timer(me);
|
||||||
me->pressed_mouse_button = 0;
|
me->pressed_mouse_button = 0;
|
||||||
|
|
||||||
@ -647,6 +662,20 @@ void midend_new_game(midend *me)
|
|||||||
me->newgame_can_store_undo = true;
|
me->newgame_can_store_undo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *midend_load_prefs(
|
||||||
|
midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx)
|
||||||
|
{
|
||||||
|
const char *err = midend_deserialise_prefs(me, NULL, read, rctx);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void midend_save_prefs(midend *me,
|
||||||
|
void (*write)(void *ctx, const void *buf, int len),
|
||||||
|
void *wctx)
|
||||||
|
{
|
||||||
|
midend_serialise_prefs(me, NULL, write, wctx);
|
||||||
|
}
|
||||||
|
|
||||||
bool midend_can_undo(midend *me)
|
bool midend_can_undo(midend *me)
|
||||||
{
|
{
|
||||||
return (me->statepos > 1 || me->newgame_undo.len);
|
return (me->statepos > 1 || me->newgame_undo.len);
|
||||||
@ -1711,6 +1740,10 @@ config_item *midend_get_config(midend *me, int which, char **wintitle)
|
|||||||
ret[1].name = NULL;
|
ret[1].name = NULL;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
case CFG_PREFS:
|
||||||
|
sprintf(titlebuf, "%s preferences", me->ourgame->name);
|
||||||
|
*wintitle = titlebuf;
|
||||||
|
return midend_get_prefs(me, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!"We shouldn't be here");
|
assert(!"We shouldn't be here");
|
||||||
@ -1959,6 +1992,10 @@ const char *midend_set_config(midend *me, int which, config_item *cfg)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CFG_PREFS:
|
||||||
|
midend_set_prefs(me, NULL, cfg);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2543,6 +2580,7 @@ static const char *midend_deserialise_internal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data.ui = me->ourgame->new_ui(data.states[0].state);
|
data.ui = me->ourgame->new_ui(data.states[0].state);
|
||||||
|
midend_apply_prefs(me, data.ui);
|
||||||
if (data.uistr && me->ourgame->decode_ui)
|
if (data.uistr && me->ourgame->decode_ui)
|
||||||
me->ourgame->decode_ui(data.ui, data.uistr,
|
me->ourgame->decode_ui(data.ui, data.uistr,
|
||||||
data.states[data.statepos-1].state);
|
data.states[data.statepos-1].state);
|
||||||
@ -2815,14 +2853,326 @@ const char *midend_print_puzzle(midend *me, document *doc, bool with_soln)
|
|||||||
soln = NULL;
|
soln = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This call passes over ownership of the two game_states and
|
* This call passes over ownership of the two game_states, the
|
||||||
* the game_params. Hence we duplicate the ones we want to
|
* game_params and the game_ui. Hence we duplicate the ones we
|
||||||
* keep, and we don't have to bother freeing soln if it was
|
* want to keep, and we don't have to bother freeing soln if it
|
||||||
* non-NULL.
|
* was non-NULL.
|
||||||
*/
|
*/
|
||||||
|
game_ui *ui = me->ourgame->new_ui(me->states[0].state);
|
||||||
|
midend_apply_prefs(me, ui);
|
||||||
document_add_puzzle(doc, me->ourgame,
|
document_add_puzzle(doc, me->ourgame,
|
||||||
me->ourgame->dup_params(me->curparams),
|
me->ourgame->dup_params(me->curparams), ui,
|
||||||
me->ourgame->dup_game(me->states[0].state), soln);
|
me->ourgame->dup_game(me->states[0].state), soln);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void midend_apply_prefs(midend *me, game_ui *ui)
|
||||||
|
{
|
||||||
|
struct midend_serialise_buf_read_ctx rctx[1];
|
||||||
|
rctx->ser = &me->be_prefs;
|
||||||
|
rctx->len = me->be_prefs.len;
|
||||||
|
rctx->pos = 0;
|
||||||
|
const char *err = midend_deserialise_prefs(
|
||||||
|
me, me->ui, midend_serialise_buf_read, rctx);
|
||||||
|
/* This should have come from our own serialise function, so
|
||||||
|
* it should never be invalid. */
|
||||||
|
assert(!err && "Bad internal serialisation of preferences");
|
||||||
|
}
|
||||||
|
|
||||||
|
static config_item *midend_get_prefs(midend *me, game_ui *ui)
|
||||||
|
{
|
||||||
|
int n_be_prefs, n_me_prefs, pos, i;
|
||||||
|
config_item *all_prefs, *be_prefs;
|
||||||
|
|
||||||
|
be_prefs = NULL;
|
||||||
|
n_be_prefs = 0;
|
||||||
|
if (me->ourgame->get_prefs) {
|
||||||
|
if (ui) {
|
||||||
|
be_prefs = me->ourgame->get_prefs(ui);
|
||||||
|
} else if (me->ui) {
|
||||||
|
be_prefs = me->ourgame->get_prefs(me->ui);
|
||||||
|
} else {
|
||||||
|
game_ui *tmp_ui = me->ourgame->new_ui(NULL);
|
||||||
|
be_prefs = me->ourgame->get_prefs(tmp_ui);
|
||||||
|
me->ourgame->free_ui(tmp_ui);
|
||||||
|
}
|
||||||
|
while (be_prefs[n_be_prefs].type != C_END)
|
||||||
|
n_be_prefs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_me_prefs = 0;
|
||||||
|
all_prefs = snewn(n_me_prefs + n_be_prefs + 1, config_item);
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n_be_prefs; i++) {
|
||||||
|
all_prefs[pos] = be_prefs[i]; /* structure copy */
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
all_prefs[pos].name = NULL;
|
||||||
|
all_prefs[pos].type = C_END;
|
||||||
|
|
||||||
|
if (be_prefs)
|
||||||
|
free_cfg(be_prefs);
|
||||||
|
|
||||||
|
return all_prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void midend_set_prefs(midend *me, game_ui *ui, config_item *all_prefs)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
game_ui *tmpui = NULL;
|
||||||
|
|
||||||
|
if (me->ourgame->get_prefs) {
|
||||||
|
if (!ui)
|
||||||
|
ui = tmpui = me->ourgame->new_ui(NULL);
|
||||||
|
me->ourgame->set_prefs(ui, all_prefs + pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
me->be_prefs.len = 0;
|
||||||
|
midend_serialise_prefs(me, ui, midend_serialise_buf_write, &me->be_prefs);
|
||||||
|
|
||||||
|
if (tmpui)
|
||||||
|
me->ourgame->free_ui(tmpui);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void midend_serialise_prefs(
|
||||||
|
midend *me, game_ui *ui,
|
||||||
|
void (*write)(void *ctx, const void *buf, int len), void *wctx)
|
||||||
|
{
|
||||||
|
config_item *cfg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
cfg = midend_get_prefs(me, ui);
|
||||||
|
|
||||||
|
assert(cfg);
|
||||||
|
|
||||||
|
for (i = 0; cfg[i].type != C_END; i++) {
|
||||||
|
config_item *it = &cfg[i];
|
||||||
|
|
||||||
|
/* Expect keywords to be made up only of simple characters */
|
||||||
|
assert(it->kw[strspn(it->kw, "abcdefghijklmnopqrstuvwxyz-")] == '\0');
|
||||||
|
|
||||||
|
write(wctx, it->kw, strlen(it->kw));
|
||||||
|
write(wctx, "=", 1);
|
||||||
|
|
||||||
|
switch (it->type) {
|
||||||
|
case C_BOOLEAN:
|
||||||
|
if (it->u.boolean.bval)
|
||||||
|
write(wctx, "true", 4);
|
||||||
|
else
|
||||||
|
write(wctx, "false", 5);
|
||||||
|
break;
|
||||||
|
case C_STRING: {
|
||||||
|
const char *p = it->u.string.sval;
|
||||||
|
while (*p) {
|
||||||
|
char c = *p++;
|
||||||
|
write(wctx, &c, 1);
|
||||||
|
if (c == '\n')
|
||||||
|
write(wctx, " ", 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case C_CHOICES: {
|
||||||
|
int n = it->u.choices.selected;
|
||||||
|
const char *p = it->u.choices.choicekws;
|
||||||
|
char sepstr[2];
|
||||||
|
|
||||||
|
sepstr[0] = *p++;
|
||||||
|
sepstr[1] = '\0';
|
||||||
|
|
||||||
|
while (n > 0) {
|
||||||
|
const char *q = strchr(p, sepstr[0]);
|
||||||
|
assert(q != NULL && "Value out of range in C_CHOICES");
|
||||||
|
p = q+1;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(wctx, p, strcspn(p, sepstr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(wctx, "\n", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer {
|
||||||
|
char *data;
|
||||||
|
size_t len, size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void buffer_append(struct buffer *buf, char c)
|
||||||
|
{
|
||||||
|
if (buf->len + 1 > buf->size) {
|
||||||
|
size_t new_size = buf->size + buf->size / 4 + 128;
|
||||||
|
assert(new_size > buf->size);
|
||||||
|
buf->data = sresize(buf->data, new_size, char);
|
||||||
|
buf->size = new_size;
|
||||||
|
assert(buf->len < buf->size);
|
||||||
|
}
|
||||||
|
buf->data[buf->len++] = c;
|
||||||
|
assert(buf->len < buf->size);
|
||||||
|
buf->data[buf->len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *midend_deserialise_prefs(
|
||||||
|
midend *me, game_ui *ui,
|
||||||
|
bool (*read)(void *ctx, void *buf, int len), void *rctx)
|
||||||
|
{
|
||||||
|
config_item *cfg, *it;
|
||||||
|
int i;
|
||||||
|
struct buffer buf[1] = {{ NULL, 0, 0 }};
|
||||||
|
const char *errmsg = NULL;
|
||||||
|
char read_char;
|
||||||
|
char ungot_char = '\0';
|
||||||
|
bool have_ungot_a_char = false, eof = false;
|
||||||
|
|
||||||
|
cfg = midend_get_prefs(me, ui);
|
||||||
|
|
||||||
|
while (!eof) {
|
||||||
|
if (have_ungot_a_char) {
|
||||||
|
read_char = ungot_char;
|
||||||
|
have_ungot_a_char = false;
|
||||||
|
} else {
|
||||||
|
if (!read(rctx, &read_char, 1))
|
||||||
|
goto out; /* EOF at line start == success */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_char == '#' || read_char == '\n') {
|
||||||
|
/* Skip comment or blank line */
|
||||||
|
while (read_char != '\n') {
|
||||||
|
if (!read(rctx, &read_char, 1))
|
||||||
|
goto out; /* EOF during boring line == success */
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->len = 0;
|
||||||
|
while (true) {
|
||||||
|
buffer_append(buf, read_char);
|
||||||
|
if (!read(rctx, &read_char, 1)) {
|
||||||
|
errmsg = "Partial line at end of preferences file";
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (read_char == '\n') {
|
||||||
|
errmsg = "Expected '=' after keyword";
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (read_char == '=')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = NULL;
|
||||||
|
for (i = 0; cfg[i].type != C_END; i++)
|
||||||
|
if (!strcmp(buf->data, cfg[i].kw))
|
||||||
|
it = &cfg[i];
|
||||||
|
|
||||||
|
buf->len = 0;
|
||||||
|
while (true) {
|
||||||
|
if (!read(rctx, &read_char, 1)) {
|
||||||
|
/* We tolerate missing \n at the end of the file, so
|
||||||
|
* this is taken to mean we've got a complete config
|
||||||
|
* directive. But set the eof flag so that we stop
|
||||||
|
* after processing it. */
|
||||||
|
eof = true;
|
||||||
|
break;
|
||||||
|
} else if (read_char == '\n') {
|
||||||
|
/* Newline _might_ be the end of this config
|
||||||
|
* directive, unless it's followed by a space, in
|
||||||
|
* which case it's a space-stuffed line
|
||||||
|
* continuation. */
|
||||||
|
if (read(rctx, &read_char, 1)) {
|
||||||
|
if (read_char == ' ') {
|
||||||
|
buffer_append(buf, '\n');
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* But if the next character wasn't a space,
|
||||||
|
* then we must unget it so that it'll be
|
||||||
|
* available to the next iteration of our
|
||||||
|
* outer loop as the first character of the
|
||||||
|
* next keyword. */
|
||||||
|
ungot_char = read_char;
|
||||||
|
have_ungot_a_char = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* And if the newline was followed by EOF, then we
|
||||||
|
* should finish this iteration of the outer
|
||||||
|
* loop normally, and then not go round again. */
|
||||||
|
eof = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Any other character is just added to the buffer. */
|
||||||
|
buffer_append(buf, read_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it) {
|
||||||
|
/*
|
||||||
|
* Tolerate unknown keywords in a preferences file, on the
|
||||||
|
* assumption that they're from a different (probably
|
||||||
|
* later) version of the game.
|
||||||
|
*/
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (it->type) {
|
||||||
|
case C_BOOLEAN:
|
||||||
|
if (!strcmp(buf->data, "true"))
|
||||||
|
it->u.boolean.bval = true;
|
||||||
|
else if (!strcmp(buf->data, "false"))
|
||||||
|
it->u.boolean.bval = false;
|
||||||
|
else {
|
||||||
|
errmsg = "Value for boolean was not 'true' or 'false'";
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case C_STRING:
|
||||||
|
sfree(it->u.string.sval);
|
||||||
|
it->u.string.sval = buf->data;
|
||||||
|
buf->data = NULL;
|
||||||
|
buf->len = buf->size = 0;
|
||||||
|
break;
|
||||||
|
case C_CHOICES: {
|
||||||
|
int n = 0;
|
||||||
|
bool found = false;
|
||||||
|
const char *p = it->u.choices.choicekws;
|
||||||
|
char sepstr[2];
|
||||||
|
|
||||||
|
sepstr[0] = *p;
|
||||||
|
sepstr[1] = '\0';
|
||||||
|
|
||||||
|
while (*p++) {
|
||||||
|
int len = strcspn(p, sepstr);
|
||||||
|
if (buf->len == len && !memcmp(p, buf->data, len)) {
|
||||||
|
it->u.choices.selected = n;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p += len;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
errmsg = "Invalid value for enumeration";
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
|
||||||
|
if (!errmsg)
|
||||||
|
midend_set_prefs(me, ui, cfg);
|
||||||
|
|
||||||
|
free_cfg(cfg);
|
||||||
|
sfree(buf->data);
|
||||||
|
return errmsg;
|
||||||
|
}
|
||||||
|
2
nullfe.c
2
nullfe.c
@ -52,7 +52,7 @@ void print_line_width(drawing *dr, int width) {}
|
|||||||
void print_line_dotted(drawing *dr, bool dotted) {}
|
void print_line_dotted(drawing *dr, bool dotted) {}
|
||||||
void status_bar(drawing *dr, const char *text) {}
|
void status_bar(drawing *dr, const char *text) {}
|
||||||
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
||||||
game_state *st, game_state *st2) {}
|
game_ui *ui, game_state *st, game_state *st2) {}
|
||||||
|
|
||||||
void fatal(const char *fmt, ...)
|
void fatal(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
2
osx.m
2
osx.m
@ -174,7 +174,7 @@ static bool savefile_read(void *wctx, void *buf, int len)
|
|||||||
* this stub to satisfy the reference in midend_print_puzzle().
|
* this stub to satisfy the reference in midend_print_puzzle().
|
||||||
*/
|
*/
|
||||||
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
||||||
game_state *st, game_state *st2)
|
game_ui *ui, game_state *st, game_state *st2)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
printing.c
14
printing.c
@ -10,6 +10,7 @@
|
|||||||
struct puzzle {
|
struct puzzle {
|
||||||
const game *game;
|
const game *game;
|
||||||
game_params *par;
|
game_params *par;
|
||||||
|
game_ui *ui;
|
||||||
game_state *st;
|
game_state *st;
|
||||||
game_state *st2;
|
game_state *st2;
|
||||||
};
|
};
|
||||||
@ -56,6 +57,7 @@ void document_free(document *doc)
|
|||||||
|
|
||||||
for (i = 0; i < doc->npuzzles; i++) {
|
for (i = 0; i < doc->npuzzles; i++) {
|
||||||
doc->puzzles[i].game->free_params(doc->puzzles[i].par);
|
doc->puzzles[i].game->free_params(doc->puzzles[i].par);
|
||||||
|
doc->puzzles[i].game->free_ui(doc->puzzles[i].ui);
|
||||||
doc->puzzles[i].game->free_game(doc->puzzles[i].st);
|
doc->puzzles[i].game->free_game(doc->puzzles[i].st);
|
||||||
if (doc->puzzles[i].st2)
|
if (doc->puzzles[i].st2)
|
||||||
doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
|
doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
|
||||||
@ -75,7 +77,7 @@ void document_free(document *doc)
|
|||||||
* another sheet (typically the solution to the first game_state).
|
* another sheet (typically the solution to the first game_state).
|
||||||
*/
|
*/
|
||||||
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
||||||
game_state *st, game_state *st2)
|
game_ui *ui, game_state *st, game_state *st2)
|
||||||
{
|
{
|
||||||
if (doc->npuzzles >= doc->puzzlesize) {
|
if (doc->npuzzles >= doc->puzzlesize) {
|
||||||
doc->puzzlesize += 32;
|
doc->puzzlesize += 32;
|
||||||
@ -83,6 +85,7 @@ void document_add_puzzle(document *doc, const game *game, game_params *par,
|
|||||||
}
|
}
|
||||||
doc->puzzles[doc->npuzzles].game = game;
|
doc->puzzles[doc->npuzzles].game = game;
|
||||||
doc->puzzles[doc->npuzzles].par = par;
|
doc->puzzles[doc->npuzzles].par = par;
|
||||||
|
doc->puzzles[doc->npuzzles].ui = ui;
|
||||||
doc->puzzles[doc->npuzzles].st = st;
|
doc->puzzles[doc->npuzzles].st = st;
|
||||||
doc->puzzles[doc->npuzzles].st2 = st2;
|
doc->puzzles[doc->npuzzles].st2 = st2;
|
||||||
doc->npuzzles++;
|
doc->npuzzles++;
|
||||||
@ -274,14 +277,9 @@ void document_print_page(const document *doc, drawing *dr, int page_nr)
|
|||||||
* permit each game to choose its own?)
|
* permit each game to choose its own?)
|
||||||
*/
|
*/
|
||||||
tilesize = 512;
|
tilesize = 512;
|
||||||
{
|
pz->game->compute_size(pz->par, tilesize, pz->ui, &pixw, &pixh);
|
||||||
game_ui *ui = pz->game->new_ui(pz->st);
|
|
||||||
pz->game->compute_size(pz->par, tilesize, ui,
|
|
||||||
&pixw, &pixh);
|
|
||||||
print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
|
print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
|
||||||
pz->game->print(dr, pass == 0 ? pz->st : pz->st2, ui, tilesize);
|
pz->game->print(dr, pass == 0 ? pz->st : pz->st2, pz->ui, tilesize);
|
||||||
pz->game->free_ui(ui);
|
|
||||||
}
|
|
||||||
print_end_puzzle(dr);
|
print_end_puzzle(dr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +331,7 @@ void midend_timer(midend *me, float tplus);
|
|||||||
struct preset_menu *midend_get_presets(midend *me, int *id_limit);
|
struct preset_menu *midend_get_presets(midend *me, int *id_limit);
|
||||||
int midend_which_preset(midend *me);
|
int midend_which_preset(midend *me);
|
||||||
bool midend_wants_statusbar(midend *me);
|
bool midend_wants_statusbar(midend *me);
|
||||||
enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_FRONTEND_SPECIFIC };
|
enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_PREFS, CFG_FRONTEND_SPECIFIC };
|
||||||
config_item *midend_get_config(midend *me, int which, char **wintitle);
|
config_item *midend_get_config(midend *me, int which, char **wintitle);
|
||||||
const char *midend_set_config(midend *me, int which, config_item *cfg);
|
const char *midend_set_config(midend *me, int which, config_item *cfg);
|
||||||
const char *midend_game_id(midend *me, const char *id);
|
const char *midend_game_id(midend *me, const char *id);
|
||||||
@ -352,6 +352,11 @@ void midend_serialise(midend *me,
|
|||||||
const char *midend_deserialise(midend *me,
|
const char *midend_deserialise(midend *me,
|
||||||
bool (*read)(void *ctx, void *buf, int len),
|
bool (*read)(void *ctx, void *buf, int len),
|
||||||
void *rctx);
|
void *rctx);
|
||||||
|
const char *midend_load_prefs(
|
||||||
|
midend *me, bool (*read)(void *ctx, void *buf, int len), void *rctx);
|
||||||
|
void midend_save_prefs(midend *me,
|
||||||
|
void (*write)(void *ctx, const void *buf, int len),
|
||||||
|
void *wctx);
|
||||||
const char *identify_game(char **name,
|
const char *identify_game(char **name,
|
||||||
bool (*read)(void *ctx, void *buf, int len),
|
bool (*read)(void *ctx, void *buf, int len),
|
||||||
void *rctx);
|
void *rctx);
|
||||||
@ -557,7 +562,7 @@ void SHA_Simple(const void *p, int len, unsigned char *output);
|
|||||||
document *document_new(int pw, int ph, float userscale);
|
document *document_new(int pw, int ph, float userscale);
|
||||||
void document_free(document *doc);
|
void document_free(document *doc);
|
||||||
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
||||||
game_state *st, game_state *st2);
|
game_ui *ui, game_state *st, game_state *st2);
|
||||||
int document_npages(const document *doc);
|
int document_npages(const document *doc);
|
||||||
void document_begin(const document *doc, drawing *dr);
|
void document_begin(const document *doc, drawing *dr);
|
||||||
void document_end(const document *doc, drawing *dr);
|
void document_end(const document *doc, drawing *dr);
|
||||||
|
Reference in New Issue
Block a user