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;
|
||||
};
|
||||
|
||||
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;
|
||||
if (ctx->buffer)
|
||||
memcpy(ctx->buffer + ctx->pos, buf, len);
|
||||
ctx->pos += len;
|
||||
const unsigned char *buf = (const unsigned char *)vbuf;
|
||||
for (int i = 0; i < len; i++) {
|
||||
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)
|
||||
|
@ -379,8 +379,7 @@ function initPuzzle() {
|
||||
"Click to download the "));
|
||||
var a = document.createElement("a");
|
||||
a.download = "puzzle.sav";
|
||||
a.href = "data:application/octet-stream," +
|
||||
encodeURIComponent(savefile_text);
|
||||
a.href = "data:application/octet-stream," + savefile_text;
|
||||
a.appendChild(document.createTextNode("saved-game file"));
|
||||
dlg_form.appendChild(a);
|
||||
dlg_form.appendChild(document.createTextNode("."));
|
||||
|
Reference in New Issue
Block a user