UNPKG

emglken

Version:
1,381 lines (1,229 loc) 46.5 kB
// This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. // When targeting node and ES6 we use `await import ..` in the generated code // so the outer function needs to be marked as async. async function Module(moduleArg = {}) { var moduleRtn; // include: shell.js // include: minimum_runtime_check.js // end include: minimum_runtime_check.js // The Module object: Our interface to the outside world. We import // and export values on it. There are various ways Module can be used: // 1. Not defined. We create it here // 2. A function parameter, function(moduleArg) => Promise<Module> // 3. pre-run appended it, var Module = {}; ..generated code.. // 4. External script tag defines var Module. // We need to check if Module already exists (e.g. case 3 above). // Substitution will be replaced with actual code on later stage of the build, // this way Closure Compiler will not mangle it (e.g. case 4. above). // Note that if you want to run closure, and also to use Module // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. var Module = moduleArg; // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). // Attempt to auto-detect the environment var ENVIRONMENT_IS_WEB = !!globalThis.window; var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; // N.b. Electron.js environment is simultaneously a NODE-environment, but // also a web environment. var ENVIRONMENT_IS_NODE = globalThis.process?.versions?.node && globalThis.process?.type != "renderer"; if (ENVIRONMENT_IS_NODE) { // When building an ES module `require` is not normally available. // We need to use `createRequire()` to construct the require()` function. const {createRequire} = await import("node:module"); /** @suppress{duplicate} */ var require = createRequire(import.meta.url); } // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) // include: /src/src/preamble.js /* Emglken preamble ================ Copyright (c) 2024 Dannii Willis MIT licenced https://github.com/curiousdannii/emglken */ // Make eslint happy /* eslint no-unused-vars: "off" */ /* global callMain, Module */ let Dialog; let GlkOte; let glkote_event_data; // eslint-disable-next-line prefer-const let glkote_event_ready = () => {}; // Our main start function // We depart from the Quixe standard in these ways: // - `options.arguments` must be set in order to play a storyfile // - no longer receives the storyfile - the Dialog library will handle loading it Module["start"] = function(options) { Dialog = options.Dialog; if (!Dialog.async) { throw new Error("Emglken requires an async Dialog library"); } GlkOte = options.GlkOte; options.accept = accept; callMain(options.arguments); GlkOte.init(options); }; function accept(data) { if (glkote_event_data) { console.warn("Already have GlkOte event when next event arrives"); } glkote_event_data = data; glkote_event_ready(); } // Log output //Module['print'] = console.log // In single-file mode new URL constructor won't work Module["locateFile"] = function(filename) { try { return new URL(filename, import.meta.url).href; } catch { return filename; } }; // end include: /src/src/preamble.js var arguments_ = []; var thisProgram = "./this.program"; var quit_ = (status, toThrow) => { throw toThrow; }; var _scriptName = import.meta.url; // `/` should be present at the end if `scriptDirectory` is not empty var scriptDirectory = ""; function locateFile(path) { if (Module["locateFile"]) { return Module["locateFile"](path, scriptDirectory); } return scriptDirectory + path; } // Hooks that are implemented differently in different runtime environments. var readAsync, readBinary; if (ENVIRONMENT_IS_NODE) { // These modules will usually be used on Node.js. Load them eagerly to avoid // the complexity of lazy-loading. var fs = require("node:fs"); if (_scriptName.startsWith("file:")) { scriptDirectory = require("node:path").dirname(require("node:url").fileURLToPath(_scriptName)) + "/"; } // include: node_shell_read.js readBinary = filename => { // We need to re-wrap `file://` strings to URLs. filename = isFileURI(filename) ? new URL(filename) : filename; var ret = fs.readFileSync(filename); return ret; }; readAsync = async (filename, binary = true) => { // See the comment in the `readBinary` function. filename = isFileURI(filename) ? new URL(filename) : filename; var ret = fs.readFileSync(filename, binary ? undefined : "utf8"); return ret; }; // end include: node_shell_read.js if (process.argv.length > 1) { thisProgram = process.argv[1].replace(/\\/g, "/"); } arguments_ = process.argv.slice(2); quit_ = (status, toThrow) => { process.exitCode = status; throw toThrow; }; } else // Note that this includes Node.js workers when relevant (pthreads is enabled). // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and // ENVIRONMENT_IS_NODE. if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { try { scriptDirectory = new URL(".", _scriptName).href; } catch {} { // include: web_or_worker_shell_read.js readAsync = async url => { var response = await fetch(url, { credentials: "same-origin" }); if (response.ok) { return response.arrayBuffer(); } throw new Error(response.status + " : " + response.url); }; } } else {} var out = console.log.bind(console); var err = console.error.bind(console); // end include: shell.js // include: preamble.js // === Preamble library stuff === // Documentation for the public APIs defined in this file must be updated in: // site/source/docs/api_reference/preamble.js.rst // A prebuilt local version of the documentation is available at: // site/build/text/docs/api_reference/preamble.js.txt // You can also build docs locally as HTML or other formats in site/ // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html var wasmBinary; // Wasm globals //======================================== // Runtime essentials //======================================== // whether we are quitting the application. no code should run after this. // set in exit() and abort() var ABORT = false; // set by exit() and abort(). Passed to 'onExit' handler. // NOTE: This is also used as the process return code in shell environments // but only when noExitRuntime is false. var EXITSTATUS; /** * Indicates whether filename is delivered via file protocol (as opposed to http/https) * @noinline */ var isFileURI = filename => filename.startsWith("file://"); // include: runtime_common.js // include: runtime_stack_check.js // end include: runtime_stack_check.js // include: runtime_exceptions.js // Base Emscripten EH error class class EmscriptenEH {} class EmscriptenSjLj extends EmscriptenEH {} // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js var readyPromiseResolve, readyPromiseReject; // Memory management var runtimeInitialized = false; var runtimeExited = false; function updateMemoryViews() { var b = wasmMemory.buffer; HEAP8 = new Int8Array(b); HEAP16 = new Int16Array(b); HEAPU8 = new Uint8Array(b); HEAPU16 = new Uint16Array(b); HEAP32 = new Int32Array(b); HEAPU32 = new Uint32Array(b); HEAPF32 = new Float32Array(b); HEAPF64 = new Float64Array(b); HEAP64 = new BigInt64Array(b); HEAPU64 = new BigUint64Array(b); } // include: memoryprofiler.js // end include: memoryprofiler.js // end include: runtime_common.js function preRun() {} function initRuntime() { runtimeInitialized = true; // No ATINITS hooks wasmExports["B"](); } function preMain() {} function exitRuntime() { // PThreads reuse the runtime from the main thread. ___funcs_on_exit(); // Native atexit() functions // Begin ATEXITS hooks flush_NO_FILESYSTEM(); // End ATEXITS hooks runtimeExited = true; } function postRun() {} /** * @param {string|number=} what * @noreturn */ function abort(what) { what = `Aborted(${what})`; // TODO(sbc): Should we remove printing and leave it up to whoever // catches the exception? err(what); ABORT = true; what += ". Build with -sASSERTIONS for more info."; // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to // simply make the program stop. // FIXME This approach does not work in Wasm EH because it currently does not assume // all RuntimeErrors are from traps; it decides whether a RuntimeError is from // a trap or not based on a hidden field within the object. So at the moment // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that // allows this in the wasm spec. // Suppress closure compiler warning here. Closure compiler's builtin extern // definition for WebAssembly.RuntimeError claims it takes no arguments even // though it can. // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); readyPromiseReject?.(e); // Throw the error whether or not MODULARIZE is set because abort is used // in code paths apart from instantiation where an exception is expected // to be thrown when abort is called. throw e; } var wasmBinaryFile; function findWasmBinary() { if (Module["locateFile"]) { return locateFile("hugo.wasm"); } // Use bundler-friendly `new URL(..., import.meta.url)` pattern; works in browsers too. return new URL("hugo.wasm", import.meta.url).href; } function getBinarySync(file) { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary); } if (readBinary) { return readBinary(file); } // Throwing a plain string here, even though it not normally advisable since // this gets turning into an `abort` in instantiateArrayBuffer. throw "both async and sync fetching of the wasm failed"; } async function getWasmBinary(binaryFile) { // If we don't have the binary yet, load it asynchronously using readAsync. if (!wasmBinary) { // Fetch the binary using readAsync try { var response = await readAsync(binaryFile); return new Uint8Array(response); } catch {} } // Otherwise, getBinarySync should be able to get it synchronously return getBinarySync(binaryFile); } async function instantiateArrayBuffer(binaryFile, imports) { try { var binary = await getWasmBinary(binaryFile); var instance = await WebAssembly.instantiate(binary, imports); return instance; } catch (reason) { err(`failed to asynchronously prepare wasm: ${reason}`); abort(reason); } } async function instantiateAsync(binary, binaryFile, imports) { if (!binary && !ENVIRONMENT_IS_NODE) { try { var response = fetch(binaryFile, { credentials: "same-origin" }); var instantiationResult = await WebAssembly.instantiateStreaming(response, imports); return instantiationResult; } catch (reason) { // We expect the most common failure cause to be a bad MIME type for the binary, // in which case falling back to ArrayBuffer instantiation should work. err(`wasm streaming compile failed: ${reason}`); err("falling back to ArrayBuffer instantiation"); } } return instantiateArrayBuffer(binaryFile, imports); } function getWasmImports() { // prepare imports var imports = { "a": wasmImports }; return imports; } // Create the wasm instance. // Receives the wasm imports, returns the exports. async function createWasm() { // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { wasmExports = instance.exports; wasmExports = Asyncify.instrumentWasmExports(wasmExports); assignWasmExports(wasmExports); updateMemoryViews(); return wasmExports; } // Prefer streaming instantiation if available. function receiveInstantiationResult(result) { // 'result' is a ResultObject object which has both the module and instance. // receiveInstance() will swap in the exports (to Module.asm) so they can be called // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. // When the regression is fixed, can restore the above PTHREADS-enabled path. return receiveInstance(result["instance"]); } var info = getWasmImports(); wasmBinaryFile ??= findWasmBinary(); var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info); var exports = receiveInstantiationResult(result); return exports; } // end include: preamble.js // Begin JS library code class ExitStatus { name="ExitStatus"; constructor(status) { this.message = `Program terminated with exit(${status})`; this.status = status; } } /** @type {!Int16Array} */ var HEAP16; /** @type {!Int32Array} */ var HEAP32; /** not-@type {!BigInt64Array} */ var HEAP64; /** @type {!Int8Array} */ var HEAP8; /** @type {!Float32Array} */ var HEAPF32; /** @type {!Float64Array} */ var HEAPF64; /** @type {!Uint16Array} */ var HEAPU16; /** @type {!Uint32Array} */ var HEAPU32; /** not-@type {!BigUint64Array} */ var HEAPU64; /** @type {!Uint8Array} */ var HEAPU8; var dynCalls = {}; var ___call_sighandler = (fp, sig) => (a1 => dynCall_vi(fp, a1))(sig); var ___syscall_getcwd = (buf, size) => {}; var __abort_js = () => abort(""); var runtimeKeepaliveCounter = 0; var __emscripten_runtime_keepalive_clear = () => { runtimeKeepaliveCounter = 0; }; var isLeapYear = year => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); var MONTH_DAYS_LEAP_CUMULATIVE = [ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ]; var MONTH_DAYS_REGULAR_CUMULATIVE = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ]; var ydayFromDate = date => { var leap = isLeapYear(date.getFullYear()); var monthDaysCumulative = (leap ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE); var yday = monthDaysCumulative[date.getMonth()] + date.getDate() - 1; // -1 since it's days since Jan 1 return yday; }; var INT53_MAX = 9007199254740992; var INT53_MIN = -9007199254740992; var bigintToI53Checked = num => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num); function __localtime_js(time, tmPtr) { time = bigintToI53Checked(time); var date = new Date(time * 1e3); HEAP32[((tmPtr) >> 2)] = date.getSeconds(); HEAP32[(((tmPtr) + (4)) >> 2)] = date.getMinutes(); HEAP32[(((tmPtr) + (8)) >> 2)] = date.getHours(); HEAP32[(((tmPtr) + (12)) >> 2)] = date.getDate(); HEAP32[(((tmPtr) + (16)) >> 2)] = date.getMonth(); HEAP32[(((tmPtr) + (20)) >> 2)] = date.getFullYear() - 1900; HEAP32[(((tmPtr) + (24)) >> 2)] = date.getDay(); var yday = ydayFromDate(date) | 0; HEAP32[(((tmPtr) + (28)) >> 2)] = yday; HEAP32[(((tmPtr) + (36)) >> 2)] = -(date.getTimezoneOffset() * 60); // Attention: DST is in December in South, and some regions don't have DST at all. var start = new Date(date.getFullYear(), 0, 1); var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); var winterOffset = start.getTimezoneOffset(); var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset)) | 0; HEAP32[(((tmPtr) + (32)) >> 2)] = dst; } var timers = {}; var handleException = e => { // Certain exception types we do not treat as errors since they are used for // internal control flow. // 1. ExitStatus, which is thrown by exit() // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others // that wish to return to JS event loop. if (e instanceof ExitStatus || e == "unwind") { return EXITSTATUS; } quit_(1, e); }; var keepRuntimeAlive = () => runtimeKeepaliveCounter > 0; /** @noreturn */ var _proc_exit = code => { EXITSTATUS = code; if (!keepRuntimeAlive()) { ABORT = true; } quit_(code, new ExitStatus(code)); }; /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { EXITSTATUS = status; if (!keepRuntimeAlive()) { exitRuntime(); } _proc_exit(status); }; var _exit = exitJS; var maybeExit = () => { if (runtimeExited) { return; } if (!keepRuntimeAlive()) { try { _exit(EXITSTATUS); } catch (e) { handleException(e); } } }; var callUserCallback = func => { if (runtimeExited || ABORT) { return; } try { return func(); } catch (e) { handleException(e); } finally { maybeExit(); } }; var _emscripten_get_now = () => performance.now(); var __setitimer_js = (which, timeout_ms) => { // First, clear any existing timer. if (timers[which]) { clearTimeout(timers[which].id); delete timers[which]; } // A timeout of zero simply cancels the current timeout so we have nothing // more to do. if (!timeout_ms) return 0; var id = setTimeout(() => { delete timers[which]; callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now())); }, timeout_ms); timers[which] = { id, timeout_ms }; return 0; }; var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { // Parameter maxBytesToWrite is not optional. Negative values, 0, null, // undefined and false each don't write out any bytes. if (!(maxBytesToWrite > 0)) return 0; var startIdx = outIdx; var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. for (var i = 0; i < str.length; ++i) { // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description // and https://www.ietf.org/rfc/rfc2279.txt // and https://tools.ietf.org/html/rfc3629 var u = str.codePointAt(i); if (u <= 127) { if (outIdx >= endIdx) break; heap[outIdx++] = u; } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break; heap[outIdx++] = 192 | (u >> 6); heap[outIdx++] = 128 | (u & 63); } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break; heap[outIdx++] = 224 | (u >> 12); heap[outIdx++] = 128 | ((u >> 6) & 63); heap[outIdx++] = 128 | (u & 63); } else { if (outIdx + 3 >= endIdx) break; heap[outIdx++] = 240 | (u >> 18); heap[outIdx++] = 128 | ((u >> 12) & 63); heap[outIdx++] = 128 | ((u >> 6) & 63); heap[outIdx++] = 128 | (u & 63); // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. // We need to manually skip over the second code unit for correct iteration. i++; } } // Null-terminate the pointer to the buffer. heap[outIdx] = 0; return outIdx - startIdx; }; var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); var __tzset_js = (timezone, daylight, std_name, dst_name) => { // TODO: Use (malleable) environment variables instead of system settings. var currentYear = (new Date).getFullYear(); var winter = new Date(currentYear, 0, 1); var summer = new Date(currentYear, 6, 1); var winterOffset = winter.getTimezoneOffset(); var summerOffset = summer.getTimezoneOffset(); // Local standard timezone offset. Local standard time is not adjusted for // daylight savings. This code uses the fact that getTimezoneOffset returns // a greater value during Standard Time versus Daylight Saving Time (DST). // Thus it determines the expected output during Standard Time, and it // compares whether the output of the given date the same (Standard) or less // (DST). var stdTimezoneOffset = Math.max(winterOffset, summerOffset); // timezone is specified as seconds west of UTC ("The external variable // `timezone` shall be set to the difference, in seconds, between // Coordinated Universal Time (UTC) and local standard time."), the same // as returned by stdTimezoneOffset. // See http://pubs.opengroup.org/onlinepubs/009695399/functions/tzset.html HEAPU32[((timezone) >> 2)] = stdTimezoneOffset * 60; HEAP32[((daylight) >> 2)] = Number(winterOffset != summerOffset); var extractZone = timezoneOffset => { // Why inverse sign? // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset var sign = timezoneOffset >= 0 ? "-" : "+"; var absOffset = Math.abs(timezoneOffset); var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); var minutes = String(absOffset % 60).padStart(2, "0"); return `UTC${sign}${hours}${minutes}`; }; var winterName = extractZone(winterOffset); var summerName = extractZone(summerOffset); if (summerOffset < winterOffset) { // Northern hemisphere stringToUTF8(winterName, std_name, 17); stringToUTF8(summerName, dst_name, 17); } else { stringToUTF8(winterName, dst_name, 17); stringToUTF8(summerName, std_name, 17); } }; var _emscripten_date_now = () => Date.now(); var nowIsMonotonic = 1; var checkWasiClock = clock_id => clock_id >= 0 && clock_id <= 3; function _clock_time_get(clk_id, ignored_precision, ptime) { ignored_precision = bigintToI53Checked(ignored_precision); if (!checkWasiClock(clk_id)) { return 28; } var now; // all wasi clocks but realtime are monotonic if (clk_id === 0) { now = _emscripten_date_now(); } else if (nowIsMonotonic) { now = _emscripten_get_now(); } else { return 52; } // "now" is in ms, and wasi times are in ns. var nsec = Math.round(now * 1e3 * 1e3); HEAP64[((ptime) >> 3)] = BigInt(nsec); return 0; } function writeBuffer(buffer, data) { const ptr = _malloc(data.length); HEAP8.set(data, ptr); HEAP32[((buffer) >> 2)] = ptr; HEAP32[(((buffer) + (4)) >> 2)] = data.length; } function writeBufferJSON(buffer, data) { const json = JSON.stringify(data); writeBuffer(buffer, encoder.encode(json)); } var UTF8Decoder = new TextDecoder; var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var maxIdx = idx + maxBytesToRead; if (ignoreNul) return maxIdx; // TextDecoder needs to know the byte length in advance, it doesn't stop on // null terminator by itself. // As a tiny code save trick, compare idx against maxIdx using a negation, // so that maxBytesToRead=undefined/NaN means Infinity. while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; return idx; }; /** * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the * emscripten HEAP, returns a copy of that string as a Javascript String object. * * @param {number} ptr * @param {number=} maxBytesToRead - An optional length that specifies the * maximum number of bytes to read. You can omit this parameter to scan the * string until the first 0 byte. If maxBytesToRead is passed, and the string * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the * string will cut short at that byte index. * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. * @return {string} */ var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { if (!ptr) return ""; var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); }; function _emglken_file_delete_sync(path_ptr, path_len) { const path = UTF8ToString(path_ptr, path_len); Dialog.delete(path); } var _emglken_file_exists = function emglken_file_exists(path_ptr, path_len) { return Asyncify.handleAsync(async () => { const path = UTF8ToString(path_ptr, path_len); return Dialog.exists(path); }); }; _emglken_file_exists.isAsync = true; var _emglken_file_flush = function emglken_file_flush() { return Asyncify.handleAsync(async () => { await Dialog.write(emglken_files); emglken_files = {}; }); }; _emglken_file_flush.isAsync = true; function _emglken_file_flush_sync() { Dialog.write(emglken_files); } var _emglken_file_read = function emglken_file_read(path_ptr, path_len, buffer) { return Asyncify.handleAsync(async () => { const path = UTF8ToString(path_ptr, path_len); const data = await Dialog.read(path); if (data) { writeBuffer(buffer, data); return true; } return false; }); }; _emglken_file_read.isAsync = true; function _emglken_file_write_buffer(path_ptr, path_len, buf_ptr, buf_len) { const path = UTF8ToString(path_ptr, path_len); const data = HEAP8.subarray(buf_ptr, buf_ptr + buf_len); emglken_files[path] = data; } function _emglken_get_dirs(buffer) { const dirs = Dialog.get_dirs(); writeBufferJSON(buffer, dirs); } var _emglken_get_glkote_event = function emglken_get_glkote_event(buffer) { return Asyncify.handleAsync(async () => { if (!glkote_event_data) { await new Promise(resolve => { glkote_event_ready = resolve; }); } writeBufferJSON(buffer, glkote_event_data); glkote_event_data = null; }); }; _emglken_get_glkote_event.isAsync = true; function _emglken_send_glkote_update(update_ptr, update_len) { const obj = JSON.parse(UTF8ToString(update_ptr, update_len)); GlkOte.update(obj); } function _emglken_set_storyfile_dir(path_ptr, path_len, buffer) { const path = UTF8ToString(path_ptr, path_len); const dirs = Dialog.set_storyfile_dir(path); writeBufferJSON(buffer, dirs); } var getHeapMax = () => // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side // for any code that deals with heap sizes, which would require special // casing all heap size related code to treat 0 specially. 2147483648; var alignMemory = (size, alignment) => Math.ceil(size / alignment) * alignment; var growMemory = size => { var oldHeapSize = wasmMemory.buffer.byteLength; var pages = ((size - oldHeapSize + 65535) / 65536) | 0; try { // round size grow request up to wasm page size (fixed 64KB per spec) wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size updateMemoryViews(); return 1; } catch (e) {} }; var _emscripten_resize_heap = requestedSize => { var oldSize = HEAPU8.length; // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. requestedSize >>>= 0; // With multithreaded builds, races can happen (another thread might increase the size // in between), so return a failure, and let the caller retry. // Memory resize rules: // 1. Always increase heap size to at least the requested size, rounded up // to next page multiple. // 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap // geometrically: increase the heap size according to // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), At most // overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). // 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap // linearly: increase the heap size by at least // MEMORY_GROWTH_LINEAR_STEP bytes. // 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by // MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest // 4. If we were unable to allocate as much memory, it may be due to // over-eager decision to excessively reserve due to (3) above. // Hence if an allocation fails, cut down on the amount of excess // growth, in an attempt to succeed to perform a smaller allocation. // A limit is set for how much we can grow. We should not exceed that // (the wasm binary specifies it, so if we tried, we'd fail anyhow). var maxHeapSize = getHeapMax(); if (requestedSize > maxHeapSize) { return false; } // Loop through potential heap size increases. If we attempt a too eager // reservation that fails, cut down on the attempted size and reserve a // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + .2 / cutDown); // ensure geometric growth // but limit overreserving (default to capping at +96MB overgrowth at most) overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296); var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)); var replacement = growMemory(newSize); if (replacement) { return true; } } return false; }; var ENV = {}; var getExecutableName = () => thisProgram || "./this.program"; var getEnvStrings = () => { if (!getEnvStrings.strings) { // Default values. // Browser language detection #8751 var lang = (globalThis.navigator?.language ?? "C").replace("-", "_") + ".UTF-8"; var env = { "USER": "web_user", "LOGNAME": "web_user", "PATH": "/", "PWD": "/", "HOME": "/home/web_user", "LANG": lang, "_": getExecutableName() }; // Apply the user-provided values, if any. for (var x in ENV) { // x is a key in ENV; if ENV[x] is undefined, that means it was // explicitly set to be so. We allow user code to do that to // force variables with default values to remain unset. if (ENV[x] === undefined) delete env[x]; else env[x] = ENV[x]; } var strings = []; for (var x in env) { strings.push(`${x}=${env[x]}`); } getEnvStrings.strings = strings; } return getEnvStrings.strings; }; var _environ_get = (__environ, environ_buf) => { var bufSize = 0; var envp = 0; for (var string of getEnvStrings()) { var ptr = environ_buf + bufSize; HEAPU32[(((__environ) + (envp)) >> 2)] = ptr; bufSize += stringToUTF8(string, ptr, Infinity) + 1; envp += 4; } return 0; }; var lengthBytesUTF8 = str => { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code // unit, not a Unicode code point of the character! So decode // UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 var c = str.charCodeAt(i); // possibly a lead surrogate if (c <= 127) { len++; } else if (c <= 2047) { len += 2; } else if (c >= 55296 && c <= 57343) { len += 4; ++i; } else { len += 3; } } return len; }; var _environ_sizes_get = (penviron_count, penviron_buf_size) => { var strings = getEnvStrings(); HEAPU32[((penviron_count) >> 2)] = strings.length; var bufSize = 0; for (var string of strings) { bufSize += lengthBytesUTF8(string) + 1; } HEAPU32[((penviron_buf_size) >> 2)] = bufSize; return 0; }; var printCharBuffers = [ null, [], [] ]; /** * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given * array that contains uint8 values, returns a copy of that string as a * Javascript String object. * heapOrArray is either a regular array, or a JavaScript typed array view. * @param {number=} idx * @param {number=} maxBytesToRead * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. * @return {string} */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr))); }; var printChar = (stream, curr) => { var buffer = printCharBuffers[stream]; if (curr === 0 || curr === 10) { (stream === 1 ? out : err)(UTF8ArrayToString(buffer)); buffer.length = 0; } else { buffer.push(curr); } }; var flush_NO_FILESYSTEM = () => { // flush anything remaining in the buffers during shutdown _fflush(0); if (printCharBuffers[1].length) printChar(1, 10); if (printCharBuffers[2].length) printChar(2, 10); }; var _fd_write = (fd, iov, iovcnt, pnum) => { // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 var num = 0; for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[((iov) >> 2)]; var len = HEAPU32[(((iov) + (4)) >> 2)]; iov += 8; for (var j = 0; j < len; j++) { printChar(fd, HEAPU8[ptr + j]); } num += len; } HEAPU32[((pnum) >> 2)] = num; return 0; }; var initRandomFill = () => { // This block is not needed on v19+ since crypto.getRandomValues is builtin if (ENVIRONMENT_IS_NODE) { var nodeCrypto = require("node:crypto"); return view => nodeCrypto.randomFillSync(view); } return view => (crypto.getRandomValues(view), 0); }; var randomFill = view => (randomFill = initRandomFill())(view); var _random_get = (buffer, size) => randomFill(HEAPU8.subarray(buffer, buffer + size)); var stackAlloc = sz => __emscripten_stack_alloc(sz); var stringToUTF8OnStack = str => { var size = lengthBytesUTF8(str) + 1; var ret = stackAlloc(size); stringToUTF8(str, ret, size); return ret; }; var runAndAbortIfError = func => { try { return func(); } catch (e) { abort(e); } }; var runtimeKeepalivePush = () => { runtimeKeepaliveCounter += 1; }; var runtimeKeepalivePop = () => { runtimeKeepaliveCounter -= 1; }; var Asyncify = { instrumentWasmImports(imports) { var importPattern = /^(__asyncjs__.*)$/; for (let [x, original] of Object.entries(imports)) { if (typeof original == "function") { let isAsyncifyImport = original.isAsync || importPattern.test(x); } } }, instrumentFunction(original) { var wrapper = (...args) => { Asyncify.exportCallStack.push(original); try { return original(...args); } finally { if (!ABORT) { var top = Asyncify.exportCallStack.pop(); Asyncify.maybeStopUnwind(); } } }; Asyncify.funcWrappers.set(original, wrapper); return wrapper; }, instrumentWasmExports(exports) { var ret = {}; for (let [x, original] of Object.entries(exports)) { if (typeof original == "function") { var wrapper = Asyncify.instrumentFunction(original); ret[x] = wrapper; } else { ret[x] = original; } } return ret; }, State: { Normal: 0, Unwinding: 1, Rewinding: 2, Disabled: 3 }, state: 0, StackSize: 8192, currData: null, handleSleepReturnValue: 0, exportCallStack: [], callstackFuncToId: new Map, callStackIdToFunc: new Map, funcWrappers: new Map, callStackId: 0, asyncPromiseHandlers: null, sleepCallbacks: [], getCallStackId(func) { if (!Asyncify.callstackFuncToId.has(func)) { var id = Asyncify.callStackId++; Asyncify.callstackFuncToId.set(func, id); Asyncify.callStackIdToFunc.set(id, func); } return Asyncify.callstackFuncToId.get(func); }, maybeStopUnwind() { if (Asyncify.currData && Asyncify.state === Asyncify.State.Unwinding && Asyncify.exportCallStack.length === 0) { // We just finished unwinding. // Be sure to set the state before calling any other functions to avoid // possible infinite recursion here (For example in debug pthread builds // the dbg() function itself can call back into WebAssembly to get the // current pthread_self() pointer). Asyncify.state = Asyncify.State.Normal; runtimeKeepalivePush(); // Keep the runtime alive so that a re-wind can be done later. runAndAbortIfError(_asyncify_stop_unwind); if (typeof Fibers != "undefined") { Fibers.trampoline(); } } }, whenDone() { return new Promise((resolve, reject) => { Asyncify.asyncPromiseHandlers = { resolve, reject }; }); }, allocateData() { // An asyncify data structure has three fields: // 0 current stack pos // 4 max stack pos // 8 id of function at bottom of the call stack (callStackIdToFunc[id] == wasm func) // The Asyncify ABI only interprets the first two fields, the rest is for the runtime. // We also embed a stack in the same memory region here, right next to the structure. // This struct is also defined as asyncify_data_t in emscripten/fiber.h var ptr = _malloc(12 + Asyncify.StackSize); Asyncify.setDataHeader(ptr, ptr + 12, Asyncify.StackSize); Asyncify.setDataRewindFunc(ptr); return ptr; }, setDataHeader(ptr, stack, stackSize) { HEAPU32[((ptr) >> 2)] = stack; HEAPU32[(((ptr) + (4)) >> 2)] = stack + stackSize; }, setDataRewindFunc(ptr) { var bottomOfCallStack = Asyncify.exportCallStack[0]; var rewindId = Asyncify.getCallStackId(bottomOfCallStack); HEAP32[(((ptr) + (8)) >> 2)] = rewindId; }, getDataRewindFunc(ptr) { var id = HEAP32[(((ptr) + (8)) >> 2)]; var func = Asyncify.callStackIdToFunc.get(id); return func; }, doRewind(ptr) { var original = Asyncify.getDataRewindFunc(ptr); var func = Asyncify.funcWrappers.get(original); // Once we have rewound and the stack we no longer need to artificially // keep the runtime alive. runtimeKeepalivePop(); return callUserCallback(func); }, handleSleep(startAsync) { if (ABORT) return; if (Asyncify.state === Asyncify.State.Normal) { // Prepare to sleep. Call startAsync, and see what happens: // if the code decided to call our callback synchronously, // then no async operation was in fact begun, and we don't // need to do anything. var reachedCallback = false; var reachedAfterCallback = false; startAsync((handleSleepReturnValue = 0) => { if (ABORT) return; Asyncify.handleSleepReturnValue = handleSleepReturnValue; reachedCallback = true; if (!reachedAfterCallback) { // We are happening synchronously, so no need for async. return; } Asyncify.state = Asyncify.State.Rewinding; runAndAbortIfError(() => _asyncify_start_rewind(Asyncify.currData)); if (typeof MainLoop != "undefined" && MainLoop.func) { MainLoop.resume(); } var asyncWasmReturnValue, isError = false; try { asyncWasmReturnValue = Asyncify.doRewind(Asyncify.currData); } catch (err) { asyncWasmReturnValue = err; isError = true; } // Track whether the return value was handled by any promise handlers. var handled = false; if (!Asyncify.currData) { // All asynchronous execution has finished. // `asyncWasmReturnValue` now contains the final // return value of the exported async WASM function. // Note: `asyncWasmReturnValue` is distinct from // `Asyncify.handleSleepReturnValue`. // `Asyncify.handleSleepReturnValue` contains the return // value of the last C function to have executed // `Asyncify.handleSleep()`, whereas `asyncWasmReturnValue` // contains the return value of the exported WASM function // that may have called C functions that // call `Asyncify.handleSleep()`. var asyncPromiseHandlers = Asyncify.asyncPromiseHandlers; if (asyncPromiseHandlers) { Asyncify.asyncPromiseHandlers = null; (isError ? asyncPromiseHandlers.reject : asyncPromiseHandlers.resolve)(asyncWasmReturnValue); handled = true; } } if (isError && !handled) { // If there was an error and it was not handled by now, we have no choice but to // rethrow that error into the global scope where it can be caught only by // `onerror` or `onunhandledpromiserejection`. throw asyncWasmReturnValue; } }); reachedAfterCallback = true; if (!reachedCallback) { // A true async operation was begun; start a sleep. Asyncify.state = Asyncify.State.Unwinding; // TODO: reuse, don't alloc/free every sleep Asyncify.currData = Asyncify.allocateData(); if (typeof MainLoop != "undefined" && MainLoop.func) { MainLoop.pause(); } runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData)); } } else if (Asyncify.state === Asyncify.State.Rewinding) { // Stop a resume. Asyncify.state = Asyncify.State.Normal; runAndAbortIfError(_asyncify_stop_rewind); _free(Asyncify.currData); Asyncify.currData = null; // Call all sleep callbacks now that the sleep-resume is all done. Asyncify.sleepCallbacks.forEach(callUserCallback); } else { abort(`invalid state: ${Asyncify.state}`); } return Asyncify.handleSleepReturnValue; }, handleAsync: startAsync => Asyncify.handleSleep(async wakeUp => { // TODO: add error handling as a second param when handleSleep implements it. wakeUp(await startAsync()); }) }; let emglken_files = {}; const encoder = new TextEncoder; // End JS library code // include: postlibrary.js // This file is included after the automatically-generated JS library code // but before the wasm module is created. { // Begin ATMODULES hooks if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; } // Begin runtime exports // End runtime exports // Begin JS library exports // End JS library exports // end include: postlibrary.js // Imports from the Wasm binary. var _malloc, _free, _main, ___funcs_on_exit, _fflush, __emscripten_timeout, _setThrew, __emscripten_stack_alloc, dynCall_iii, dynCall_vii, dynCall_vi, dynCall_iiii, dynCall_viiii, dynCall_ii, dynCall_jiji, dynCall_iidiiii, _asyncify_start_unwind, _asyncify_stop_unwind, _asyncify_start_rewind, _asyncify_stop_rewind, memory, __indirect_function_table, wasmMemory; function assignWasmExports(wasmExports) { _malloc = wasmExports["C"]; _free = wasmExports["D"]; _main = Module["_main"] = wasmExports["E"]; ___funcs_on_exit = wasmExports["F"]; _fflush = wasmExports["G"]; __emscripten_timeout = wasmExports["H"]; _setThrew = Module["_setThrew"] = wasmExports["I"]; __emscripten_stack_alloc = wasmExports["J"]; dynCall_iii = dynCalls["iii"] = wasmExports["K"]; dynCall_vii = dynCalls["vii"] = wasmExports["L"]; dynCall_vi = dynCalls["vi"] = wasmExports["M"]; dynCall_iiii = dynCalls["iiii"] = wasmExports["N"]; dynCall_viiii = dynCalls["viiii"] = wasmExports["O"]; dynCall_ii = dynCalls["ii"] = wasmExports["P"]; dynCall_jiji = dynCalls["jiji"] = wasmExports["Q"]; dynCall_iidiiii = dynCalls["iidiiii"] = wasmExports["R"]; _asyncify_start_unwind = wasmExports["S"]; _asyncify_stop_unwind = wasmExports["T"]; _asyncify_start_rewind = wasmExports["U"]; _asyncify_stop_rewind = wasmExports["V"]; memory = wasmMemory = wasmExports["A"]; __indirect_function_table = wasmExports["__indirect_function_table"]; } var wasmImports = { /** @export */ z: ___call_sighandler, /** @export */ y: ___syscall_getcwd, /** @export */ s: __abort_js, /** @export */ r: __emscripten_runtime_keepalive_clear, /** @export */ q: __localtime_js, /** @export */ p: __setitimer_js, /** @export */ o: __tzset_js, /** @export */ x: _clock_time_get, /** @export */ n: _emglken_file_delete_sync, /** @export */ m: _emglken_file_exists, /** @export */ d: _emglken_file_flush, /** @export */ l: _emglken_file_flush_sync, /** @export */ k: _emglken_file_read, /** @export */ c: _emglken_file_write_buffer, /** @export */ j: _emglken_get_dirs, /** @export */ i: _emglken_get_glkote_event, /** @export */ b: _emglken_send_glkote_update, /** @export */ h: _emglken_set_storyfile_dir, /** @export */ g: _emscripten_date_now, /** @export */ f: _emscripten_resize_heap, /** @export */ w: _environ_get, /** @export */ v: _environ_sizes_get, /** @export */ e: _exit, /** @export */ a: _fd_write, /** @export */ u: _proc_exit, /** @export */ t: _random_get }; // include: postamble.js // === Auto-generated postamble setup entry stuff === function callMain(args = []) { var entryFunction = _main; args.unshift(thisProgram); var argc = args.length; var argv = stackAlloc((argc + 1) * 4); var argv_ptr = argv; for (var arg of args) { HEAPU32[((argv_ptr) >> 2)] = stringToUTF8OnStack(arg); argv_ptr += 4; } HEAPU32[((argv_ptr) >> 2)] = 0; try { var ret = entryFunction(argc, argv); // if we're not running an evented main loop, it's time to exit exitJS(ret, /* implicit = */ true); return ret; } catch (e) { return handleException(e); } } function run(args = arguments_) { preRun(); function doRun() { // run may have just been called through dependencies being fulfilled just in this very frame, // or while the async setStatus time below was happening Module["calledRun"] = true; if (ABORT) return; initRuntime(); preMain(); readyPromiseResolve?.(Module); var noInitialRun = true; if (!noInitialRun) callMain(args); postRun(); } { doRun(); } } var wasmExports; // In modularize mode the generated code is within a factory function so we // can use await here (since it's not top-level-await). wasmExports = await (createWasm()); run(); // end include: postamble.js // include: postamble_modularize.js // In MODULARIZE mode we wrap the generated code in a factory function // and return either the Module itself, or a promise of the module. // We assign to the `moduleRtn` global here and configure closure to see // this as an extern so it won't get minified. if (runtimeInitialized) { moduleRtn = Module; } else { // Set up the promise that indicates the Module is initialized moduleRtn = new Promise((resolve, reject) => { readyPromiseResolve = resolve; readyPromiseReject = reject; }); } return moduleRtn; } // Export using a UMD style export, or ES6 exports if selected export default Module;