aboutsummaryrefslogtreecommitdiff
path: root/midend.c
diff options
context:
space:
mode:
authorSimon Tatham <anakin@pobox.com>2013-01-19 18:56:05 +0000
committerSimon Tatham <anakin@pobox.com>2013-01-19 18:56:05 +0000
commit6b6442b16c5de9f5f9479b736bf865a4236047cb (patch)
treeac5b9ce403576d0ee71ad93e9da303ddf6d5b7ed /midend.c
parentd1ffb55d264fe082d01cf1520fa84f3415565492 (diff)
downloadpuzzles-6b6442b16c5de9f5f9479b736bf865a4236047cb.zip
puzzles-6b6442b16c5de9f5f9479b736bf865a4236047cb.tar.gz
puzzles-6b6442b16c5de9f5f9479b736bf865a4236047cb.tar.bz2
puzzles-6b6442b16c5de9f5f9479b736bf865a4236047cb.tar.xz
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]
Diffstat (limited to 'midend.c')
-rw-r--r--midend.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/midend.c b/midend.c
index 472ebdf..53dca21 100644
--- a/midend.c
+++ b/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;