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.
This commit is contained in:
Simon Tatham
2017-09-05 20:48:42 +01:00
parent 1bf591a573
commit 721119e4a6
4 changed files with 65 additions and 3 deletions

38
emcc.c
View File

@ -766,9 +766,9 @@ struct savefile_write_ctx {
size_t pos; 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) if (ctx->buffer)
memcpy(ctx->buffer + ctx->pos, buf, len); memcpy(ctx->buffer + ctx->pos, buf, len);
ctx->pos += len; ctx->pos += len;
@ -799,6 +799,40 @@ void free_save_file(char *buffer)
sfree(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);
}
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
* Setup function called at page load time. It's called main() because * 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() * that's the most convenient thing in Emscripten, but it's not main()

View File

@ -299,6 +299,7 @@ function initPuzzle() {
// 'number' is used for C pointers // 'number' is used for C pointers
get_save_file = Module.cwrap('get_save_file', 'number', []); get_save_file = Module.cwrap('get_save_file', 'number', []);
free_save_file = Module.cwrap('free_save_file', 'void', ['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) { document.getElementById("save").onclick = function(event) {
if (dlg_dimmer === null) { if (dlg_dimmer === null) {
@ -322,6 +323,31 @@ function initPuzzle() {
} }
}; };
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();
});
}
};
gametypelist = document.getElementById("gametype"); gametypelist = document.getElementById("gametype");
gametypesubmenus.push(gametypelist); gametypesubmenus.push(gametypelist);

View File

@ -18,9 +18,10 @@
'_timer_callback', '_timer_callback',
// Callback from button presses in the UI outside the canvas // Callback from button presses in the UI outside the canvas
'_command', '_command',
// Game-saving functions // Game-saving and game-loading functions
'_get_save_file', '_get_save_file',
'_free_save_file', '_free_save_file',
'_load_game',
// Callbacks to return values from dialog boxes // Callbacks to return values from dialog boxes
'_dlg_return_sval', '_dlg_return_sval',
'_dlg_return_ival', '_dlg_return_ival',

View File

@ -210,6 +210,7 @@ ${unfinishedpara}
><li id="specific">Enter game ID</li ><li id="specific">Enter game ID</li
><li id="random">Enter random seed</li ><li id="random">Enter random seed</li
><li id="save">Download save file</li ><li id="save">Download save file</li
><li id="load">Upload save file</li
></ul></li ></ul></li
><li>Type...<ul id="gametype"></ul></li ><li>Type...<ul id="gametype"></ul></li
><li class="separator"></li ><li class="separator"></li