mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
WASM: move save file encoding from JS into C.
The previous fix worked OK, but it was conceptually wrong. Puzzles save files are better regarded as binary, not text: the length fields are measured in bytes, so translating the file into a different multibyte character encoding would invalidate them. So it was wrong to fetch a C byte string containing the exactly right binary data, then translate it into a Javascript string as if decoding from UTF-8, then retranslate to a representation of a bytewise encoding via encodeURIComponent, and then label the result as application/octet-stream. This probably wouldn't have caused any problems in practice, because I don't remember any situation in which my save files use characters outside printable ASCII (plus newline). But it's not actually forbidden, so a save file might choose to do that some day, so that UTF-8 decode/reencode hidden in the JS was a latent bug. Now the URI-encoding is done on the C side, while we still know exactly what the binary data ought to look like and can be sure we're translating it byte for byte into the output encoding for the data: URI. By the time the JS receives a string pointer from get_save_file, it's already URI-encoded, which _guarantees_ that it's in ASCII and won't be messed about with by Emscripten's UTF8ToString.
This commit is contained in:
49
emcc.c
49
emcc.c
@ -787,12 +787,53 @@ struct savefile_write_ctx {
|
|||||||
size_t pos;
|
size_t pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void savefile_write(void *vctx, const void *buf, int len)
|
static void savefile_write(void *vctx, const void *vbuf, int len)
|
||||||
{
|
{
|
||||||
|
static const unsigned char length[256] = {
|
||||||
|
/*
|
||||||
|
* Assign a length of 1 to any printable ASCII character that
|
||||||
|
* can be written literally in URI-encoding, i.e.
|
||||||
|
*
|
||||||
|
* A-Z a-z 0-9 - _ . ! ~ * ' ( )
|
||||||
|
*
|
||||||
|
* Assign length 3 (for % and two hex digits) to all other
|
||||||
|
* byte values.
|
||||||
|
*/
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 1, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 1, 1, 3,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1,
|
||||||
|
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
};
|
||||||
|
static const char hexdigits[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)vctx;
|
struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)vctx;
|
||||||
if (ctx->buffer)
|
const unsigned char *buf = (const unsigned char *)vbuf;
|
||||||
memcpy(ctx->buffer + ctx->pos, buf, len);
|
for (int i = 0; i < len; i++) {
|
||||||
ctx->pos += len;
|
unsigned char c = buf[i];
|
||||||
|
int clen = length[c];
|
||||||
|
if (ctx->buffer) {
|
||||||
|
if (clen == 1) {
|
||||||
|
ctx->buffer[ctx->pos] = c;
|
||||||
|
} else {
|
||||||
|
ctx->buffer[ctx->pos] = '%';
|
||||||
|
ctx->buffer[ctx->pos+1] = hexdigits[c >> 4];
|
||||||
|
ctx->buffer[ctx->pos+2] = hexdigits[c & 0xF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->pos += clen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_save_file(void)
|
char *get_save_file(void)
|
||||||
|
@ -379,8 +379,7 @@ function initPuzzle() {
|
|||||||
"Click to download the "));
|
"Click to download the "));
|
||||||
var a = document.createElement("a");
|
var a = document.createElement("a");
|
||||||
a.download = "puzzle.sav";
|
a.download = "puzzle.sav";
|
||||||
a.href = "data:application/octet-stream," +
|
a.href = "data:application/octet-stream," + savefile_text;
|
||||||
encodeURIComponent(savefile_text);
|
|
||||||
a.appendChild(document.createTextNode("saved-game file"));
|
a.appendChild(document.createTextNode("saved-game file"));
|
||||||
dlg_form.appendChild(a);
|
dlg_form.appendChild(a);
|
||||||
dlg_form.appendChild(document.createTextNode("."));
|
dlg_form.appendChild(document.createTextNode("."));
|
||||||
|
Reference in New Issue
Block a user