mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -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
|
* 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()
|
||||||
|
26
emccpre.js
26
emccpre.js
@ -296,6 +296,32 @@ function initPuzzle() {
|
|||||||
command(9);
|
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");
|
gametypelist = document.getElementById("gametype");
|
||||||
gametypesubmenus.push(gametypelist);
|
gametypesubmenus.push(gametypelist);
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
'_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
|
||||||
|
'_get_save_file',
|
||||||
|
'_free_save_file',
|
||||||
// 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',
|
||||||
|
@ -209,6 +209,7 @@ ${unfinishedpara}
|
|||||||
><li id="new">New game</li
|
><li id="new">New game</li
|
||||||
><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
|
||||||
></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
|
||||||
|
Reference in New Issue
Block a user