mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-20 23:51:29 -07:00
Support for saving games in Javascript puzzles.
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.
This commit is contained in:
43
emcc.c
43
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()
|
||||
|
26
emccpre.js
26
emccpre.js
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user