chiark / gitweb /
Support for saving games in Javascript puzzles.
authorSimon Tatham <anakin@pobox.com>
Tue, 5 Sep 2017 19:10:16 +0000 (20:10 +0100)
committerSimon Tatham <anakin@pobox.com>
Tue, 5 Sep 2017 19:56:55 +0000 (20:56 +0100)
This is done by getting midend_serialise to produce the complete
saved-game file as an in-memory string buffer, and then encoding that
into a data: URI which we provide to the user as a hyperlink in a
dialog box. The hyperlink has the 'download' attribute, which means
clicking on it should automatically offer to save the file, and also
lets me specify a not-too-silly default file name.

emcc.c
emccpre.js
emccx.json
html/jspage.pl

diff --git a/emcc.c b/emcc.c
index ca033cbd471a0f20258b4617f4b864254c4a2d80..26e1ac220d171a29a0a9fa26a82a9e1de7d94729 100644 (file)
--- a/emcc.c
+++ b/emcc.c
@@ -756,6 +756,49 @@ void command(int n)
     }
 }
 
+/* ----------------------------------------------------------------------
+ * Called from JS to prepare a save-game file, and free one after it's
+ * been used.
+ */
+
+struct savefile_write_ctx {
+    char *buffer;
+    size_t pos;
+};
+
+static void savefile_write(void *wctx, void *buf, int len)
+{
+    struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)wctx;
+    if (ctx->buffer)
+        memcpy(ctx->buffer + ctx->pos, buf, len);
+    ctx->pos += len;
+}
+
+char *get_save_file(void)
+{
+    struct savefile_write_ctx ctx;
+    size_t size;
+
+    /* First pass, to count up the size */
+    ctx.buffer = NULL;
+    ctx.pos = 0;
+    midend_serialise(me, savefile_write, &ctx);
+    size = ctx.pos;
+
+    /* Second pass, to actually write out the data */
+    ctx.buffer = snewn(size, char);
+    ctx.pos = 0;
+    midend_serialise(me, savefile_write, &ctx);
+    assert(ctx.pos == size);
+
+    return ctx.buffer;
+}
+
+void free_save_file(char *buffer)
+{
+    sfree(buffer);
+}
+
 /* ----------------------------------------------------------------------
  * Setup function called at page load time. It's called main() because
  * that's the most convenient thing in Emscripten, but it's not main()
index efd54ae4d58e035d97a25d9f84f59abce8b0d094..16702bb129c33c54c3fe6cf01e54e271339f9564 100644 (file)
@@ -296,6 +296,32 @@ function initPuzzle() {
             command(9);
     };
 
+    // 'number' is used for C pointers
+    get_save_file = Module.cwrap('get_save_file', 'number', []);
+    free_save_file = Module.cwrap('free_save_file', 'void', ['number']);
+
+    document.getElementById("save").onclick = function(event) {
+        if (dlg_dimmer === null) {
+            var savefile_ptr = get_save_file();
+            var savefile_text = Pointer_stringify(savefile_ptr);
+            free_save_file(savefile_ptr);
+            dialog_init("Download saved-game file");
+            dlg_form.appendChild(document.createTextNode(
+                "Click to download the "));
+            var a = document.createElement("a");
+            a.download = "puzzle.sav";
+            a.href = "data:application/octet-stream," +
+                encodeURIComponent(savefile_text);
+            a.appendChild(document.createTextNode("saved-game file"));
+            dlg_form.appendChild(a);
+            dlg_form.appendChild(document.createTextNode("."));
+            dlg_form.appendChild(document.createElement("br"));
+            dialog_launch(function(event) {
+                dialog_cleanup();
+            });
+        }
+    };
+
     gametypelist = document.getElementById("gametype");
     gametypesubmenus.push(gametypelist);
 
index e03f7e25c7564fcbe670dab687b31fcda3dd2f1b..25d08fecb9b48f821e62bbdf935f6709566d503c 100644 (file)
@@ -18,6 +18,9 @@
     '_timer_callback',
     // Callback from button presses in the UI outside the canvas
     '_command',
+    // Game-saving functions
+    '_get_save_file',
+    '_free_save_file',
     // Callbacks to return values from dialog boxes
     '_dlg_return_sval',
     '_dlg_return_ival',
index f828ffe9f11fbaa64d1e4fa5c4ae11194fb777ea..20e2a78f2daca6e162832a380346ddbc76c56ff7 100755 (executable)
@@ -209,6 +209,7 @@ ${unfinishedpara}
 ><li id="new">New game</li
 ><li id="specific">Enter game ID</li
 ><li id="random">Enter random seed</li
+><li id="save">Download save file</li
 ></ul></li
 ><li>Type...<ul id="gametype"></ul></li
 ><li class="separator"></li