mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Revamp of the Windows command-line parsing and puzzle-loading code.
The Windows puzzles now accept similar command-line syntax to the GTK ones, in that you can give them either a game ID (descriptive, random or just plain params) or the name of a save file. Unlike the GTK ones, however, the save file interpretation is tried first; this is because some puzzles (e.g. Black Box) will interpret any old string as a valid (if boring) game ID, and unlike the GTK puzzles it's not feasible to require users to disambiguate via a command-line option, because on Windows a thing that might easily happen is that a user passes a save file to a puzzle binary via 'Open With' in the GUI shell, where they don't get the chance to add extra options. In order to make this work sensibly in the all-in-one Windows app, I had to get round to another thing I've been planning to do for a while, which is to write a function to examine a saved game file and find out which puzzle it's for. So the combined Windows binary will auto-switch to the right game if you pass a save file on its command line, and also if you use Load while the program is running. Another utility function I needed is one to split the WinMain single command line string into argv. For this I've imported a copy of split_into_argv() from Windows PuTTY (which doesn't affect this package's list of copyright holders, since that function was all my own code anyway). [originally from svn r9749]
This commit is contained in:
110
midend.c
110
midend.c
@ -172,6 +172,11 @@ midend *midend_new(frontend *fe, const game *ourgame,
|
||||
return me;
|
||||
}
|
||||
|
||||
const game *midend_which_game(midend *me)
|
||||
{
|
||||
return me->ourgame;
|
||||
}
|
||||
|
||||
static void midend_purge_states(midend *me)
|
||||
{
|
||||
while (me->nstates > me->statepos) {
|
||||
@ -1949,6 +1954,111 @@ char *midend_deserialise(midend *me,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function examines a saved game file just far enough to
|
||||
* determine which game type it contains. It returns NULL on success
|
||||
* and the game name string in 'name' (which will be dynamically
|
||||
* allocated and should be caller-freed), or an error message on
|
||||
* failure.
|
||||
*/
|
||||
char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len),
|
||||
void *rctx)
|
||||
{
|
||||
int nstates = 0, statepos = -1, gotstates = 0;
|
||||
int started = FALSE;
|
||||
|
||||
char *val = NULL;
|
||||
/* Initially all errors give the same report */
|
||||
char *ret = "Data does not appear to be a saved game file";
|
||||
|
||||
*name = NULL;
|
||||
|
||||
/*
|
||||
* Loop round and round reading one key/value pair at a time from
|
||||
* the serialised stream, until we've found the game name.
|
||||
*/
|
||||
while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) {
|
||||
char key[9], c;
|
||||
int len;
|
||||
|
||||
do {
|
||||
if (!read(rctx, key, 1)) {
|
||||
/* unexpected EOF */
|
||||
goto cleanup;
|
||||
}
|
||||
} while (key[0] == '\r' || key[0] == '\n');
|
||||
|
||||
if (!read(rctx, key+1, 8)) {
|
||||
/* unexpected EOF */
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (key[8] != ':') {
|
||||
if (started)
|
||||
ret = "Data was incorrectly formatted for a saved game file";
|
||||
goto cleanup;
|
||||
}
|
||||
len = strcspn(key, ": ");
|
||||
assert(len <= 8);
|
||||
key[len] = '\0';
|
||||
|
||||
len = 0;
|
||||
while (1) {
|
||||
if (!read(rctx, &c, 1)) {
|
||||
/* unexpected EOF */
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (c == ':') {
|
||||
break;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
len = (len * 10) + (c - '0');
|
||||
} else {
|
||||
if (started)
|
||||
ret = "Data was incorrectly formatted for a"
|
||||
" saved game file";
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
val = snewn(len+1, char);
|
||||
if (!read(rctx, val, len)) {
|
||||
if (started)
|
||||
goto cleanup;
|
||||
}
|
||||
val[len] = '\0';
|
||||
|
||||
if (!started) {
|
||||
if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
|
||||
/* ret already has the right message in it */
|
||||
goto cleanup;
|
||||
}
|
||||
/* Now most errors are this one, unless otherwise specified */
|
||||
ret = "Saved data ended unexpectedly";
|
||||
started = TRUE;
|
||||
} else {
|
||||
if (!strcmp(key, "VERSION")) {
|
||||
if (strcmp(val, SERIALISE_VERSION)) {
|
||||
ret = "Cannot handle this version of the saved game"
|
||||
" file format";
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (!strcmp(key, "GAME")) {
|
||||
*name = dupstr(val);
|
||||
ret = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
sfree(val);
|
||||
val = NULL;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
sfree(val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *midend_print_puzzle(midend *me, document *doc, int with_soln)
|
||||
{
|
||||
game_state *soln = NULL;
|
||||
|
Reference in New Issue
Block a user