@thenick775/mgba-wasm
Version:
mGBA emulator compiled to webassembly
1,298 lines (1,083 loc) • 486 kB
JavaScript
var mGBA = (() => {
var _scriptName = import.meta.url;
return (
async function(moduleArg = {}) {
var moduleRtn;
// include: shell.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;
// Set up the promise that indicates the Module is initialized
var readyPromiseResolve, readyPromiseReject;
var readyPromise = new Promise((resolve, reject) => {
readyPromiseResolve = resolve;
readyPromiseReject = reject;
});
// 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 = typeof window == 'object';
var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != 'undefined';
// N.b. Electron.js environment is simultaneously a NODE-environment, but
// also a web environment.
var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' && process.type != 'renderer';
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
// Three configurations we can be running in:
// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false)
// 2) We could be the application main() thread proxied to worker. (with Emscripten -sPROXY_TO_WORKER) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false)
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
// The way we signal to a worker that it is hosting a pthread is to construct
// it with a specific name.
var ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && self.name?.startsWith('em-pthread');
// --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: /home/mgba/src/src/platform/wasm/pre.js
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Module.loadGame = (romPath, savePathOverride) => {
const loadGame = cwrap('loadGame', 'number', ['string', 'string']);
if (loadGame(romPath, savePathOverride)) {
const arr = romPath.split('.');
arr.pop();
const saveName = arr.join('.') + '.sav';
const autoSaveStateName = arr.join('.') + '_auto.ss';
Module.gameName = romPath;
Module.saveName =
savePathOverride ?? saveName.replace('/data/games/', '/data/saves/');
Module.autoSaveStateName = autoSaveStateName.replace(
'/data/games/',
'/autosave/'
);
return true;
}
return false;
};
Module.getSave = () => {
const exists = FS.analyzePath(Module.saveName).exists;
return exists ? FS.readFile(Module.saveName) : null;
};
Module.listRoms = () => {
return FS.readdir('/data/games/');
};
Module.listSaves = () => {
return FS.readdir('/data/saves/');
};
Module.FSInit = () => {
return new Promise((resolve, reject) => {
FS.mkdir('/data');
FS.mount(FS.filesystems.IDBFS, {}, '/data');
// mount auto save directory, this should auto persist, while the data mount should not
FS.mkdir('/autosave');
FS.mount(FS.filesystems.IDBFS, { autoPersist: true }, '/autosave');
// load data from IDBFS
FS.syncfs(true, (err) => {
if (err) {
reject(new Error(`Error syncing app data from IndexedDB: ${err}`));
}
// When we read from indexedb, these directories may or may not exist.
// If we mkdir and they already exist they throw, so just catch all of them.
try {
FS.mkdir('/data/saves');
} catch (e) {}
try {
FS.mkdir('/data/states');
} catch (e) {}
try {
FS.mkdir('/data/games');
} catch (e) {}
try {
FS.mkdir('/data/cheats');
} catch (e) {}
try {
FS.mkdir('/data/screenshots');
} catch (e) {}
try {
FS.mkdir('/data/patches');
} catch (e) {}
resolve();
});
});
};
Module.FSSync = () => {
return new Promise((resolve, reject) => {
// write data to IDBFS
FS.syncfs((err) => {
if (err) {
reject(new Error(`Error syncing app data to IndexedDB: ${err}`));
}
resolve();
});
});
};
Module.filePaths = () => {
return {
root: '/data',
cheatsPath: '/data/cheats',
gamePath: '/data/games',
savePath: '/data/saves',
saveStatePath: '/data/states',
screenshotsPath: '/data/screenshots',
patchPath: '/data/patches',
autosave: '/autosave',
};
};
Module.uploadSaveOrSaveState = (file, callback) => {
const split = file.name.split('.');
if (split.length < 2) {
console.warn('unrecognized file extension: ' + file.name);
return;
}
const extension = split[split.length - 1].toLowerCase();
let dir = null;
if (extension == 'sav') {
dir = '/data/saves/';
} else if (extension.startsWith('ss')) {
dir = '/data/states/';
} else {
console.warn('unrecognized file extension: ' + extension);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
FS.writeFile(dir + file.name, new Uint8Array(e.target.result));
if (callback) {
callback();
}
};
reader.readAsArrayBuffer(file);
};
Module.uploadRom = (file, callback) => {
const split = file.name.split('.');
if (split.length < 2) {
console.warn('unrecognized file extension: ' + file.name);
return;
}
const extension = split[split.length - 1].toLowerCase();
let dir = null;
if (['gba', 'gbc', 'gb', 'zip', '7z'].includes(extension)) {
dir = '/data/games/';
} else {
console.warn('unrecognized file extension: ' + extension);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
FS.writeFile(dir + file.name, new Uint8Array(e.target.result));
if (callback) {
callback();
}
};
reader.readAsArrayBuffer(file);
};
Module.uploadCheats = (file, callback) => {
const split = file.name.split('.');
if (split.length < 2) {
console.warn('unrecognized file extension: ' + file.name);
return;
}
const extension = split[split.length - 1].toLowerCase();
let dir = null;
if (extension == 'cheats') {
dir = '/data/cheats/';
} else {
console.warn('unrecognized file extension: ' + extension);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
FS.writeFile(dir + file.name, new Uint8Array(e.target.result));
if (callback) {
callback();
}
};
reader.readAsArrayBuffer(file);
};
Module.uploadScreenshot = (file, callback) => {
const split = file.name.split('.');
if (split.length < 2) {
console.warn('unrecognized file extension: ' + file.name);
return;
}
const extension = split[split.length - 1].toLowerCase();
let dir = null;
if (extension == 'png') {
dir = '/data/screenshots/';
} else {
console.warn('unrecognized file extension: ' + extension);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
FS.writeFile(dir + file.name, new Uint8Array(e.target.result));
if (callback) {
callback();
}
};
reader.readAsArrayBuffer(file);
};
Module.uploadPatch = (file, callback) => {
const split = file.name.split('.');
if (split.length < 2) {
console.warn('unrecognized file extension: ' + file.name);
return;
}
const extension = split[split.length - 1].toLowerCase();
let dir = null;
if (['ips', 'ups', 'bps'].includes(extension)) {
dir = '/data/patches/';
} else {
console.warn('unrecognized file extension: ' + extension);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
FS.writeFile(dir + file.name, new Uint8Array(e.target.result));
if (callback) {
callback();
}
};
reader.readAsArrayBuffer(file);
};
const keyBindings = new Map([
['a', 0],
['b', 1],
['select', 2],
['start', 3],
['right', 4],
['left', 5],
['up', 6],
['down', 7],
['r', 8],
['l', 9],
]);
Module.buttonPress = (name) => {
const buttonPress = cwrap('buttonPress', null, ['number']);
buttonPress(keyBindings.get(name.toLowerCase()));
};
Module.buttonUnpress = (name) => {
const buttonUnpress = cwrap('buttonUnpress', null, ['number']);
buttonUnpress(keyBindings.get(name.toLowerCase()));
};
// bindingName is the key name you want to associate to an input, ex. 'p' key binding -> 'a' input
// inputName is the name of the input to bind to, ex 'a', 'b', 'up' etc.
Module.bindKey = (bindingName, inputName) => {
const bindKey = cwrap('bindKey', null, ['string', 'number']);
bindKey(bindingName, keyBindings.get(inputName.toLowerCase()));
};
Module.pauseGame = () => {
const pauseGame = cwrap('pauseGame', null, []);
pauseGame();
};
Module.resumeGame = () => {
const resumeGame = cwrap('resumeGame', null, []);
resumeGame();
};
Module.pauseAudio = () => {
const pauseAudio = cwrap('pauseAudio', null, []);
pauseAudio();
};
Module.resumeAudio = () => {
const resumeAudio = cwrap('resumeAudio', null, []);
resumeAudio();
};
Module.getVolume = () => {
const getVolume = cwrap('getVolume', 'number', []);
return getVolume();
};
Module.setVolume = (percent) => {
const setVolume = cwrap('setVolume', null, ['number']);
setVolume(percent);
};
Module.getMainLoopTimingMode = () => {
const getMainLoopTimingMode = cwrap('getMainLoopTimingMode', 'number', []);
return getMainLoopTimingMode();
};
Module.getMainLoopTimingValue = () => {
const getMainLoopTimingValue = cwrap('getMainLoopTimingValue', 'number', []);
return getMainLoopTimingValue();
};
Module.setMainLoopTiming = (mode, value) => {
const setMainLoopTiming = cwrap('setMainLoopTiming', 'number', [
'number',
'number',
]);
setMainLoopTiming(mode, value);
};
Module.quitGame = () => {
const quitGame = cwrap('quitGame', null, []);
quitGame();
};
Module.quitMgba = () => {
const quitMgba = cwrap('quitMgba', null, []);
quitMgba();
};
Module.quickReload = () => {
const quickReload = cwrap('quickReload', null, []);
quickReload();
};
Module.toggleInput = (toggle) => {
const setEventEnable = cwrap('setEventEnable', null, ['boolean']);
setEventEnable(toggle);
};
Module.screenshot = (fileName) => {
const screenshot = cwrap('screenshot', 'boolean', ['string']);
return screenshot(fileName);
};
Module.saveState = (slot) => {
const saveState = cwrap('saveState', 'boolean', ['number']);
return saveState(slot);
};
Module.loadState = (slot) => {
const loadState = cwrap('loadState', 'boolean', ['number']);
return loadState(slot);
};
Module.forceAutoSaveState = () => {
const autoSaveState = cwrap('autoSaveState', 'boolean', []);
return autoSaveState();
};
Module.loadAutoSaveState = () => {
const loadAutoSaveState = cwrap('loadAutoSaveState', 'boolean', []);
return loadAutoSaveState();
};
Module.getAutoSaveState = () => {
const exists = FS.analyzePath(Module.autoSaveStateName).exists;
return exists
? {
autoSaveStateName: Module.autoSaveStateName,
data: FS.readFile(Module.autoSaveStateName),
}
: null;
};
Module.uploadAutoSaveState = async (autoSaveStateName, data) => {
return new Promise((resolve, reject) => {
try {
if (!(data instanceof Uint8Array)) {
console.warn('Auto save state data must be a Uint8Array');
return;
}
if (!autoSaveStateName.length) {
console.warn('Auto save state file name invalid');
return;
}
const path = `${autoSaveStateName}`;
FS.writeFile(path, data);
resolve();
} catch (err) {
reject(err);
}
});
};
Module.saveStateSlot = (slot, flags) => {
var saveStateSlot = cwrap('saveStateSlot', 'number', ['number', 'number']);
Module.saveStateSlot = (slot, flags) => {
if (flags === undefined) {
flags = 0b111111;
}
return saveStateSlot(slot, flags);
};
return Module.saveStateSlot(slot, flags);
};
Module.loadStateSlot = (slot, flags) => {
var loadStateSlot = cwrap('loadStateSlot', 'number', ['number', 'number']);
Module.loadStateSlot = (slot, flags) => {
if (flags === undefined) {
flags = 0b111101;
}
return loadStateSlot(slot, flags);
};
return Module.loadStateSlot(slot, flags);
};
Module.autoLoadCheats = () => {
const autoLoadCheats = cwrap('autoLoadCheats', 'bool', []);
return autoLoadCheats();
};
Module.setFastForwardMultiplier = (multiplier) => {
const setFastForwardMultiplier = cwrap('setFastForwardMultiplier', null, [
'number',
]);
setFastForwardMultiplier(multiplier);
};
Module.getFastForwardMultiplier = () => {
const getFastForwardMultiplier = cwrap(
'getFastForwardMultiplier',
'number',
[]
);
return getFastForwardMultiplier();
};
// core callback store, used to persist long lived js function pointers for use in core callbacks in c
const coreCallbackStore = {
alarmCallbackPtr: null,
coreCrashedCallbackPtr: null,
keysReadCallbackPtr: null,
saveDataUpdatedCallbackPtr: null,
videoFrameEndedCallbackPtr: null,
videoFrameStartedCallbackPtr: null,
autoSaveStateCapturedCallbackPtr: null,
autoSaveStateLoadedCallbackPtr: null,
};
// adds user callbacks to the callback store, and makes function(s) available to the core in c
// passing null clears the callback, allowing for partial additions/removals of callbacks
Module.addCoreCallbacks = (callbacks) => {
const addCoreCallbacks = cwrap('addCoreCallbacks', null, ['number']);
Object.keys(coreCallbackStore).forEach((callbackKey) => {
const callbackName = callbackKey.replace('CallbackPtr', 'Callback');
const callback = callbacks[callbackName];
// if the pointer is stored remove the old function pointer if a new callback was passed, or the callback is null
if (callback !== undefined && !!coreCallbackStore[callbackKey]) {
removeFunction(coreCallbackStore[callbackKey]);
coreCallbackStore[callbackKey] = null;
}
// add the new function pointer to the store if present
if (!!callback)
coreCallbackStore[callbackKey] = addFunction(callback, 'vi');
});
// add the callbacks from the store to the core
addCoreCallbacks(
coreCallbackStore.alarmCallbackPtr,
coreCallbackStore.coreCrashedCallbackPtr,
coreCallbackStore.keysReadCallbackPtr,
coreCallbackStore.saveDataUpdatedCallbackPtr,
coreCallbackStore.videoFrameEndedCallbackPtr,
coreCallbackStore.videoFrameStartedCallbackPtr,
coreCallbackStore.autoSaveStateCapturedCallbackPtr,
coreCallbackStore.autoSaveStateLoadedCallbackPtr
);
};
Module.toggleRewind = (toggle) => {
const toggleRewind = cwrap('toggleRewind', null, ['boolean']);
toggleRewind(toggle);
};
Module.setCoreSettings = (coreSettings) => {
const setIntegerCoreSetting = cwrap('setIntegerCoreSetting', null, [
'string',
'number',
]);
if (coreSettings.allowOpposingDirections !== undefined)
setIntegerCoreSetting(
'allowOpposingDirections',
coreSettings.allowOpposingDirections
);
if (coreSettings.rewindBufferCapacity !== undefined)
setIntegerCoreSetting(
'rewindBufferCapacity',
coreSettings.rewindBufferCapacity
);
if (coreSettings.rewindBufferInterval !== undefined)
setIntegerCoreSetting(
'rewindBufferInterval',
coreSettings.rewindBufferInterval
);
if (coreSettings?.frameSkip !== undefined)
setIntegerCoreSetting('frameSkip', coreSettings.frameSkip);
if (coreSettings.audioSampleRate !== undefined)
setIntegerCoreSetting('audioSampleRate', coreSettings.audioSampleRate);
if (coreSettings.audioBufferSize !== undefined)
setIntegerCoreSetting('audioBufferSize', coreSettings.audioBufferSize);
if (coreSettings.videoSync !== undefined)
setIntegerCoreSetting('videoSync', coreSettings.videoSync);
if (coreSettings.audioSync !== undefined)
setIntegerCoreSetting('audioSync', coreSettings.audioSync);
if (coreSettings.threadedVideo !== undefined)
setIntegerCoreSetting('threadedVideo', coreSettings.threadedVideo);
if (coreSettings.rewindEnable !== undefined)
setIntegerCoreSetting('rewindEnable', coreSettings.rewindEnable);
if (coreSettings.baseFpsTarget !== undefined)
setIntegerCoreSetting('baseFpsTarget', coreSettings.baseFpsTarget);
if (coreSettings.timestepSync !== undefined)
setIntegerCoreSetting('timestepSync', coreSettings.timestepSync);
if (coreSettings.showFpsCounter !== undefined)
setIntegerCoreSetting('showFpsCounter', coreSettings.showFpsCounter);
if (coreSettings.autoSaveStateTimerIntervalSeconds !== undefined)
setIntegerCoreSetting(
'autoSaveStateTimerIntervalSeconds',
coreSettings.autoSaveStateTimerIntervalSeconds
);
if (coreSettings.autoSaveStateEnable !== undefined)
setIntegerCoreSetting(
'autoSaveStateEnable',
coreSettings.autoSaveStateEnable
);
if (coreSettings.restoreAutoSaveStateOnLoad !== undefined)
setIntegerCoreSetting(
'restoreAutoSaveStateOnLoad',
coreSettings.restoreAutoSaveStateOnLoad
);
};
// end include: /home/mgba/src/src/platform/wasm/pre.js
// Sometimes an existing Module object exists with properties
// meant to overwrite the default module functionality. Here
// we collect those properties and reapply _after_ we configure
// the current environment's defaults to avoid having to be so
// defensive during initialization.
var moduleOverrides = Object.assign({}, Module);
var arguments_ = [];
var thisProgram = './this.program';
var quit_ = (status, toThrow) => {
throw toThrow;
};
// `/` 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;
// 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) {
if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled
scriptDirectory = self.location.href;
} else if (typeof document != 'undefined' && document.currentScript) { // web
scriptDirectory = document.currentScript.src;
}
// When MODULARIZE, this JS may be executed later, after document.currentScript
// is gone, so we saved it, and we use it here instead of any other info.
if (_scriptName) {
scriptDirectory = _scriptName;
}
// blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.
// otherwise, slice off the final part of the url to find the script directory.
// if scriptDirectory does not contain a slash, lastIndexOf will return -1,
// and scriptDirectory will correctly be replaced with an empty string.
// If scriptDirectory contains a query (starting with ?) or a fragment (starting with #),
// they are removed because they could contain a slash.
if (scriptDirectory.startsWith('blob:')) {
scriptDirectory = '';
} else {
scriptDirectory = scriptDirectory.slice(0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/')+1);
}
{
// include: web_or_worker_shell_read.js
if (ENVIRONMENT_IS_WORKER) {
readBinary = (url) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.responseType = 'arraybuffer';
xhr.send(null);
return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response));
};
}
readAsync = async (url) => {
var response = await fetch(url, { credentials: 'same-origin' });
if (response.ok) {
return response.arrayBuffer();
}
throw new Error(response.status + ' : ' + response.url);
};
// end include: web_or_worker_shell_read.js
}
} else
{
}
var out = Module['print'] || console.log.bind(console);
var err = Module['printErr'] || console.error.bind(console);
// Merge back in the overrides
Object.assign(Module, moduleOverrides);
// Free the object hierarchy contained in the overrides, this lets the GC
// reclaim data used.
moduleOverrides = null;
// Emit code to handle expected values on the Module object. This applies Module.x
// to the proper local x. This has two benefits: first, we only emit it if it is
// expected to arrive, and second, by using a local everywhere else that can be
// minified.
if (Module['arguments']) arguments_ = Module['arguments'];
if (Module['thisProgram']) thisProgram = Module['thisProgram'];
// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
// 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 = Module['wasmBinary'];
// Wasm globals
var wasmMemory;
// For sending to workers.
var wasmModule;
//========================================
// 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 code in shell environments
// but only when noExitRuntime is false.
var EXITSTATUS;
// In STRICT mode, we only define assert() when ASSERTIONS is set. i.e. we
// don't define it at all in release modes. This matches the behaviour of
// MINIMAL_RUNTIME.
// TODO(sbc): Make this the default even without STRICT enabled.
/** @type {function(*, string=)} */
function assert(condition, text) {
if (!condition) {
// This build was created without ASSERTIONS defined. `assert()` should not
// ever be called in this configuration but in case there are callers in
// the wild leave this simple abort() implementation here for now.
abort(text);
}
}
// Memory management
var HEAP,
/** @type {!Int8Array} */
HEAP8,
/** @type {!Uint8Array} */
HEAPU8,
/** @type {!Int16Array} */
HEAP16,
/** @type {!Uint16Array} */
HEAPU16,
/** @type {!Int32Array} */
HEAP32,
/** @type {!Uint32Array} */
HEAPU32,
/** @type {!Float32Array} */
HEAPF32,
/* BigInt64Array type is not correctly defined in closure
/** not-@type {!BigInt64Array} */
HEAP64,
/* BigUint64Array type is not correctly defined in closure
/** not-t@type {!BigUint64Array} */
HEAPU64,
/** @type {!Float64Array} */
HEAPF64;
var runtimeInitialized = false;
/**
* Indicates whether filename is delivered via file protocol (as opposed to http/https)
* @noinline
*/
var isFileURI = (filename) => filename.startsWith('file://');
// include: runtime_shared.js
// include: runtime_stack_check.js
// end include: runtime_stack_check.js
// include: runtime_exceptions.js
// end include: runtime_exceptions.js
// include: runtime_debug.js
// end include: runtime_debug.js
// include: memoryprofiler.js
// end include: memoryprofiler.js
// include: runtime_pthread.js
// Pthread Web Worker handling code.
// This code runs only on pthread web workers and handles pthread setup
// and communication with the main thread via postMessage.
if (ENVIRONMENT_IS_PTHREAD) {
var wasmModuleReceived;
// Thread-local guard variable for one-time init of the JS state
var initializedJS = false;
function threadPrintErr(...args) {
var text = args.join(' ');
console.error(text);
}
if (!Module['printErr'])
err = threadPrintErr;
function threadAlert(...args) {
var text = args.join(' ');
postMessage({cmd: 'alert', text, threadId: _pthread_self()});
}
self.alert = threadAlert;
// Turn unhandled rejected promises into errors so that the main thread will be
// notified about them.
self.onunhandledrejection = (e) => { throw e.reason || e; };
function handleMessage(e) {
try {
var msgData = e['data'];
//dbg('msgData: ' + Object.keys(msgData));
var cmd = msgData.cmd;
if (cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
// Until we initialize the runtime, queue up any further incoming messages.
let messageQueue = [];
self.onmessage = (e) => messageQueue.push(e);
// And add a callback for when the runtime is initialized.
self.startWorker = (instance) => {
// Notify the main thread that this thread has loaded.
postMessage({ cmd: 'loaded' });
// Process any messages that were queued before the thread was ready.
for (let msg of messageQueue) {
handleMessage(msg);
}
// Restore the real message handler.
self.onmessage = handleMessage;
};
// Use `const` here to ensure that the variable is scoped only to
// that iteration, allowing safe reference from a closure.
for (const handler of msgData.handlers) {
// The the main module has a handler for a certain even, but no
// handler exists on the pthread worker, then proxy that handler
// back to the main thread.
if (!Module[handler] || Module[handler].proxy) {
Module[handler] = (...args) => {
postMessage({ cmd: 'callHandler', handler, args: args });
}
// Rebind the out / err handlers if needed
if (handler == 'print') out = Module[handler];
if (handler == 'printErr') err = Module[handler];
}
}
wasmMemory = msgData.wasmMemory;
updateMemoryViews();
wasmModuleReceived(msgData.wasmModule);
} else if (cmd === 'run') {
// Call inside JS module to set up the stack frame for this pthread in JS module scope.
// This needs to be the first thing that we do, as we cannot call to any C/C++ functions
// until the thread stack is initialized.
establishStackSpace(msgData.pthread_ptr);
// Pass the thread address to wasm to store it for fast access.
__emscripten_thread_init(msgData.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1, 0, 0);
PThread.threadInitTLS();
// Await mailbox notifications with `Atomics.waitAsync` so we can start
// using the fast `Atomics.notify` notification path.
__emscripten_thread_mailbox_await(msgData.pthread_ptr);
if (!initializedJS) {
initializedJS = true;
}
try {
invokeEntryPoint(msgData.start_routine, msgData.arg);
} catch(ex) {
if (ex != 'unwind') {
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
// would make this thread joinable). Instead, re-throw the exception
// and let the top level handler propagate it back to the main thread.
throw ex;
}
}
} else if (msgData.target === 'setimmediate') {
// no-op
} else if (cmd === 'checkMailbox') {
if (initializedJS) {
checkMailbox();
}
} else if (cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a cmd field present), but is not one of the
// recognized commands:
err(`worker: received unknown command ${cmd}`);
err(msgData);
}
} catch(ex) {
__emscripten_thread_crashed();
throw ex;
}
};
self.onmessage = handleMessage;
} // ENVIRONMENT_IS_PTHREAD
// end include: runtime_pthread.js
function updateMemoryViews() {
var b = wasmMemory.buffer;
Module['HEAP8'] = HEAP8 = new Int8Array(b);
Module['HEAP16'] = HEAP16 = new Int16Array(b);
Module['HEAPU8'] = HEAPU8 = new Uint8Array(b);
Module['HEAPU16'] = HEAPU16 = new Uint16Array(b);
Module['HEAP32'] = HEAP32 = new Int32Array(b);
Module['HEAPU32'] = HEAPU32 = new Uint32Array(b);
Module['HEAPF32'] = HEAPF32 = new Float32Array(b);
Module['HEAPF64'] = HEAPF64 = new Float64Array(b);
Module['HEAP64'] = HEAP64 = new BigInt64Array(b);
Module['HEAPU64'] = HEAPU64 = new BigUint64Array(b);
}
// end include: runtime_shared.js
// In non-standalone/normal mode, we create the memory here.
// include: runtime_init_memory.js
// Create the wasm memory. (Note: this only applies if IMPORTED_MEMORY is defined)
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
if (!ENVIRONMENT_IS_PTHREAD) {
if (Module['wasmMemory']) {
wasmMemory = Module['wasmMemory'];
} else
{
var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 268435456;
/** @suppress {checkTypes} */
wasmMemory = new WebAssembly.Memory({
'initial': INITIAL_MEMORY / 65536,
'maximum': INITIAL_MEMORY / 65536,
'shared': true,
});
}
updateMemoryViews();
}
// end include: runtime_init_memory.js
function preRun() {
if (Module['preRun']) {
if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
while (Module['preRun'].length) {
addOnPreRun(Module['preRun'].shift());
}
}
callRuntimeCallbacks(onPreRuns);
}
function initRuntime() {
runtimeInitialized = true;
if (ENVIRONMENT_IS_PTHREAD) return startWorker(Module);
if (!Module['noFSInit'] && !FS.initialized) FS.init();
TTY.init();
wasmExports['__wasm_call_ctors']();
FS.ignorePermissions = false;
}
function preMain() {
}
function postRun() {
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
if (Module['postRun']) {
if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
while (Module['postRun'].length) {
addOnPostRun(Module['postRun'].shift());
}
}
callRuntimeCallbacks(onPostRuns);
}
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
// decrement it. Incrementing must happen in a place like
// Module.preRun (used by emcc to add file preloading).
// Note that you can add dependencies in preRun, even though
// it happens right before run - run will be postponed until
// the dependencies are met.
var runDependencies = 0;
var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled
function getUniqueRunDependency(id) {
return id;
}
function addRunDependency(id) {
runDependencies++;
Module['monitorRunDependencies']?.(runDependencies);
}
function removeRunDependency(id) {
runDependencies--;
Module['monitorRunDependencies']?.(runDependencies);
if (runDependencies == 0) {
if (dependenciesFulfilled) {
var callback = dependenciesFulfilled;
dependenciesFulfilled = null;
callback(); // can add another dependenciesFulfilled
}
}
}
/** @param {string|number=} what */
function abort(what) {
Module['onAbort']?.(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('mgba.wasm');
}
// Use bundler-friendly `new URL(..., import.meta.url)` pattern; works in browsers too.
return new URL('mgba.wasm', import.meta.url).href;
}
function getBinarySync(file) {
if (file == wasmBinaryFile && wasmBinary) {
return new Uint8Array(wasmBinary);
}
if (readBinary) {
return readBinary(file);
}
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 {
// Fall back to getBinarySync below;
}
}
// 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 && typeof WebAssembly.instantiateStreaming == 'function'
) {
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');
// fall back of instantiateArrayBuffer below
};
}
return instantiateArrayBuffer(binaryFile, imports);
}
function getWasmImports() {
assignWasmImports();
// prepare imports
return {
'env': wasmImports,
'wasi_snapshot_preview1': wasmImports,
}
}
// 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;
registerTLSInit(wasmExports['_emscripten_tls_init']);
wasmTable = wasmExports['__indirect_function_table'];
// We now have the Wasm module loaded up, keep a reference to the compiled module so we can post it to the workers.
wasmModule = module;
removeRunDependency('wasm-instantiate');
return wasmExports;
}
// wait for the pthread pool (if any)
addRunDependency('wasm-instantiate');
// 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
return receiveInstance(result['instance'], result['module']);
}
var info = getWasmImports();
// User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback
// to manually instantiate the Wasm module themselves. This allows pages to
// run the instantiation parallel to any other async startup actions they are
// performing.
// Also pthreads and wasm workers initialize the wasm instance through this
// path.
if (Module['instantiateWasm']) {
return new Promise((resolve, reject) => {
Module['instantiateWasm'](info, (mod, inst) => {
receiveInstance(mod, inst);
resolve(mod.exports);
});
});
}
if (ENVIRONMENT_IS_PTHREAD) {
return new Promise((resolve) => {
wasmModuleReceived = (module) => {
// Instantiate from the module posted from the main thread.
// We can just use sync instantiation in the worker.
var instance = new WebAssembly.Instance(module, getWasmImports());
resolve(receiveInstance(instance, module));
};
});
}
wasmBinaryFile ??= findWasmBinary();
try {
var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info);
var exports = receiveInstantiationResult(result);
return exports;
} catch (e) {
// If instantiation fails, reject the module ready promise.
readyPromiseReject(e);
return Promise.reject(e);
}
}
// === Body ===
var ASM_CONSTS = {
310352: ($0, $1) => { Module.canvas.width = $0; Module.canvas.height = $1; },
310409: () => { console.error("thread instantiation failed") },
310458: ($0, $1, $2, $3, $4, $5, $6) => { Module.version = { gitCommit : UTF8ToString($0), gitShort : UTF8ToString($1), gitBranch : UTF8ToString($2), gitRevision : $3, binaryName : UTF8ToString($4), projectName : UTF8ToString($5), projectVersion : UTF8ToString($6) }; },
310690: ($0, $1) => { const funcPtr = $0; const ctx = $1; const func = wasmTable.get(funcPtr); if (func) func(ctx); },
310788: ($0, $1) => { const funcPtr = $0; const ctx = $1; const func = wasmTable.get(funcPtr); if (func) func(ctx); },
310886: ($0, $1) => { const funcPtr = $0; const ctx = $1; const func = wasmTable.get(funcPtr); if (func) func(ctx); },
310984: ($0, $1) => { const funcPtr = $0; const ctx = $1; const func = wasmTable.get(funcPtr); if (func) func(ctx); },
311082: ($0, $1) => { const funcPtr = $0; const ctx = $1; const func = wasmTable.get(funcPtr); if (func) func(ctx); },
311180: ($0, $1) => { const funcPtr = $0; const ctx = $1; const func = wasmTable.get(funcPtr); if (func) func(ctx); },
311278: () => { FS.syncfs(function (err) { assert(!err); }) },
311322: ($0) => { var str = UTF8ToString($0) + '\n\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; var reply = window.prompt(str, "i"); if (reply === null) { reply = "i"; } return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL); },
311547: () => { if (typeof(AudioContext) !== 'undefined') { return true; } else if (typeof(webkitAudioContext) !== 'undefined') { return true; } return false; },
311694: () => { if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) { return true; } else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') { return true; } return false; },
311928: ($0) => { if(typeof(Module['SDL2']) === 'undefined') { Module['SDL2'] = {}; } var SDL2 = Module['SDL2']; if (!$0) { SDL2.audio = {}; } else { SDL2.capture = {}; } if (!SDL2.audioContext) { if (typeof(AudioContext) !== 'undefined') { SDL2.audioContext = new AudioContext(); } else if (typeof(webkitAudioContext) !== 'undefined') { SDL2.audioContext = new webkitAudioContext(); } if (SDL2.audioContext) { if ((typeof navigator.userActivation) === 'undefined') { autoResumeAudioContext(SDL2.audioContext); } } } return SDL2.audioContext === undefined ? -1 : 0; },
312480: () => { var SDL2 = Module['SDL2']; return SDL2.audioContext.sampleRate; },
312548: ($0, $1, $2, $3) => { var SDL2 = Module['SDL2']; var have_microphone = function(stream) { if (SDL2.capture.silenceTimer !== undefined) { clearInterval(SDL2.capture.silenceTimer); SDL2.capture.silenceTimer = undefined; SDL2.capture.silenceBuffer = undefined } SDL2.capture.mediaStreamNode = SDL2.audioContext.createMediaStreamSource(stream); SDL2.capture.scriptProcessorNode = SDL2.audioContext.createScriptProcessor($1, $0, 1); SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) { if ((SDL2 === undefined) || (SDL2.capture === undefined)) { return; } audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0); SDL2.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer; dynCall('vi', $2, [$3]); }; SDL2.capture.mediaStreamNode.connect(SDL2.capture.scriptProcessorNode); SDL2.capture.scriptProcessorNode.connect(SDL2.audioContext.destination); SDL2.capture.stream = stream; }; var no_microphone = function(error) { }; SDL2.capture.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate); SDL2.capture.silenceBuffer.getChannelData(0).fill(0.0); var silence_callback = function() { SDL2.capture.currentCaptureBuffer = SDL2.capture.silenceBuffer; dynCall('vi', $2, [$3]); }; SDL2.capture.silenceTimer = setInterval(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000); if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) { navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone); } else if (navigator.webkitGetUserMedia !== undefined) { navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone); } },
314241: ($0, $1, $2, $3) => { var SDL2 = Module['SDL2']; SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0); SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) { if ((SDL2 === undefined) || (SDL2.audio === undefined)) { return; } if (SDL2.audio.silenceTimer !== undefined) { clearInterval(SDL2.audio.silenceTimer); SDL2.audio.silenceTimer = undefined; SDL2.audio.silenceBuffer = undefined; } SDL2.audio.currentOutputBuffer = e['outputBuffer']; dynCall('vi', $2, [$3]); }; SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']); if (SDL2.audioContext.state === 'suspended') { SDL2.audio.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate); SDL2.audio.silenceBuffer.getChannelData(0).fill(0.0); var silence_callback = function() { if ((typeof navigator.userActivation) !== 'undefined') { if (navigator.userActivation.hasBeenActive) { SDL2.audioContext.resume(); } } SDL2.audio.currentOutputBuffer = SDL2.audio.silenceBuffer; dynCall('vi', $2, [$3]); SDL2.audio.currentOutputBuffer = undefined; }; SDL2.audio.silenceTimer = setInterval(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000); } },
315416: ($0, $1) => { var SDL2 = Module['SDL2']; var numChannels = SDL2.capture.currentCaptureBuffer.numberOfChannels; for (var c = 0; c < numChannels; ++c) { var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c); if (channelData.length != $1) { throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; } if (numChannels == 1) { for (var j = 0; j < $1; ++j) { setValue($0 + (j * 4), channelData[j], 'float'); } } else { for (var j = 0; j < $1; ++j) { setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float'); } } } },
316021: ($0, $1) => { var SDL2 = Module['SDL2']; var buf = $0 >>> 2; var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels']; for (var c = 0; c < numChannels; ++c) { var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c); if (channelData.length != $1) { throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; } for (var j = 0; j < $1; ++j) { channelData[j] = HEAPF32[buf + (j*numChannels + c)]; } } },
316510: ($0) => { var SDL2 = Module['SDL2']; if ($0) { if (SDL2.capture.silenceTimer !== undefined) { clearInterval(SDL2.capture.silenceTimer); } if (SDL2.capture.stream !== undefined) { var tracks = SDL2.capture.stream.getAudioTracks(); for (var i = 0; i < tracks.length; i++) { SDL2.capture.stream.removeTrack(tracks[i]); } } if (SDL2.capture.scriptProcessorNode !== undefined) { SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {}; SDL2.capture.scriptProcessorNode.disconnect(); } if (SDL2.capture.mediaStreamNode !== undefined) { SDL2.capture.mediaStreamNode.disconnect(); } SDL2.capture = undefined; } else { if (SDL2.audio.scriptProcessorNode != undefined) { SDL2.audio.scriptProcessorNode.disconnect(); } if (SDL2.audio.silenceTimer !== undefined) { clearInterval(SDL2.audio.silenceTimer); } SDL2.audio = undefined; } if ((SDL2.audioContext !== undefined) && (SDL2.audio === undefined) && (SDL2.capture === undefined)) { SDL2.audioContext.close(); SDL2.audioContext = undefined; } },
317516: ($0, $1, $2) => { var w = $0; var h = $1; var pixels = $2; if (!Module['SDL2']) Module['SDL2'] = {}; var SDL2 = Module['SDL2']; if (SDL2.ctxCanvas !== Module['canvas']) { SDL2.ctx = Module['createContext'](Module['canvas'], false, true); SDL2.ctxCanvas = Module['canvas']; } if (SDL2.w !== w || SDL2.h !== h || SDL2.imageCtx !== SDL2.ctx) { SDL2.image = SDL2.ctx.createImageData(w, h); SDL2.w = w; SDL2.h = h; SDL2.imageCtx = SDL2.ctx; } var data = SDL2.image.data; var src = pixels / 4; var dst = 0; var num; if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) { num = data.length; while (dst < num) { var val = HEAP32[src]; data[dst ] = val & 0xff; data[dst+1] = (val >> 8) & 0xff; data[dst+2] = (val >> 16) & 0xff; data[dst+3] = 0xff; src++; dst += 4; } } else { if (SDL2.data32Data !== data) { SDL2.data32 = new Int32Array(data.buffer); SDL2.data8 = new Uint8Array(data.buffer); SDL2.data32Data = data; } var data32 = SDL2.data32; num = data32.length; data32.set(HEAP32.subarray(src, src + num)); var data8 = SDL2.data8; var i = 3; var j = i + 4*num; if (num % 8 == 0) { while (i < j) { data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; } } else { while (i < j) { data8[i] = 0xff; i = i + 4 | 0; } } } SDL2.ctx.putImageData(SDL2.image, 0, 0); },
318984: ($0, $1, $2, $3, $4) => { var w = $0; var h = $1; var hot_x = $2; var hot_y = $3; var pixels = $4; var canvas = document.createElement("canvas"); canvas.width = w; canvas.height = h; var ctx = canvas.getContext("2d"); var image = ctx.createImageData(w, h); var data = image.data; var src = pixels / 4; var dst = 0; var num; if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) { num = data.length; while (dst < num) { var val = HEAP32[src]; data[dst ] = val & 0xff; data[dst+1] = (val >> 8) & 0xff; data[dst+2] = (val >> 16) & 0xff; data[dst+3] = (val >> 24) & 0xff; src++; dst += 4; } } else { var data32 = new Int32Array(data.buffer); num = data32.length; data32.set(HEAP32.subarray(src, src + num)); } ctx.putImageData(image, 0, 0); var url = hot_x === 0 && hot_y === 0 ? "url(" + canvas.toDataURL() + "), auto" : "url(" + canvas.toDataURL() + ") " + hot_x + " " + hot_y + ", auto"; var urlBuf = _malloc(url.length + 1); stringToUTF8(url, urlBuf, url.length + 1); return urlBuf; },
319972: ($0) => { if (Module['canvas']) { Module['canvas'].style['cursor'] = UTF8ToString($0); } },
320055: () => { if (Module['canvas']) { Module['canvas'].style['cursor'] = 'none'; } },
320124: () => { return window.innerWidth; },
320154: () => { return window.innerHeight; }
};
// end include: preamble.js
class ExitStatus {
name = 'ExitStatus';
constructor(status) {
this.message = `Program terminated with exit(${status})`;
this.status = status;
}
}
var terminateWorker = (worker) => {
worker.terminate();
// terminate() can be asynchronous, so in theory the worker can continue
// to run for some amount of time after termination. However from our POV
// the worker now dead and we don't want to hear from it again, so we stub
// out its message handler here. This avoids having to check in each of
// the onmessage handlers if the message was coming from valid worker.
worker.onmessage = (e) => {
};
};
var cleanupThread = (pthread_ptr) => {
var worker = PThread.pthreads[pthread_ptr];
PThread.returnWorkerToPool(worker);
};
var callRuntimeCallbacks = (callbacks) =