ref: 721119e4a61cbb261b456dfd134811d7beb5ce98
parent: 1bf591a5735068d1853be13c5a4255962835d5fe
author: Simon Tatham <anakin@pobox.com>
date: Tue Sep 5 16:48:42 EDT 2017
Support for loading games in Javascript puzzles. This is done by showing a dialog containing an <input type="file"> through which the user can 'upload' a save file - though, of course, the 'upload' doesn't go to any HTTP server, but only into the mind of the Javascript running in the same browser. It would be even nicer to support drag-and-drop as an alternative UI for getting the save file into the browser, but that isn't critical to getting the first version of this feature out of the door.
--- a/emcc.c
+++ b/emcc.c
@@ -766,9 +766,9 @@
size_t pos;
};
-static void savefile_write(void *wctx, void *buf, int len)
+static void savefile_write(void *vctx, void *buf, int len)
{
- struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)wctx;
+ struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)vctx;
if (ctx->buffer)
memcpy(ctx->buffer + ctx->pos, buf, len);
ctx->pos += len;
@@ -797,6 +797,40 @@
void free_save_file(char *buffer)
{
sfree(buffer);
+}
+
+struct savefile_read_ctx {
+ const char *buffer;
+ int len_remaining;
+};
+
+static int savefile_read(void *vctx, void *buf, int len)
+{
+ struct savefile_read_ctx *ctx = (struct savefile_read_ctx *)vctx;
+ if (ctx->len_remaining < len)
+ return FALSE;
+ memcpy(buf, ctx->buffer, len);
+ ctx->len_remaining -= len;
+ ctx->buffer += len;
+ return TRUE;
+}
+
+void load_game(const char *buffer, int len)
+{
+ struct savefile_read_ctx ctx;
+ const char *err;
+
+ ctx.buffer = buffer;
+ ctx.len_remaining = len;
+ err = midend_deserialise(me, savefile_read, &ctx);
+
+ if (err) {
+ js_error_box(err);
+ } else {
+ select_appropriate_preset();
+ resize();
+ midend_redraw(me);
+ }
}
/* ----------------------------------------------------------------------
--- a/emccpre.js
+++ b/emccpre.js
@@ -299,6 +299,7 @@
// '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']);
+ load_game = Module.cwrap('load_game', 'void', ['string', 'number']);
document.getElementById("save").onclick = function(event) {
if (dlg_dimmer === null) {
@@ -317,6 +318,31 @@
dlg_form.appendChild(document.createTextNode("."));
dlg_form.appendChild(document.createElement("br"));
dialog_launch(function(event) {
+ dialog_cleanup();
+ });
+ }
+ };
+
+ document.getElementById("load").onclick = function(event) {
+ if (dlg_dimmer === null) {
+ dialog_init("Upload saved-game file");
+ var input = document.createElement("input");
+ input.type = "file";
+ input.multiple = false;
+ dlg_form.appendChild(input);
+ dlg_form.appendChild(document.createElement("br"));
+ dialog_launch(function(event) {
+ if (input.files.length == 1) {
+ var file = input.files.item(0);
+ var reader = new FileReader();
+ reader.addEventListener("loadend", function() {
+ var string = reader.result;
+ load_game(string, string.length);
+ });
+ reader.readAsBinaryString(file);
+ }
+ dialog_cleanup();
+ }, function(event) {
dialog_cleanup();
});
}
--- a/emccx.json
+++ b/emccx.json
@@ -18,9 +18,10 @@
'_timer_callback',
// Callback from button presses in the UI outside the canvas
'_command',
- // Game-saving functions
+ // Game-saving and game-loading functions
'_get_save_file',
'_free_save_file',
+ '_load_game',
// Callbacks to return values from dialog boxes
'_dlg_return_sval',
'_dlg_return_ival',
--- a/html/jspage.pl
+++ b/html/jspage.pl
@@ -210,6 +210,7 @@
><li id="specific">Enter game ID</li
><li id="random">Enter random seed</li
><li id="save">Download save file</li
+><li id="load">Upload save file</li
></ul></li
><li>Type...<ul id="gametype"></ul></li
><li class="separator"></li