diff --git a/package-lock.json b/package-lock.json index a6e0c24..3f7e9f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "diabloweb", - "version": "1.0.36", + "version": "1.0.37", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 48310c1..e9ae3f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diabloweb", - "version": "1.0.36", + "version": "1.0.37", "private": true, "dependencies": { "@babel/core": "7.4.3", diff --git a/src/App.js b/src/App.js index f01e1eb..7b8b728 100644 --- a/src/App.js +++ b/src/App.js @@ -11,6 +11,7 @@ import { mapStackTrace } from 'sourcemapped-stacktrace'; import create_fs from './fs'; import load_game from './api/loader'; import { SpawnSizes } from './api/load_spawn'; +import CompressMpq from './mpqcmp'; import Peer from 'peerjs'; @@ -130,7 +131,9 @@ class App extends React.Component { if (spawn && SpawnSizes.includes(spawn.byteLength)) { this.setState({has_spawn: true}); } - this.updateSaves(); + if ([...fs.files.keys()].filter(name => name.match(/\.sv$/i)).length) { + this.setState({save_names: true}); + } }); } @@ -138,7 +141,11 @@ class App extends React.Component { const file = getDropFile(e); if (file) { e.preventDefault(); - this.start(file); + if (this.compressMpq) { + this.compressMpq.start(file); + } else { + this.start(file); + } } this.setState({dropping: 0}); } @@ -223,12 +230,18 @@ class App extends React.Component { this.saveName = name; } + showSaves = () => { + if (this.state.save_names === true) { + this.updateSaves().then(() => this.setState({show_saves: !this.state.show_saves})); + } else { + this.setState({show_saves: !this.state.show_saves}); + } + } updateSaves() { - this.fs.then(fs => { - const saves = []; + return this.fs.then(fs => { + const saves = {}; [...fs.files.keys()].filter(name => name.match(/\.sv$/i)).forEach(name => { - saves.push(name); - console.log(name, getPlayerName(fs.files.get(name).buffer)); + saves[name] = getPlayerName(fs.files.get(name).buffer, name); }); this.setState({save_names: saves}); }); @@ -302,7 +315,7 @@ class App extends React.Component { document.removeEventListener("dragleave", this.onDragLeave, true); this.setState({dropping: 0}); - const retail = !!(file && file.name.match(/^diabdat\.mpq$/i)); + const retail = !!(file && !file.name.match(/^spawn\.mpq$/i)); if (process.env.NODE_ENV === 'production') { ReactGA.event({ category: 'Game', @@ -656,30 +669,78 @@ class App extends React.Component { } } - render() { - const {started, loading, error, progress, dropping, has_spawn, save_names, show_saves} = this.state; - if (show_saves && save_names) { + renderUi() { + const {started, loading, error, progress, has_spawn, save_names, show_saves, compress} = this.state; + if (show_saves && typeof save_names === "object") { + const plrClass = ["Warrior", "Rogue", "Sorcerer"]; return ( -
-
-
-
    - {save_names.map(name =>
  • - {name} - this.downloadSave(name)}/> - this.removeSave(name)}/> -
  • )} -
-
- - -
- this.setState({show_saves: false})}>Back -
-
+
+ +
+ + +
+
this.setState({show_saves: false})}>Back
+
+ ); + } else if (compress) { + return ( + this.compressMpq = e}/> + ); + } else if (error) { + return ( + +

The following error has occurred:

+

{error.message}

+

Click to create an issue on GitHub

+ {error.save != null && Download save file} + + ); + } else if (loading && !started) { + return ( +
+ {(progress && progress.text) || 'Loading...'} + {progress != null && !!progress.total && ( + + )} +
+ ); + } else if (!started) { + return ( +
+

+ This is a web port of the original Diablo game, based on source code reconstructed by + GalaXyHaXz and devilution team. The project page with information and links can be found over here https://github.com/d07RiV/diabloweb +

+

+ If you own the original game, you can drop the original DIABDAT.MPQ onto this page or click the button below to start playing. + The game can be purchased from GoG. + {" "} this.setState({compress: true})}>Click here to compress the MPQ, greatly reducing its size. +

+ {!has_spawn && ( +

+ Or you can play the shareware version for free (50MB download). +

+ )} +
+ + +
+
this.start()}>Play Shareware
+ {!!save_names &&
Manage Saves
}
); } + } + + render() { + const {started, error, dropping} = this.state; return (
@@ -699,45 +760,7 @@ class App extends React.Component {
- {!!error && ( - -

The following error has occurred:

-

{error.message}

-

Click to create an issue on GitHub

- {error.save != null && Download save file} - - )} - {!!loading && !started && !error && ( -
- {(progress && progress.text) || 'Loading...'} - {progress != null && !!progress.total && ( - - )} -
- )} - {!started && !loading && !error && ( -
-

- This is a web port of the original Diablo game, based on source code reconstructed by - GalaXyHaXz and devilution team. The project page with information and links can be found over here https://github.com/d07RiV/diabloweb -

-

- If you own the original game, you can drop the original DIABDAT.MPQ onto this page or click the button below to start playing. - The game can be purchased from GoG. -

- {!has_spawn && ( -

- Or you can play the shareware version for free (50MB download). -

- )} -
- - -
- this.start()}>Play Shareware - {!!(save_names && save_names.length) && this.setState({show_saves: true})}>Manage Saves} -
- )} + {this.renderUi()}
); diff --git a/src/App.scss b/src/App.scss index af3e5ff..08ff118 100644 --- a/src/App.scss +++ b/src/App.scss @@ -110,7 +110,9 @@ body, #root, .App { margin: 10px 0; } .startButton { - display: inline-block; + display: block; + margin-left: auto; + margin-right: auto; border: 1px solid #fff; background: #000; font-size: 2em; @@ -127,14 +129,24 @@ body, #root, .App { text-align: left; li { padding: 0 6px; + .info { + color: #888; + margin-left: 6px; + } .btnRemove { - color: #f88; + color: #800; + &:hover { + color: #f00; + } float: right; cursor: pointer; margin: 0 4px; } .btnDownload { - color: #fff; + color: #888; + &:hover { + color: #fff; + } float: right; cursor: pointer; margin: 0 4px; diff --git a/src/api/codec.js b/src/api/codec.js index 2bf2aa6..9736192 100644 --- a/src/api/codec.js +++ b/src/api/codec.js @@ -1,25 +1,28 @@ const W = new Uint32Array(80); -const SHA1CircularShift = (shift, value) => ((value << shift) | (value >>> (32 - shift))); +const SHA1CircularShift = (shift, value) => ((value << shift) | (value >> (32 - shift))); class SHA1 { - state = new Uint32Array(5); + digest = new Uint32Array(5); count = 0; - input(u8) { + input8(u8) { const u32 = new Uint32Array(u8.buffer, u8.byteOffset, 16); - context.count += data.length * 32; + this.input(u32); + } + input(u32) { + this.count += u32.length * 32; for (let i = 0; i < 16; ++i) { W[i] = u32[i]; } for (let i = 16; i < 80; ++i) { W[i] = W[i - 16] ^ W[i - 14] ^ W[i - 8] ^ W[i - 3]; } - let A = this.state[0]; - let B = this.state[1]; - let C = this.state[2]; - let D = this.state[3]; - let E = this.state[4]; + let A = this.digest[0]; + let B = this.digest[1]; + let C = this.digest[2]; + let D = this.digest[3]; + let E = this.digest[4]; for (let i = 0; i < 20; i++) { const temp = SHA1CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[i] + 0x5A827999; @@ -57,21 +60,21 @@ class SHA1 { A = temp | 0; } - this.state[0] += A; - this.state[1] += B; - this.state[2] += C; - this.state[3] += D; - this.state[4] += E; + this.digest[0] += A; + this.digest[1] += B; + this.digest[2] += C; + this.digest[3] += D; + this.digest[4] += E; } constructor() { - this.state[0] = 0x67452301; - this.state[1] = 0xEFCDAB89; - this.state[2] = 0x98BADCFE; - this.state[3] = 0x10325476; - this.state[4] = 0xC3D2E1F0; + this.digest[0] = 0x67452301; + this.digest[1] = 0xEFCDAB89; + this.digest[2] = 0x98BADCFE; + this.digest[3] = 0x10325476; + this.digest[4] = 0xC3D2E1F0; - this.result = new Uint8Array(this.state.buffer); + this.digest8 = new Uint8Array(this.digest.buffer); } } @@ -85,16 +88,10 @@ class Random { } } -struct CodecSignature { - DWORD checksum; - BYTE error; - BYTE last_chunk_size; - WORD unused; -}; - function codec_init_key(password) { const rand = new Random(0x7058); const key = new Uint8Array(136); + const k32 = new Uint32Array(key.buffer); for (let i = 0; i < 136; ++i) { key[i] = rand.next(); } @@ -103,62 +100,50 @@ function codec_init_key(password) { pw[i] = password.charCodeAt(i % password.length); } - const sha = new SHA1(); - sha.input(pw); + let sha = new SHA1(); + sha.input8(pw); - for (let i = 0; i < 136; ++i) { - key[i] ^= sha.result[i % sha.result.length]; + for (let i = 0; i < 34; ++i) { + k32[i] ^= sha.digest[i % sha.digest.length]; } sha = new SHA1(); - sha.input(key.subarray(72)); + sha.input(k32.subarray(18)); return sha; } -function codec_decode(data, password) { - const sha = codec_init_key(password); +export default function codec_decode(data, password) { if (data.length <= 8) { return; } const size = data.length - 8; - if () - char buf[128]; - char dst[SHA1HashSize]; - int i; - CodecSignature *sig; + if (size % 64) { + return; + } - codec_init_key(0, pszPassword); - if (size <= 8) - return 0; - size = size - 8; - if (size % 64 != 0) - return 0; - for (i = size; i != 0; pbSrcDst += 64, i -= 64) { - memcpy(buf, pbSrcDst, 64); - SHA1Result(0, dst); - for (int j = 0; j < 64; j++) { - buf[j] ^= dst[j % SHA1HashSize]; - } - SHA1Calculate(0, buf, NULL); - memset(dst, 0, sizeof(dst)); - memcpy(pbSrcDst, buf, 64); - } + if (data[size + 4]) { + return; + } - memset(buf, 0, sizeof(buf)); - sig = (CodecSignature *)pbSrcDst; - if (sig->error > 0) { - size = 0; - SHA1Clear(); - } else { - SHA1Result(0, dst); - if (sig->checksum != *(DWORD *)dst) { - memset(dst, 0, sizeof(dst)); - size = 0; - SHA1Clear(); - } else { - size += sig->last_chunk_size - 64; - SHA1Clear(); - } - } - return size; + const last_size = data[size + 5]; + const result_size = size + last_size - 64; + const result = new Uint8Array(result_size); + + const sha = codec_init_key(password); + const size32 = size >> 2; + const data32 = new Uint32Array(data.buffer, data.byteOffset, size32 + 1); + const buf32 = new Uint32Array(16); + const buf = new Uint8Array(buf32.buffer); + + for (let i = 0; i < size32; i += 16) { + for (let j = 0; j < 16; ++j) { + buf32[j] = data32[i + j] ^ sha.digest[j % sha.digest.length]; + } + sha.input(buf32); + result.set(i === size32 - 16 ? buf.subarray(0, last_size) : buf, i * 4); + } + if (data32[size32] !== sha.digest[0]) { + return; + } + return result; } diff --git a/src/api/savefile.js b/src/api/savefile.js index 728b966..508d528 100644 --- a/src/api/savefile.js +++ b/src/api/savefile.js @@ -1,4 +1,5 @@ import { explode } from './explode'; +import codec_decode from './codec'; function pkzip_decompress(data, out_size) { if (data.length === out_size) { @@ -43,7 +44,7 @@ const hashtable = (function() { } return hashtable; })(); -function decrypt(u32, key) { +export function decrypt(u32, key) { let seed = 0xEEEEEEEE; for (let i = 0; i < u32.length; ++i) { seed += hashtable[0x400 + (key & 0xFF)]; @@ -52,10 +53,23 @@ function decrypt(u32, key) { key = ((~key << 0x15) + 0x11111111) | (key >>> 0x0B); } } -function decrypt8(u8, key) { +export function decrypt8(u8, key) { decrypt(new Uint32Array(u8.buffer, u8.byteOffset, u8.length >> 2), key); } -function hash(name, type) { +export function encrypt(u32, key) { + let seed = 0xEEEEEEEE; + for (let i = 0; i < u32.length; ++i) { + seed += hashtable[0x400 + (key & 0xFF)]; + const orig = u32[i]; + u32[i] ^= seed + key; + seed = (orig + seed * 33 + 3) | 0; + key = ((~key << 0x15) + 0x11111111) | (key >>> 0x0B); + } +} +export function encrypt8(u8, key) { + encrypt(new Uint32Array(u8.buffer, u8.byteOffset, u8.length >> 2), key); +} +export function hash(name, type) { let seed1 = 0x7FED7FED; let seed2 = 0xEEEEEEEE; for (let i = 0; i < name.length; ++i) { @@ -72,7 +86,7 @@ function hash(name, type) { return seed1 >>> 0; } -function path_name(name) { +export function path_name(name) { const pos = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')); return name.substring(pos + 1); } @@ -90,11 +104,11 @@ const Flags = { Exists: 0x80000000, }; -class MpqReader { +export class MpqReader { constructor(buffer) { this.buffer = buffer; this.u8 = new Uint8Array(buffer); - this.u32 = new Uint32Array(buffer); + this.u32 = new Uint32Array(buffer, 0, buffer.byteLength >> 2); this.readHeader(); } @@ -132,79 +146,81 @@ class MpqReader { } } - read(name) { + readRaw(name) { const index = this.fileIndex(name); if (index == null) { return; } const block = this.hashTable[index * 4 + 3]; - const filePos = this.blockTable[block * 4]; - let cmpSize = this.blockTable[block * 4 + 1]; - const fileSize = this.blockTable[block * 4 + 2]; - const flags = this.blockTable[block * 4 + 3]; - - if (flags & Flags.PatchFile) { + const info = { + filePos: this.blockTable[block * 4], + cmpSize: this.blockTable[block * 4 + 1], + fileSize: this.blockTable[block * 4 + 2], + flags: this.blockTable[block * 4 + 3], + key: hash(path_name(name), 3), + }; + if ((info.flags & Flags.PatchFile) || info.filePos + info.cmpSize > this.buffer.byteLength) { return; } - if (!(flags & Flags.Compressed)) { - cmpSize = fileSize; + if (!(info.flags & Flags.Compressed)) { + info.cmpSize = info.fileSize; } - - let key = hash(path_name(name), 3); - if (flags & Flags.FixSeed) { - key = (key + filePos) ^ fileSize; + if (info.flags & Flags.FixSeed) { + info.key = (info.key + info.filePos) ^ info.fileSize; } + return {info, data: new Uint8Array(this.buffer, info.filePos, info.cmpSize)}; + } - if (flags & Flags.SingleUnit) { - const raw = new Uint8Array(this.buffer, filePos, cmpSize); - if (raw.length !== cmpSize) { + read(name) { + const raw = this.readRaw(name); + if (!raw) { + return; + } + let {info, data} = raw; + data = data.slice(); + + if (info.flags & Flags.SingleUnit) { + if (info.flags & Flags.Encrypted) { + decrypt8(data, info.key); + } + if (info.flags & Flags.CompressMulti) { return; + } else if (info.flags & Flags.CompressPkWare) { + return pkzip_decompress(data, info.fileSize); } - if (flags & Flags.Encrypted) { - decrypt8(raw, key); - } - if (flags & Flags.CompressMulti) { - return; - } else if (flags & Flags.CompressPkWare) { - return pkzip_decompress(raw, fileSize); - } - return raw; - } else if (!(flags & Flags.Compressed)) { - const raw = Uint8Array(this.buffer, filePos, fileSize); - if (raw.length !== fileSize) { - return; - } - if (flags & Flags.Encrypted) { - for (let i = 0; i < fileSize; i += this.blockSize) { - decrypt8(raw.subarray(i, Math.min(fileSize, i + this.blockSize)), key + i / this.blockSize); + return data; + } else if (!(info.flags & Flags.Compressed)) { + if (info.flags & Flags.Encrypted) { + for (let i = 0; i < info.fileSize; i += this.blockSize) { + decrypt8(data.subarray(i, Math.min(info.fileSize, i + this.blockSize)), info.key + i / this.blockSize); } } - return raw; + return data; } else { - const numBlocks = Math.floor((fileSize + this.blockSize - 1) / this.blockSize); - const tableSize = numBlocks + 1 + ((flags & Flags.SectorCrc) ? 1 : 0); - const blocks = new Uint32Array(this.buffer, filePos, tableSize); - if (blocks.length !== tableSize) { + const numBlocks = Math.floor((info.fileSize + this.blockSize - 1) / this.blockSize); + const tableSize = numBlocks + 1; + if (data.length < tableSize * 4) { return; } - if (flags & Flags.Encrypted) { - decrypt(blocks, key - 1); + const blocks = new Uint32Array(data.buffer, 0, tableSize); + if (info.flags & Flags.Encrypted) { + decrypt(blocks, info.key - 1); } - const output = new Uint8Array(fileSize); + const output = new Uint8Array(info.fileSize); for (let i = 0; i < numBlocks; ++i) { const oPos = i * this.blockSize; - const cSize = blocks[i + 1] - blocks[i]; - const uSize = Math.min(this.blockSize, fileSize - oPos); - let tmp = new Uint8Array(this.buffer, filePos + blocks[i], cSize); - if (tmp.length !== cSize) { + const uSize = Math.min(this.blockSize, info.fileSize - oPos); + if (blocks[i + 1] > data.length) { return; } - if (flags & Flags.Encrypted) { - decrypt8(tmp, key + i); + let tmp = data.subarray(blocks[i], blocks[i + 1]); + if (info.flags & Flags.Encrypted) { + // this is not safe, but our files are small enough + decrypt8(tmp, info.key + i); } - if (flags & Flags.CompressMulti) { + if (info.flags & Flags.CompressMulti) { return; - } else if (flags & Flags.CompressPkWare) { + } else if (info.flags & Flags.CompressPkWare) { tmp = pkzip_decompress(tmp, uSize); } if (!tmp || tmp.length !== uSize) { @@ -217,12 +233,28 @@ class MpqReader { } } -export default function getPlayerName(data) { - debugger; +function getPassword(name) { + if (name.match(/spawn\d+\.sv/i)) { + return 'lshbkfg1'; // single, spawn + } else if (name.match(/share_\d+\.sv/i)) { + return 'lshbkfg1'; // multi, spawn + } else if (name.match(/multi_\d+\.sv/i)) { + return 'szqnlsk1'; // multi, retail + } else { + return 'xrgyrkj1'; // single, retail + } +} + +export default function getPlayerName(data, name) { try { const reader = new MpqReader(data); - const hero = reader.read("hero"); - return ''; + const hero = codec_decode(reader.read("hero"), getPassword(name)); + const nameEnd = hero.indexOf(0, 16); + const result = {}; + result.name = String.fromCharCode(...hero.subarray(16, nameEnd)); + result.cls = hero[48]; + result.level = hero[53]; + return result; } catch (e) { return null; } diff --git a/src/mpqcmp/MpqCmp.jscc b/src/mpqcmp/MpqCmp.jscc index cc1906b..b48a5e7 100644 --- a/src/mpqcmp/MpqCmp.jscc +++ b/src/mpqcmp/MpqCmp.jscc @@ -5,11 +5,6 @@ var MpqCmp = (function() { function(MpqCmp) { MpqCmp = MpqCmp || {}; -// Copyright 2010 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. - // 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 @@ -50,35 +45,25 @@ Module['quit'] = function(status, toThrow) { Module['preRun'] = []; Module['postRun'] = []; -// Determine the runtime environment we are in. You can customize this by -// setting the ENVIRONMENT setting at compile time (see settings.js). +// The environment setup code below is customized to use Module. +// *** Environment setup code *** var ENVIRONMENT_IS_WEB = false; var ENVIRONMENT_IS_WORKER = false; var ENVIRONMENT_IS_NODE = false; -var ENVIRONMENT_HAS_NODE = false; var ENVIRONMENT_IS_SHELL = false; ENVIRONMENT_IS_WEB = typeof window === 'object'; ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; -// A web environment like Electron.js can have Node enabled, so we must -// distinguish between Node-enabled environments and Node environments per se. -// This will allow the former to do things like mount NODEFS. -// Extended check using process.versions fixes issue #8816. -// (Also makes redundant the original check that 'require' is a function.) -ENVIRONMENT_HAS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; -ENVIRONMENT_IS_NODE = ENVIRONMENT_HAS_NODE && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER; +ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof require === 'function' && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER; 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 -s PROXY_TO_WORKER=1) (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) - - // `/` should be present at the end if `scriptDirectory` is not empty var scriptDirectory = ''; function locateFile(path) { @@ -89,12 +74,6 @@ function locateFile(path) { } } -// Hooks that are implemented differently in different runtime environments. -var read_, - readAsync, - readBinary, - setWindowTitle; - if (ENVIRONMENT_IS_NODE) { scriptDirectory = __dirname + '/'; @@ -103,7 +82,7 @@ if (ENVIRONMENT_IS_NODE) { var nodeFS; var nodePath; - read_ = function shell_read(filename, binary) { + Module['read'] = function shell_read(filename, binary) { var ret; if (!nodeFS) nodeFS = require('fs'); if (!nodePath) nodePath = require('path'); @@ -112,8 +91,8 @@ if (ENVIRONMENT_IS_NODE) { return binary ? ret : ret.toString(); }; - readBinary = function readBinary(filename) { - var ret = read_(filename, true); + Module['readBinary'] = function readBinary(filename) { + var ret = Module['read'](filename, true); if (!ret.buffer) { ret = new Uint8Array(ret); } @@ -137,7 +116,9 @@ if (ENVIRONMENT_IS_NODE) { }); // Currently node will swallow unhandled rejections, but this behavior is // deprecated, and in the future it will exit with error status. - process['on']('unhandledRejection', abort); + process['on']('unhandledRejection', function(reason, p) { + process['exit'](1); + }); Module['quit'] = function(status) { process['exit'](status); @@ -149,12 +130,12 @@ if (ENVIRONMENT_IS_SHELL) { if (typeof read != 'undefined') { - read_ = function shell_read(f) { + Module['read'] = function shell_read(f) { return read(f); }; } - readBinary = function readBinary(f) { + Module['readBinary'] = function readBinary(f) { var data; if (typeof readbuffer === 'function') { return new Uint8Array(readbuffer(f)); @@ -177,10 +158,12 @@ if (ENVIRONMENT_IS_SHELL) { } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { - if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled + if (ENVIRONMENT_IS_WEB) { + if (document.currentScript) { + scriptDirectory = document.currentScript.src; + } + } else { // worker scriptDirectory = self.location.href; - } else if (document.currentScript) { // web - scriptDirectory = document.currentScript.src; } // When MODULARIZE (and not _INSTANCE), 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. @@ -189,16 +172,14 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { } // 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.indexOf('blob:') !== 0) { - scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/')+1); + scriptDirectory = scriptDirectory.split('/').slice(0, -1).join('/') + '/'; } else { scriptDirectory = ''; } - read_ = function shell_read(url) { + Module['read'] = function shell_read(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); @@ -206,7 +187,7 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { }; if (ENVIRONMENT_IS_WORKER) { - readBinary = function readBinary(url) { + Module['readBinary'] = function readBinary(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.responseType = 'arraybuffer'; @@ -215,7 +196,7 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { }; } - readAsync = function readAsync(url, onload, onerror) { + Module['readAsync'] = function readAsync(url, onload, onerror) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; @@ -230,7 +211,7 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { xhr.send(null); }; - setWindowTitle = function(title) { document.title = title }; + Module['setWindowTitle'] = function(title) { document.title = title }; } else { } @@ -244,6 +225,8 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { var out = Module['print'] || (typeof console !== 'undefined' ? console.log.bind(console) : (typeof print !== 'undefined' ? print : null)); var err = Module['printErr'] || (typeof printErr !== 'undefined' ? printErr : ((typeof console !== 'undefined' && console.warn.bind(console)) || out)); +// *** Environment setup code *** + // Merge back in the overrides for (key in moduleOverrides) { if (moduleOverrides.hasOwnProperty(key)) { @@ -254,35 +237,37 @@ for (key in moduleOverrides) { // reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. moduleOverrides = undefined; -// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message -// TODO remove when SDL2 is fixed; also add the above assertion - - - -// Copyright 2017 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. // {{PREAMBLE_ADDITIONS}} var STACK_ALIGN = 16; +function staticAlloc(size) { + var ret = STATICTOP; + STATICTOP = (STATICTOP + size + 15) & -16; + return ret; +} + function dynamicAlloc(size) { var ret = HEAP32[DYNAMICTOP_PTR>>2]; var end = (ret + size + 15) & -16; - if (end > _emscripten_get_heap_size()) { - abort(); - } HEAP32[DYNAMICTOP_PTR>>2] = end; + if (end >= TOTAL_MEMORY) { + var success = enlargeMemory(); + if (!success) { + HEAP32[DYNAMICTOP_PTR>>2] = ret; + return 0; + } + } return ret; } function alignMemory(size, factor) { if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default - return Math.ceil(size / factor) * factor; + var ret = size = Math.ceil(size / factor) * factor; + return ret; } function getNativeTypeSize(type) { @@ -298,7 +283,7 @@ function getNativeTypeSize(type) { return 4; // A pointer } else if (type[0] === 'i') { var bits = parseInt(type.substr(1)); - assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type); + assert(bits % 8 === 0); return bits / 8; } else { return 0; @@ -329,112 +314,8 @@ var asm2wasmImports = { // special asm2wasm imports var jsCallStartIndex = 1; var functionPointers = new Array(0); -// Wraps a JS function as a wasm function with a given signature. -// In the future, we may get a WebAssembly.Function constructor. Until then, -// we create a wasm module that takes the JS function as an import with a given -// signature, and re-exports that as a wasm function. -function convertJsFunctionToWasm(func, sig) { - - // The module is static, with the exception of the type section, which is - // generated based on the signature passed in. - var typeSection = [ - 0x01, // id: section, - 0x00, // length: 0 (placeholder) - 0x01, // count: 1 - 0x60, // form: func - ]; - var sigRet = sig.slice(0, 1); - var sigParam = sig.slice(1); - var typeCodes = { - 'i': 0x7f, // i32 - 'j': 0x7e, // i64 - 'f': 0x7d, // f32 - 'd': 0x7c, // f64 - }; - - // Parameters, length + signatures - typeSection.push(sigParam.length); - for (var i = 0; i < sigParam.length; ++i) { - typeSection.push(typeCodes[sigParam[i]]); - } - - // Return values, length + signatures - // With no multi-return in MVP, either 0 (void) or 1 (anything else) - if (sigRet == 'v') { - typeSection.push(0x00); - } else { - typeSection = typeSection.concat([0x01, typeCodes[sigRet]]); - } - - // Write the overall length of the type section back into the section header - // (excepting the 2 bytes for the section id and length) - typeSection[1] = typeSection.length - 2; - - // Rest of the module is static - var bytes = new Uint8Array([ - 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") - 0x01, 0x00, 0x00, 0x00, // version: 1 - ].concat(typeSection, [ - 0x02, 0x07, // import section - // (import "e" "f" (func 0 (type 0))) - 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, - 0x07, 0x05, // export section - // (export "f" (func 0 (type 0))) - 0x01, 0x01, 0x66, 0x00, 0x00, - ])); - - // We can compile this wasm module synchronously because it is very small. - // This accepts an import (at "e.f"), that it reroutes to an export (at "f") - var module = new WebAssembly.Module(bytes); - var instance = new WebAssembly.Instance(module, { - e: { - f: func - } - }); - var wrappedFunc = instance.exports.f; - return wrappedFunc; -} - -// Add a wasm function to the table. -function addFunctionWasm(func, sig) { - var table = wasmTable; - var ret = table.length; - - // Grow the table - try { - table.grow(1); - } catch (err) { - if (!err instanceof RangeError) { - throw err; - } - throw 'Unable to grow wasm table. Use a higher value for RESERVED_FUNCTION_POINTERS or set ALLOW_TABLE_GROWTH.'; - } - - // Insert new element - try { - // Attempting to call this with JS function will cause of table.set() to fail - table.set(ret, func); - } catch (err) { - if (!err instanceof TypeError) { - throw err; - } - assert(typeof sig !== 'undefined', 'Missing signature argument to addFunction'); - var wrapped = convertJsFunctionToWasm(func, sig); - table.set(ret, wrapped); - } - - return ret; -} - -function removeFunctionWasm(index) { - // TODO(sbc): Look into implementing this to allow re-using of table slots -} - -// 'sig' parameter is required for the llvm backend but only when func is not -// already a WebAssembly function. +// 'sig' parameter is only used on LLVM wasm backend function addFunction(func, sig) { - - var base = 0; for (var i = base; i < base + 0; i++) { if (!functionPointers[i]) { @@ -443,11 +324,9 @@ function addFunction(func, sig) { } } throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.'; - } function removeFunction(index) { - functionPointers[index-jsCallStartIndex] = null; } @@ -493,18 +372,13 @@ function dynCall(sig, ptr, args) { } } -var tempRet0 = 0; - -var setTempRet0 = function(value) { - tempRet0 = value; -}; - -var getTempRet0 = function() { - return tempRet0; -}; var Runtime = { + // FIXME backwards compatibility layer for ports. Support some Runtime.* + // for now, fix it there, then remove it from here. That way we + // can minimize any period of breakage. + dynCall: dynCall, // for SDL2 port }; // The address globals begin at. Very low in memory, for code size and optimization opportunities. @@ -514,8 +388,6 @@ var Runtime = { var GLOBAL_BASE = 1024; - - // === Preamble library stuff === // Documentation for the public APIs defined in this file must be updated in: @@ -528,70 +400,11 @@ var GLOBAL_BASE = 1024; -if (typeof WebAssembly !== 'object') { - err('no native wasm support detected'); -} - - -// In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking. -// In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties) - -/** @type {function(number, number, string, boolean=)} */ -function setValue(ptr, value, type, noSafe) { - type = type || 'i8'; - if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit - switch(type) { - case 'i1': HEAP8[((ptr)>>0)]=value; break; - case 'i8': HEAP8[((ptr)>>0)]=value; break; - case 'i16': HEAP16[((ptr)>>1)]=value; break; - case 'i32': HEAP32[((ptr)>>2)]=value; break; - case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)]=tempI64[0],HEAP32[(((ptr)+(4))>>2)]=tempI64[1]); break; - case 'float': HEAPF32[((ptr)>>2)]=value; break; - case 'double': HEAPF64[((ptr)>>3)]=value; break; - default: abort('invalid type for setValue: ' + type); - } -} - -/** @type {function(number, string, boolean=)} */ -function getValue(ptr, type, noSafe) { - type = type || 'i8'; - if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit - switch(type) { - case 'i1': return HEAP8[((ptr)>>0)]; - case 'i8': return HEAP8[((ptr)>>0)]; - case 'i16': return HEAP16[((ptr)>>1)]; - case 'i32': return HEAP32[((ptr)>>2)]; - case 'i64': return HEAP32[((ptr)>>2)]; - case 'float': return HEAPF32[((ptr)>>2)]; - case 'double': return HEAPF64[((ptr)>>3)]; - default: abort('invalid type for getValue: ' + type); - } - return null; -} - - - - - -// Wasm globals - -var wasmMemory; - -// Potentially used for direct table calls. -var wasmTable; - - //======================================== // 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 ABORT = 0; // whether we are quitting the application. no code should run after this. set in exit() and abort() var EXITSTATUS = 0; /** @type {function(*, string=)} */ @@ -601,6 +414,8 @@ function assert(condition, text) { } } +var globalScope = this; + // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) function getCFunc(ident) { var func = Module['_' + ident]; // closure exported function @@ -608,29 +423,44 @@ function getCFunc(ident) { return func; } +var JSfuncs = { + // Helpers for cwrap -- it can't refer to Runtime directly because it might + // be renamed by closure, instead it calls JSfuncs['stackSave'].body to find + // out what the minified function name is. + 'stackSave': function() { + stackSave() + }, + 'stackRestore': function() { + stackRestore() + }, + // type conversion from js to c + 'arrayToC' : function(arr) { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + }, + 'stringToC' : function(str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { // null string + // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' + var len = (str.length << 2) + 1; + ret = stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + } +}; + +// For fast lookup of conversion functions +var toC = { + 'string': JSfuncs['stringToC'], 'array': JSfuncs['arrayToC'] +}; + + // C calling interface. function ccall(ident, returnType, argTypes, args, opts) { - // For fast lookup of conversion functions - var toC = { - 'string': function(str) { - var ret = 0; - if (str !== null && str !== undefined && str !== 0) { // null string - // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' - var len = (str.length << 2) + 1; - ret = stackAlloc(len); - stringToUTF8(str, ret, len); - } - return ret; - }, - 'array': function(arr) { - var ret = stackAlloc(arr.length); - writeArrayToMemory(arr, ret); - return ret; - } - }; - function convertReturnValue(ret) { - if (returnType === 'string') return UTF8ToString(ret); + if (returnType === 'string') return Pointer_stringify(ret); if (returnType === 'boolean') return Boolean(ret); return ret; } @@ -669,10 +499,44 @@ function cwrap(ident, returnType, argTypes, opts) { } } +/** @type {function(number, number, string, boolean=)} */ +function setValue(ptr, value, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': HEAP8[((ptr)>>0)]=value; break; + case 'i8': HEAP8[((ptr)>>0)]=value; break; + case 'i16': HEAP16[((ptr)>>1)]=value; break; + case 'i32': HEAP32[((ptr)>>2)]=value; break; + case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)]=tempI64[0],HEAP32[(((ptr)+(4))>>2)]=tempI64[1]); break; + case 'float': HEAPF32[((ptr)>>2)]=value; break; + case 'double': HEAPF64[((ptr)>>3)]=value; break; + default: abort('invalid type for setValue: ' + type); + } +} + +/** @type {function(number, string, boolean=)} */ +function getValue(ptr, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': return HEAP8[((ptr)>>0)]; + case 'i8': return HEAP8[((ptr)>>0)]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP32[((ptr)>>2)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + default: abort('invalid type for getValue: ' + type); + } + return null; +} + var ALLOC_NORMAL = 0; // Tries to use _malloc() var ALLOC_STACK = 1; // Lives for the duration of the current function call -var ALLOC_DYNAMIC = 2; // Cannot be freed except through sbrk -var ALLOC_NONE = 3; // Do not allocate +var ALLOC_STATIC = 2; // Cannot be freed +var ALLOC_DYNAMIC = 3; // Cannot be freed except through sbrk +var ALLOC_NONE = 4; // Do not allocate // allocate(): This is for internal use. You can use it yourself as well, but the interface // is a little tricky (see docs right below). The reason is that it is optimized @@ -704,9 +568,7 @@ function allocate(slab, types, allocator, ptr) { if (allocator == ALLOC_NONE) { ret = ptr; } else { - ret = [_malloc, - stackAlloc, - dynamicAlloc][allocator](Math.max(size, singleType ? 1 : types.length)); + ret = [typeof _malloc === 'function' ? _malloc : staticAlloc, stackAlloc, staticAlloc, dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); } if (zeroinit) { @@ -760,16 +622,41 @@ function allocate(slab, types, allocator, ptr) { // Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready function getMemory(size) { + if (!staticSealed) return staticAlloc(size); if (!runtimeInitialized) return dynamicAlloc(size); return _malloc(size); } - - - /** @type {function(number, number=)} */ function Pointer_stringify(ptr, length) { - abort("this function has been removed - you should use UTF8ToString(ptr, maxBytesToRead) instead!"); + if (length === 0 || !ptr) return ''; + // Find the length, and check for UTF while doing so + var hasUtf = 0; + var t; + var i = 0; + while (1) { + t = HEAPU8[(((ptr)+(i))>>0)]; + hasUtf |= t; + if (t == 0 && !length) break; + i++; + if (length && i == length) break; + } + if (!length) length = i; + + var ret = ''; + + if (hasUtf < 128) { + var MAX_CHUNK = 1024; // split up into chunks, because .apply on a huge string can overflow the stack + var curr; + while (length > 0) { + curr = String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK))); + ret = ret ? ret + curr : curr; + ptr += MAX_CHUNK; + length -= MAX_CHUNK; + } + return ret; + } + return UTF8ToString(ptr); } // Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns @@ -778,7 +665,7 @@ function Pointer_stringify(ptr, length) { function AsciiToString(ptr) { var str = ''; while (1) { - var ch = HEAPU8[((ptr++)>>0)]; + var ch = HEAP8[((ptr++)>>0)]; if (!ch) return str; str += String.fromCharCode(ch); } @@ -791,46 +678,46 @@ function stringToAscii(str, outPtr) { return writeAsciiToMemory(str, outPtr, false); } - // Given a pointer 'ptr' 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. var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; - -/** - * @param {number} idx - * @param {number=} maxBytesToRead - * @return {string} - */ -function UTF8ArrayToString(u8Array, idx, maxBytesToRead) { - var endIdx = idx + maxBytesToRead; +function UTF8ArrayToString(u8Array, idx) { var endPtr = idx; // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. - // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity) - while (u8Array[endPtr] && !(endPtr >= endIdx)) ++endPtr; + while (u8Array[endPtr]) ++endPtr; if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) { return UTF8Decoder.decode(u8Array.subarray(idx, endPtr)); } else { + var u0, u1, u2, u3, u4, u5; + var str = ''; - // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that - while (idx < endPtr) { - // For UTF8 byte structure, see: - // http://en.wikipedia.org/wiki/UTF-8#Description - // https://www.ietf.org/rfc/rfc2279.txt - // https://tools.ietf.org/html/rfc3629 - var u0 = u8Array[idx++]; + while (1) { + // 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 + u0 = u8Array[idx++]; + if (!u0) return str; if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } - var u1 = u8Array[idx++] & 63; + u1 = u8Array[idx++] & 63; if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } - var u2 = u8Array[idx++] & 63; + u2 = u8Array[idx++] & 63; if ((u0 & 0xF0) == 0xE0) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; } else { - u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (u8Array[idx++] & 63); + u3 = u8Array[idx++] & 63; + if ((u0 & 0xF8) == 0xF0) { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | u3; + } else { + u4 = u8Array[idx++] & 63; + if ((u0 & 0xFC) == 0xF8) { + u0 = ((u0 & 3) << 24) | (u1 << 18) | (u2 << 12) | (u3 << 6) | u4; + } else { + u5 = u8Array[idx++] & 63; + u0 = ((u0 & 1) << 30) | (u1 << 24) | (u2 << 18) | (u3 << 12) | (u4 << 6) | u5; + } + } } - if (u0 < 0x10000) { str += String.fromCharCode(u0); } else { @@ -839,26 +726,13 @@ function UTF8ArrayToString(u8Array, idx, maxBytesToRead) { } } } - return str; } -// 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. -// 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 (i.e. maxBytesToRead will -// not produce a string of exact length [ptr, ptr+maxBytesToRead[) -// N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may -// throw JS JIT optimizations off, so it is worth to consider consistently using one -// style or the other. -/** - * @param {number} ptr - * @param {number=} maxBytesToRead - * @return {string} - */ -function UTF8ToString(ptr, maxBytesToRead) { - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; +// 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. + +function UTF8ToString(ptr) { + return UTF8ArrayToString(HEAPU8,ptr); } // Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', @@ -901,12 +775,27 @@ function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) { outU8Array[outIdx++] = 0xE0 | (u >> 12); outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); outU8Array[outIdx++] = 0x80 | (u & 63); - } else { + } else if (u <= 0x1FFFFF) { if (outIdx + 3 >= endIdx) break; outU8Array[outIdx++] = 0xF0 | (u >> 18); outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x3FFFFFF) { + if (outIdx + 4 >= endIdx) break; + outU8Array[outIdx++] = 0xF8 | (u >> 24); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 5 >= endIdx) break; + outU8Array[outIdx++] = 0xFC | (u >> 30); + outU8Array[outIdx++] = 0x80 | ((u >> 24) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); } } // Null-terminate the pointer to the buffer. @@ -924,6 +813,7 @@ function stringToUTF8(str, outPtr, maxBytesToWrite) { } // Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. + function lengthBytesUTF8(str) { var len = 0; for (var i = 0; i < str.length; ++i) { @@ -931,15 +821,23 @@ function lengthBytesUTF8(str) { // See http://unicode.org/faq/utf_bom.html#utf16-3 var u = str.charCodeAt(i); // possibly a lead surrogate if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); - if (u <= 0x7F) ++len; - else if (u <= 0x7FF) len += 2; - else if (u <= 0xFFFF) len += 3; - else len += 4; + if (u <= 0x7F) { + ++len; + } else if (u <= 0x7FF) { + len += 2; + } else if (u <= 0xFFFF) { + len += 3; + } else if (u <= 0x1FFFFF) { + len += 4; + } else if (u <= 0x3FFFFFF) { + len += 5; + } else { + len += 6; + } } return len; } - // Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns // a copy of that string as a Javascript String object. @@ -1093,42 +991,6 @@ function allocateUTF8OnStack(str) { return ret; } -// Deprecated: This function should not be called because it is unsafe and does not provide -// a maximum length limit of how many bytes it is allowed to write. Prefer calling the -// function stringToUTF8Array() instead, which takes in a maximum length that can be used -// to be secure from out of bounds writes. -/** @deprecated */ -function writeStringToMemory(string, buffer, dontAddNull) { - warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); - - var /** @type {number} */ lastChar, /** @type {number} */ end; - if (dontAddNull) { - // stringToUTF8Array always appends null. If we don't want to do that, remember the - // character that existed at the location where the null will be placed, and restore - // that after the write (below). - end = buffer + lengthBytesUTF8(string); - lastChar = HEAP8[end]; - } - stringToUTF8(string, buffer, Infinity); - if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. -} - -function writeArrayToMemory(array, buffer) { - HEAP8.set(array, buffer); -} - -function writeAsciiToMemory(str, buffer, dontAddNull) { - for (var i = 0; i < str.length; ++i) { - HEAP8[((buffer++)>>0)]=str.charCodeAt(i); - } - // Null-terminate the pointer to the HEAP. - if (!dontAddNull) HEAP8[((buffer)>>0)]=0; -} - - - - - function demangle(func) { return func; } @@ -1139,7 +1001,7 @@ function demangleAll(text) { return text.replace(regex, function(x) { var y = demangle(x); - return x === y ? x : (y + ' [' + x + ']'); + return x === y ? x : (x + ' [' + y + ']'); }); } @@ -1166,13 +1028,12 @@ function stackTrace() { return demangleAll(js); } - - // Memory management var PAGE_SIZE = 16384; var WASM_PAGE_SIZE = 65536; var ASMJS_PAGE_SIZE = 16777216; +var MIN_TOTAL_MEMORY = 16777216; function alignUp(x, multiple) { if (x % multiple > 0) { @@ -1201,6 +1062,10 @@ var HEAP, /** @type {Float64Array} */ HEAPF64; +function updateGlobalBuffer(buf) { + Module['buffer'] = buffer = buf; +} + function updateGlobalBufferViews() { Module['HEAP8'] = HEAP8 = new Int8Array(buffer); Module['HEAP16'] = HEAP16 = new Int16Array(buffer); @@ -1212,53 +1077,116 @@ function updateGlobalBufferViews() { Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer); } +var STATIC_BASE, STATICTOP, staticSealed; // static area +var STACK_BASE, STACKTOP, STACK_MAX; // stack area +var DYNAMIC_BASE, DYNAMICTOP_PTR; // dynamic area handled by sbrk -var STATIC_BASE = 1024, - STACK_BASE = 127296, - STACKTOP = STACK_BASE, - STACK_MAX = 5370176, - DYNAMIC_BASE = 5370176, - DYNAMICTOP_PTR = 127264; + STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0; + staticSealed = false; -var TOTAL_STACK = 5242880; +function abortOnCannotGrowMemory() { + abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 '); +} -var INITIAL_TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 134217728; -if (INITIAL_TOTAL_MEMORY < TOTAL_STACK) err('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); +if (!Module['reallocBuffer']) Module['reallocBuffer'] = function(size) { + var ret; + try { + if (ArrayBuffer.transfer) { + ret = ArrayBuffer.transfer(buffer, size); + } else { + var oldHEAP8 = HEAP8; + ret = new ArrayBuffer(size); + var temp = new Int8Array(ret); + temp.set(oldHEAP8); + } + } catch(e) { + return false; + } + var success = _emscripten_replace_memory(ret); + if (!success) return false; + return ret; +}; + +function enlargeMemory() { + // TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top. + + + var PAGE_MULTIPLE = Module["usingWasm"] ? WASM_PAGE_SIZE : ASMJS_PAGE_SIZE; // In wasm, heap size must be a multiple of 64KB. In asm.js, they need to be multiples of 16MB. + var LIMIT = 2147483648 - PAGE_MULTIPLE; // We can do one page short of 2GB as theoretical maximum. + + if (HEAP32[DYNAMICTOP_PTR>>2] > LIMIT) { + return false; + } + + var OLD_TOTAL_MEMORY = TOTAL_MEMORY; + TOTAL_MEMORY = Math.max(TOTAL_MEMORY, MIN_TOTAL_MEMORY); // So the loop below will not be infinite, and minimum asm.js memory size is 16MB. + + while (TOTAL_MEMORY < HEAP32[DYNAMICTOP_PTR>>2]) { // Keep incrementing the heap size as long as it's less than what is requested. + if (TOTAL_MEMORY <= 536870912) { + TOTAL_MEMORY = alignUp(2 * TOTAL_MEMORY, PAGE_MULTIPLE); // Simple heuristic: double until 1GB... + } else { + // ..., but after that, add smaller increments towards 2GB, which we cannot reach + TOTAL_MEMORY = Math.min(alignUp((3 * TOTAL_MEMORY + 2147483648) / 4, PAGE_MULTIPLE), LIMIT); + } + } + + + var replacement = Module['reallocBuffer'](TOTAL_MEMORY); + if (!replacement || replacement.byteLength != TOTAL_MEMORY) { + // restore the state to before this call, we failed + TOTAL_MEMORY = OLD_TOTAL_MEMORY; + return false; + } + + // everything worked + + updateGlobalBuffer(replacement); + updateGlobalBufferViews(); + + + + return true; +} + +var byteLength; +try { + byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get); + byteLength(new ArrayBuffer(4)); // can fail on older ie +} catch(e) { // can fail on older node/v8 + byteLength = function(buffer) { return buffer.byteLength; }; +} + +var TOTAL_STACK = Module['TOTAL_STACK'] || 5242880; +var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 536870912; +if (TOTAL_MEMORY < TOTAL_STACK) err('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); // Initialize the runtime's memory - - - - if (Module['wasmMemory']) { - wasmMemory = Module['wasmMemory']; - } else { - wasmMemory = new WebAssembly.Memory({ - 'initial': INITIAL_TOTAL_MEMORY / WASM_PAGE_SIZE - }); +// Use a provided buffer, if there is one, or else allocate a new one +if (Module['buffer']) { + buffer = Module['buffer']; +} else { + // Use a WebAssembly memory where available + if (typeof WebAssembly === 'object' && typeof WebAssembly.Memory === 'function') { + Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE }); + buffer = Module['wasmMemory'].buffer; + } else + { + buffer = new ArrayBuffer(TOTAL_MEMORY); } - - -if (wasmMemory) { - buffer = wasmMemory.buffer; + Module['buffer'] = buffer; } - -// If the user provides an incorrect length, just use that length instead rather than providing the user to -// specifically provide the memory length with Module['TOTAL_MEMORY']. -INITIAL_TOTAL_MEMORY = buffer.byteLength; updateGlobalBufferViews(); -HEAP32[DYNAMICTOP_PTR>>2] = DYNAMIC_BASE; - - - - +function getTotalMemory() { + return TOTAL_MEMORY; +} // Endianness check (note: assumes compiler arch was little-endian) @@ -1303,18 +1231,18 @@ function preRun() { callRuntimeCallbacks(__ATPRERUN__); } -function initRuntime() { +function ensureInitRuntime() { + if (runtimeInitialized) return; runtimeInitialized = true; - callRuntimeCallbacks(__ATINIT__); } function preMain() { - callRuntimeCallbacks(__ATMAIN__); } function exitRuntime() { + callRuntimeCallbacks(__ATEXIT__); runtimeExited = true; } @@ -1342,12 +1270,45 @@ function addOnPreMain(cb) { } function addOnExit(cb) { + __ATEXIT__.unshift(cb); } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb); } +// Deprecated: This function should not be called because it is unsafe and does not provide +// a maximum length limit of how many bytes it is allowed to write. Prefer calling the +// function stringToUTF8Array() instead, which takes in a maximum length that can be used +// to be secure from out of bounds writes. +/** @deprecated */ +function writeStringToMemory(string, buffer, dontAddNull) { + warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8Array always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. +} + +function writeArrayToMemory(array, buffer) { + HEAP8.set(array, buffer); +} + +function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { + HEAP8[((buffer++)>>0)]=str.charCodeAt(i); + } + // Null-terminate the pointer to the HEAP. + if (!dontAddNull) HEAP8[((buffer)>>0)]=0; +} + function unSign(value, bits, ignore) { if (value >= 0) { return value; @@ -1370,7 +1331,6 @@ function reSign(value, bits, ignore) { } - var Math_abs = Math.abs; var Math_cos = Math.cos; var Math_sin = Math.sin; @@ -1393,12 +1353,10 @@ var Math_max = Math.max; var Math_clz32 = Math.clz32; var Math_trunc = Math.trunc; - - // 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). +// PRE_RUN_ADDITIONS (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. @@ -1439,6 +1397,7 @@ Module["preloadedImages"] = {}; // maps url to image data Module["preloadedAudios"] = {}; // maps url to audio data + var memoryInitializer = null; @@ -1446,11 +1405,6 @@ var memoryInitializer = null; -// Copyright 2017 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. - // Prefix of data URIs emitted by SINGLE_FILE and related options. var dataURIPrefix = 'data:application/octet-stream;base64,'; @@ -1464,162 +1418,276 @@ function isDataURI(filename) { -var wasmBinaryFile = 'MpqCmp.wasm'; -if (!isDataURI(wasmBinaryFile)) { - wasmBinaryFile = locateFile(wasmBinaryFile); -} +function integrateWasmJS() { + // wasm.js has several methods for creating the compiled code module here: + // * 'native-wasm' : use native WebAssembly support in the browser + // * 'interpret-s-expr': load s-expression code from a .wast and interpret + // * 'interpret-binary': load binary wasm and interpret + // * 'interpret-asm2wasm': load asm.js code, translate to wasm, and interpret + // * 'asmjs': no wasm, just load the asm.js code and use that (good for testing) + // The method is set at compile time (BINARYEN_METHOD) + // The method can be a comma-separated list, in which case, we will try the + // options one by one. Some of them can fail gracefully, and then we can try + // the next. -function getBinary() { - try { - if (Module['wasmBinary']) { - return new Uint8Array(Module['wasmBinary']); - } - if (readBinary) { - return readBinary(wasmBinaryFile); - } else { - throw "both async and sync fetching of the wasm failed"; - } - } - catch (err) { - abort(err); - } -} + // inputs -function getBinaryPromise() { - // if we don't have the binary yet, and have the Fetch api, use that - // in some environments, like Electron's render process, Fetch api may be present, but have a different context than expected, let's only use it on the Web - if (!Module['wasmBinary'] && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && typeof fetch === 'function') { - return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { - if (!response['ok']) { - throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; + var method = 'native-wasm'; + + var wasmTextFile = 'MpqCmp.wast'; + var wasmBinaryFile = 'MpqCmp.wasm'; + var asmjsCodeFile = 'MpqCmp.temp.asm.js'; + + if (!isDataURI(wasmTextFile)) { + wasmTextFile = locateFile(wasmTextFile); + } + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); + } + if (!isDataURI(asmjsCodeFile)) { + asmjsCodeFile = locateFile(asmjsCodeFile); + } + + // utilities + + var wasmPageSize = 64*1024; + + var info = { + 'global': null, + 'env': null, + 'asm2wasm': asm2wasmImports, + 'parent': Module // Module inside wasm-js.cpp refers to wasm-js.cpp; this allows access to the outside program. + }; + + var exports = null; + + + function mergeMemory(newBuffer) { + // The wasm instance creates its memory. But static init code might have written to + // buffer already, including the mem init file, and we must copy it over in a proper merge. + // TODO: avoid this copy, by avoiding such static init writes + // TODO: in shorter term, just copy up to the last static init write + var oldBuffer = Module['buffer']; + if (newBuffer.byteLength < oldBuffer.byteLength) { + err('the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here'); + } + var oldView = new Int8Array(oldBuffer); + var newView = new Int8Array(newBuffer); + + + newView.set(oldView); + updateGlobalBuffer(newBuffer); + updateGlobalBufferViews(); + } + + function fixImports(imports) { + return imports; + } + + function getBinary() { + try { + if (Module['wasmBinary']) { + return new Uint8Array(Module['wasmBinary']); } - return response['arrayBuffer'](); - }).catch(function () { - return getBinary(); + if (Module['readBinary']) { + return Module['readBinary'](wasmBinaryFile); + } else { + throw "both async and sync fetching of the wasm failed"; + } + } + catch (err) { + abort(err); + } + } + + function getBinaryPromise() { + // if we don't have the binary yet, and have the Fetch api, use that + // in some environments, like Electron's render process, Fetch api may be present, but have a different context than expected, let's only use it on the Web + if (!Module['wasmBinary'] && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && typeof fetch === 'function') { + return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; + } + return response['arrayBuffer'](); + }).catch(function () { + return getBinary(); + }); + } + // Otherwise, getBinary should be able to get it synchronously + return new Promise(function(resolve, reject) { + resolve(getBinary()); }); } - // Otherwise, getBinary should be able to get it synchronously - return new Promise(function(resolve, reject) { - resolve(getBinary()); - }); -} + + // do-method functions - -// Create the wasm instance. -// Receives the wasm imports, returns the exports. -function createWasm(env) { - - // prepare imports - var info = { - 'env': env - , - 'global': { + function doNativeWasm(global, env, providedBuffer) { + if (typeof WebAssembly !== 'object') { + err('no native wasm support detected'); + return false; + } + // prepare memory import + if (!(Module['wasmMemory'] instanceof WebAssembly.Memory)) { + err('no native wasm Memory in use'); + return false; + } + env['memory'] = Module['wasmMemory']; + // Load the wasm module and create an instance of using native support in the JS engine. + info['global'] = { 'NaN': NaN, 'Infinity': Infinity - }, - 'global.Math': Math, - 'asm2wasm': asm2wasmImports - }; - // 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 - function receiveInstance(instance, module) { - var exports = instance.exports; - Module['asm'] = exports; - removeRunDependency('wasm-instantiate'); - } - addRunDependency('wasm-instantiate'); + }; + info['global.Math'] = Math; + info['env'] = env; + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + function receiveInstance(instance, module) { + exports = instance.exports; + if (exports.memory) mergeMemory(exports.memory); + Module['asm'] = exports; + Module["usingWasm"] = true; + removeRunDependency('wasm-instantiate'); + } + addRunDependency('wasm-instantiate'); + // 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. + if (Module['instantiateWasm']) { + try { + return Module['instantiateWasm'](info, receiveInstance); + } catch(e) { + err('Module.instantiateWasm callback failed with error: ' + e); + return false; + } + } - function receiveInstantiatedSource(output) { - // 'output' is a WebAssemblyInstantiatedSource 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 USE_PTHREADS-enabled path. - receiveInstance(output['instance']); - } - - - function instantiateArrayBuffer(receiver) { - return getBinaryPromise().then(function(binary) { - return WebAssembly.instantiate(binary, info); - }).then(receiver, function(reason) { - err('failed to asynchronously prepare wasm: ' + reason); - abort(reason); - }); - } - - // Prefer streaming instantiation if available. - function instantiateAsync() { + function receiveInstantiatedSource(output) { + // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + receiveInstance(output['instance'], output['module']); + } + function instantiateArrayBuffer(receiver) { + getBinaryPromise().then(function(binary) { + return WebAssembly.instantiate(binary, info); + }).then(receiver).catch(function(reason) { + err('failed to asynchronously prepare wasm: ' + reason); + abort(reason); + }); + } + // Prefer streaming instantiation if available. if (!Module['wasmBinary'] && typeof WebAssembly.instantiateStreaming === 'function' && !isDataURI(wasmBinaryFile) && typeof fetch === 'function') { - fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { - return WebAssembly.instantiateStreaming(response, info) - .then(receiveInstantiatedSource, function(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'); - instantiateArrayBuffer(receiveInstantiatedSource); - }); - }); + WebAssembly.instantiateStreaming(fetch(wasmBinaryFile, { credentials: 'same-origin' }), info) + .then(receiveInstantiatedSource) + .catch(function(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'); + instantiateArrayBuffer(receiveInstantiatedSource); + }); } else { - return instantiateArrayBuffer(receiveInstantiatedSource); - } - } - // 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. - if (Module['instantiateWasm']) { - try { - var exports = Module['instantiateWasm'](info, receiveInstance); - return exports; - } catch(e) { - err('Module.instantiateWasm callback failed with error: ' + e); - return false; + instantiateArrayBuffer(receiveInstantiatedSource); } + return {}; // no exports yet; we'll fill them in later } - instantiateAsync(); - return {}; // no exports yet; we'll fill them in later + + // We may have a preloaded value in Module.asm, save it + Module['asmPreload'] = Module['asm']; + + // Memory growth integration code + + var asmjsReallocBuffer = Module['reallocBuffer']; + + var wasmReallocBuffer = function(size) { + var PAGE_MULTIPLE = Module["usingWasm"] ? WASM_PAGE_SIZE : ASMJS_PAGE_SIZE; // In wasm, heap size must be a multiple of 64KB. In asm.js, they need to be multiples of 16MB. + size = alignUp(size, PAGE_MULTIPLE); // round up to wasm page size + var old = Module['buffer']; + var oldSize = old.byteLength; + if (Module["usingWasm"]) { + // native wasm support + try { + var result = Module['wasmMemory'].grow((size - oldSize) / wasmPageSize); // .grow() takes a delta compared to the previous size + if (result !== (-1 | 0)) { + // success in native wasm memory growth, get the buffer from the memory + return Module['buffer'] = Module['wasmMemory'].buffer; + } else { + return null; + } + } catch(e) { + return null; + } + } + }; + + Module['reallocBuffer'] = function(size) { + if (finalMethod === 'asmjs') { + return asmjsReallocBuffer(size); + } else { + return wasmReallocBuffer(size); + } + }; + + // we may try more than one; this is the final one, that worked and we are using + var finalMethod = ''; + + // Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate + // the wasm module at that time, and it receives imports and provides exports and so forth, the app + // doesn't need to care that it is wasm or olyfilled wasm or asm.js. + + Module['asm'] = function(global, env, providedBuffer) { + env = fixImports(env); + + // import table + if (!env['table']) { + var TABLE_SIZE = Module['wasmTableSize']; + if (TABLE_SIZE === undefined) TABLE_SIZE = 1024; // works in binaryen interpreter at least + var MAX_TABLE_SIZE = Module['wasmMaxTableSize']; + if (typeof WebAssembly === 'object' && typeof WebAssembly.Table === 'function') { + if (MAX_TABLE_SIZE !== undefined) { + env['table'] = new WebAssembly.Table({ 'initial': TABLE_SIZE, 'maximum': MAX_TABLE_SIZE, 'element': 'anyfunc' }); + } else { + env['table'] = new WebAssembly.Table({ 'initial': TABLE_SIZE, element: 'anyfunc' }); + } + } else { + env['table'] = new Array(TABLE_SIZE); // works in binaryen interpreter at least + } + Module['wasmTable'] = env['table']; + } + + if (!env['memoryBase']) { + env['memoryBase'] = Module['STATIC_BASE']; // tell the memory segments where to place themselves + } + if (!env['tableBase']) { + env['tableBase'] = 0; // table starts at 0 by default, in dynamic linking this will change + } + + // try the methods. each should return the exports if it succeeded + + var exports; + exports = doNativeWasm(global, env, providedBuffer); + + assert(exports, 'no binaryen method succeeded.'); + + + return exports; + }; + + var methodHandler = Module['asm']; // note our method handler, as we may modify Module['asm'] later } -// Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate -// the wasm module at that time, and it receives imports and provides exports and so forth, the app -// doesn't need to care that it is wasm or asm.js. - -Module['asm'] = function(global, env, providedBuffer) { - // memory was already allocated (so js could use the buffer) - env['memory'] = wasmMemory - ; - // import table - env['table'] = wasmTable = new WebAssembly.Table({ - 'initial': 402, - 'maximum': 402, - 'element': 'anyfunc' - }); - // With the wasm backend __memory_base and __table_base and only needed for - // relocatable output. - env['__memory_base'] = 1024; // tell the memory segments where to place themselves - // table starts at 0 by default (even in dynamic linking, for the main module) - env['__table_base'] = 0; - - var exports = createWasm(env); - return exports; -}; - -// Globals used by JS i64 conversions -var tempDouble; -var tempI64; +integrateWasmJS(); // === Body === var ASM_CONSTS = []; -function _do_error(err){ var end = HEAPU8.indexOf( 0, err ); var text = String.fromCharCode.apply(null, HEAPU8.subarray( err, end )); self.DApi.exit_error( text ); } +function _do_error(err){ var end = HEAPU8.indexOf(0, err); var text = String.fromCharCode.apply(null, HEAPU8.subarray(err, end)); self.DApi.exit_error(text); } function _do_progress(done,total){ self.DApi.progress(done, total); } function _get_file_contents(ptr,offset,size){ self.DApi.get_file_contents(HEAPU8.subarray(ptr, ptr + size), offset); } function _put_file_contents(ptr,offset,size){ self.DApi.put_file_contents(HEAPU8.subarray(ptr, ptr + size), offset); } @@ -1627,8 +1695,10 @@ function _put_file_size(size){ self.DApi.put_file_size(size); } -// STATICTOP = STATIC_BASE + 126272; -/* global initializers */ /*__ATINIT__.push();*/ +STATIC_BASE = GLOBAL_BASE; + +STATICTOP = STATIC_BASE + 113200; +/* global initializers */ __ATINIT__.push(); @@ -1636,123 +1706,118 @@ function _put_file_size(size){ self.DApi.put_file_size(size); } +var STATIC_BUMP = 113200; +Module["STATIC_BASE"] = STATIC_BASE; +Module["STATIC_BUMP"] = STATIC_BUMP; /* no memory initializer */ -var tempDoublePtr = 127280 +var tempDoublePtr = STATICTOP; STATICTOP += 16; function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; + HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; + HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; + } function copyTempDouble(ptr) { + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; + HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; + HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; + HEAP8[tempDoublePtr+4] = HEAP8[ptr+4]; + HEAP8[tempDoublePtr+5] = HEAP8[ptr+5]; + HEAP8[tempDoublePtr+6] = HEAP8[ptr+6]; + HEAP8[tempDoublePtr+7] = HEAP8[ptr+7]; + } // {{PRE_LIBRARY}} function ___assert_fail(condition, filename, line, func) { - abort('Assertion failed: ' + UTF8ToString(condition) + ', at: ' + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); + abort('Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function']); } function ___cxa_allocate_exception(size) { return _malloc(size); } - - var ___exception_infos={}; - - var ___exception_caught= []; - - function ___exception_addRef(ptr) { - if (!ptr) return; - var info = ___exception_infos[ptr]; - info.refcount++; - } - - function ___exception_deAdjust(adjusted) { - if (!adjusted || ___exception_infos[adjusted]) return adjusted; - for (var key in ___exception_infos) { - var ptr = +key; // the iteration key is a string, and if we throw this, it must be an integer as that is what we look for - var adj = ___exception_infos[ptr].adjusted; - var len = adj.length; - for (var i = 0; i < len; i++) { - if (adj[i] === adjusted) { - return ptr; - } + function ___cxa_find_matching_catch_2() { + return ___cxa_find_matching_catch.apply(null, arguments); } - } - return adjusted; - }function ___cxa_begin_catch(ptr) { - var info = ___exception_infos[ptr]; - if (info && !info.caught) { - info.caught = true; - __ZSt18uncaught_exceptionv.uncaught_exceptions--; - } - if (info) info.rethrown = false; - ___exception_caught.push(ptr); - ___exception_addRef(___exception_deAdjust(ptr)); - return ptr; - } - - var ___exception_last=0; - - function ___cxa_free_exception(ptr) { try { return _free(ptr); - } catch(e) { - } - }function ___exception_decRef(ptr) { - if (!ptr) return; - var info = ___exception_infos[ptr]; - info.refcount--; - // A rethrown exception can reach refcount 0; it must not be discarded - // Its next handler will clear the rethrown flag and addRef it, prior to - // final decRef and destruction here - if (info.refcount === 0 && !info.rethrown) { - if (info.destructor) { - Module['dynCall_vi'](info.destructor, ptr); - } - delete ___exception_infos[ptr]; - ___cxa_free_exception(ptr); - } - } - - function ___cxa_end_catch() { - // Clear state flag. - _setThrew(0); - // Call destructor if one is registered then clear it. - var ptr = ___exception_caught.pop(); - if (ptr) { - ___exception_decRef(___exception_deAdjust(ptr)); - ___exception_last = 0; // XXX in decRef? + } catch(e) { // XXX FIXME } } + function __ZSt18uncaught_exceptionv() { // std::uncaught_exception() + return !!__ZSt18uncaught_exceptionv.uncaught_exception; + } + + + var EXCEPTIONS={last:0,caught:[],infos:{},deAdjust:function (adjusted) { + if (!adjusted || EXCEPTIONS.infos[adjusted]) return adjusted; + for (var key in EXCEPTIONS.infos) { + var ptr = +key; // the iteration key is a string, and if we throw this, it must be an integer as that is what we look for + var info = EXCEPTIONS.infos[ptr]; + if (info.adjusted === adjusted) { + return ptr; + } + } + return adjusted; + },addRef:function (ptr) { + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + info.refcount++; + },decRef:function (ptr) { + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + assert(info.refcount > 0); + info.refcount--; + // A rethrown exception can reach refcount 0; it must not be discarded + // Its next handler will clear the rethrown flag and addRef it, prior to + // final decRef and destruction here + if (info.refcount === 0 && !info.rethrown) { + if (info.destructor) { + Module['dynCall_vi'](info.destructor, ptr); + } + delete EXCEPTIONS.infos[ptr]; + ___cxa_free_exception(ptr); + } + },clearRef:function (ptr) { + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + info.refcount = 0; + }}; function ___resumeException(ptr) { - if (!___exception_last) { ___exception_last = ptr; } + if (!EXCEPTIONS.last) { EXCEPTIONS.last = ptr; } throw ptr; }function ___cxa_find_matching_catch() { - var thrown = ___exception_last; + var thrown = EXCEPTIONS.last; if (!thrown) { // just pass through the null ptr return ((setTempRet0(0),0)|0); } - var info = ___exception_infos[thrown]; + var info = EXCEPTIONS.infos[thrown]; var throwntype = info.type; if (!throwntype) { // just pass through the thrown ptr @@ -1760,19 +1825,19 @@ function copyTempDouble(ptr) { } var typeArray = Array.prototype.slice.call(arguments); - var pointer = ___cxa_is_pointer_type(throwntype); + var pointer = Module['___cxa_is_pointer_type'](throwntype); // can_catch receives a **, add indirection - var buffer = 127248; - HEAP32[((buffer)>>2)]=thrown; - thrown = buffer; + if (!___cxa_find_matching_catch.buffer) ___cxa_find_matching_catch.buffer = _malloc(4); + HEAP32[((___cxa_find_matching_catch.buffer)>>2)]=thrown; + thrown = ___cxa_find_matching_catch.buffer; // The different catch blocks are denoted by different types. // Due to inheritance, those types may not precisely match the // type of the thrown object. Find one which matches, and // return the type of the catch block which should be called. for (var i = 0; i < typeArray.length; i++) { - if (typeArray[i] && ___cxa_can_catch(typeArray[i], throwntype, thrown)) { + if (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) { thrown = HEAP32[((thrown)>>2)]; // undo indirection - info.adjusted.push(thrown); + info.adjusted = thrown; return ((setTempRet0(typeArray[i]),thrown)|0); } } @@ -1781,228 +1846,47 @@ function copyTempDouble(ptr) { // typeinfo defined. Best-efforts match just in case. thrown = HEAP32[((thrown)>>2)]; // undo indirection return ((setTempRet0(throwntype),thrown)|0); - } - Module["___cxa_find_matching_catch"] = ___cxa_find_matching_catch;function ___cxa_find_matching_catch_2(a0,a1 - /*``*/) { - return ___cxa_find_matching_catch(a0,a1); - } - - function ___cxa_find_matching_catch_3(a0,a1,a2 - /*``*/) { - return ___cxa_find_matching_catch(a0,a1,a2); - } - - - function ___cxa_pure_virtual() { - ABORT = true; - throw 'Pure virtual function called!'; - } - - function ___cxa_throw(ptr, type, destructor) { - ___exception_infos[ptr] = { + }function ___cxa_throw(ptr, type, destructor) { + EXCEPTIONS.infos[ptr] = { ptr: ptr, - adjusted: [ptr], + adjusted: ptr, type: type, destructor: destructor, refcount: 0, caught: false, rethrown: false }; - ___exception_last = ptr; + EXCEPTIONS.last = ptr; if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { - __ZSt18uncaught_exceptionv.uncaught_exceptions = 1; + __ZSt18uncaught_exceptionv.uncaught_exception = 1; } else { - __ZSt18uncaught_exceptionv.uncaught_exceptions++; + __ZSt18uncaught_exceptionv.uncaught_exception++; } throw ptr; } - function ___cxa_uncaught_exceptions() { - return __ZSt18uncaught_exceptionv.uncaught_exceptions; - } - function ___gxx_personality_v0() { } - - - var PATH={splitPath:function (filename) { - var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; - return splitPathRe.exec(filename).slice(1); - },normalizeArray:function (parts, allowAboveRoot) { - // if the path tries to go above the root, `up` ends up > 0 - var up = 0; - for (var i = parts.length - 1; i >= 0; i--) { - var last = parts[i]; - if (last === '.') { - parts.splice(i, 1); - } else if (last === '..') { - parts.splice(i, 1); - up++; - } else if (up) { - parts.splice(i, 1); - up--; - } - } - // if the path is allowed to go above the root, restore leading ..s - if (allowAboveRoot) { - for (; up; up--) { - parts.unshift('..'); - } - } - return parts; - },normalize:function (path) { - var isAbsolute = path.charAt(0) === '/', - trailingSlash = path.substr(-1) === '/'; - // Normalize the path - path = PATH.normalizeArray(path.split('/').filter(function(p) { - return !!p; - }), !isAbsolute).join('/'); - if (!path && !isAbsolute) { - path = '.'; - } - if (path && trailingSlash) { - path += '/'; - } - return (isAbsolute ? '/' : '') + path; - },dirname:function (path) { - var result = PATH.splitPath(path), - root = result[0], - dir = result[1]; - if (!root && !dir) { - // No dirname whatsoever - return '.'; - } - if (dir) { - // It has a dirname, strip trailing slash - dir = dir.substr(0, dir.length - 1); - } - return root + dir; - },basename:function (path) { - // EMSCRIPTEN return '/'' for '/', not an empty string - if (path === '/') return '/'; - var lastSlash = path.lastIndexOf('/'); - if (lastSlash === -1) return path; - return path.substr(lastSlash+1); - },extname:function (path) { - return PATH.splitPath(path)[3]; - },join:function () { - var paths = Array.prototype.slice.call(arguments, 0); - return PATH.normalize(paths.join('/')); - },join2:function (l, r) { - return PATH.normalize(l + '/' + r); - }};var SYSCALLS={buffers:[null,[],[]],printChar:function (stream, curr) { - var buffer = SYSCALLS.buffers[stream]; - if (curr === 0 || curr === 10) { - (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); - buffer.length = 0; - } else { - buffer.push(curr); - } - },varargs:0,get:function (varargs) { - SYSCALLS.varargs += 4; - var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; - return ret; - },getStr:function () { - var ret = UTF8ToString(SYSCALLS.get()); - return ret; - },get64:function () { - var low = SYSCALLS.get(), high = SYSCALLS.get(); - return low; - },getZero:function () { - SYSCALLS.get(); - }};function ___syscall140(which, varargs) {SYSCALLS.varargs = varargs; - try { - // llseek - var stream = SYSCALLS.getStreamFromFD(), offset_high = SYSCALLS.get(), offset_low = SYSCALLS.get(), result = SYSCALLS.get(), whence = SYSCALLS.get(); - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - - function flush_NO_FILESYSTEM() { - // flush anything remaining in the buffers during shutdown - var fflush = Module["_fflush"]; - if (fflush) fflush(0); - var buffers = SYSCALLS.buffers; - if (buffers[1].length) SYSCALLS.printChar(1, 10); - if (buffers[2].length) SYSCALLS.printChar(2, 10); - }function ___syscall146(which, varargs) {SYSCALLS.varargs = varargs; - try { - // writev - // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 - var stream = SYSCALLS.get(), iov = SYSCALLS.get(), iovcnt = SYSCALLS.get(); - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAP32[(((iov)+(i*8))>>2)]; - var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; - for (var j = 0; j < len; j++) { - SYSCALLS.printChar(stream, HEAPU8[ptr+j]); - } - ret += len; - } - return ret; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - function ___syscall54(which, varargs) {SYSCALLS.varargs = varargs; - try { - // ioctl - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - function ___syscall6(which, varargs) {SYSCALLS.varargs = varargs; - try { - // close - var stream = SYSCALLS.getStreamFromFD(); - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - function _abort() { Module['abort'](); } - function _emscripten_get_heap_size() { - return HEAP8.length; - } - function _llvm_exp2_f32(x) { return Math.pow(2, x); - }function _llvm_exp2_f64(a0 - /*``*/) { - return _llvm_exp2_f32(a0); + }function _llvm_exp2_f64() { + return _llvm_exp2_f32.apply(null, arguments) } - function _llvm_trap() { - abort('trap!'); - } - function _emscripten_memcpy_big(dest, src, num) { HEAPU8.set(HEAPU8.subarray(src, src+num), dest); - } - - - - + return dest; + } @@ -2010,80 +1894,20 @@ function copyTempDouble(ptr) { function ___setErrNo(value) { if (Module['___errno_location']) HEAP32[((Module['___errno_location']())>>2)]=value; return value; - } - - - function abortOnCannotGrowMemory(requestedSize) { - abort('OOM'); - } - - function emscripten_realloc_buffer(size) { - var PAGE_MULTIPLE = 65536; - size = alignUp(size, PAGE_MULTIPLE); // round up to wasm page size - var oldSize = buffer.byteLength; - // native wasm support - // note that this is *not* threadsafe. multiple threads can call .grow(), and each - // presents a delta, so in theory we may over-allocate here (e.g. if two threads - // ask to grow from 256MB to 512MB, we get 2 requests to add +256MB, and may end - // up growing to 768MB (even though we may have been able to make do with 512MB). - // TODO: consider decreasing the step sizes in emscripten_resize_heap - try { - var result = wasmMemory.grow((size - oldSize) / 65536); // .grow() takes a delta compared to the previous size - if (result !== (-1 | 0)) { - // success in native wasm memory growth, get the buffer from the memory - buffer = wasmMemory.buffer; - return true; - } else { - return false; - } - } catch(e) { - return false; - } - }function _emscripten_resize_heap(requestedSize) { - var oldSize = _emscripten_get_heap_size(); - // With pthreads, races can happen (another thread might increase the size in between), so return a failure, and let the caller retry. - - - var PAGE_MULTIPLE = 65536; - var LIMIT = 2147483648 - PAGE_MULTIPLE; // We can do one page short of 2GB as theoretical maximum. - - if (requestedSize > LIMIT) { - return false; - } - - var MIN_TOTAL_MEMORY = 16777216; - var newSize = Math.max(oldSize, MIN_TOTAL_MEMORY); // So the loop below will not be infinite, and minimum asm.js memory size is 16MB. - - // TODO: see realloc_buffer - for PTHREADS we may want to decrease these jumps - while (newSize < requestedSize) { // Keep incrementing the heap size as long as it's less than what is requested. - if (newSize <= 536870912) { - newSize = alignUp(2 * newSize, PAGE_MULTIPLE); // Simple heuristic: double until 1GB... - } else { - // ..., but after that, add smaller increments towards 2GB, which we cannot reach - newSize = Math.min(alignUp((3 * newSize + 2147483648) / 4, PAGE_MULTIPLE), LIMIT); - } - - } - - - - - if (!emscripten_realloc_buffer(newSize)) { - return false; - } - - updateGlobalBufferViews(); - - - - return true; } -var ASSERTIONS = false; +DYNAMICTOP_PTR = staticAlloc(4); -// Copyright 2017 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. +STACK_BASE = STACKTOP = alignMemory(STATICTOP); + +STACK_MAX = STACK_BASE + TOTAL_STACK; + +DYNAMIC_BASE = alignMemory(STACK_MAX); + +HEAP32[DYNAMICTOP_PTR>>2] = DYNAMIC_BASE; + +staticSealed = true; // seal the static portion of memory + +var ASSERTIONS = false; /** @type {function(string, boolean=, number=)} */ function intArrayFromString(stringy, dontAddNull, length) { @@ -2110,358 +1934,222 @@ function intArrayToString(array) { } -// ASM_LIBRARY EXTERN PRIMITIVES: Int8Array,Int32Array +Module['wasmTableSize'] = 163; + +Module['wasmMaxTableSize'] = 163; function invoke_ii(index,a1) { var sp = stackSave(); try { - return dynCall_ii(index,a1); + return Module["dynCall_ii"](index,a1); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_iii(index,a1,a2) { var sp = stackSave(); try { - return dynCall_iii(index,a1,a2); + return Module["dynCall_iii"](index,a1,a2); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_iiii(index,a1,a2,a3) { var sp = stackSave(); try { - return dynCall_iiii(index,a1,a2,a3); + return Module["dynCall_iiii"](index,a1,a2,a3); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_iiiii(index,a1,a2,a3,a4) { var sp = stackSave(); try { - return dynCall_iiiii(index,a1,a2,a3,a4); + return Module["dynCall_iiiii"](index,a1,a2,a3,a4); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_iiiiii(index,a1,a2,a3,a4,a5) { var sp = stackSave(); try { - return dynCall_iiiiii(index,a1,a2,a3,a4,a5); + return Module["dynCall_iiiiii"](index,a1,a2,a3,a4,a5); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6) { var sp = stackSave(); try { - return dynCall_iiiiiii(index,a1,a2,a3,a4,a5,a6); + return Module["dynCall_iiiiiii"](index,a1,a2,a3,a4,a5,a6); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_ji(index,a1) { var sp = stackSave(); try { - return dynCall_ji(index,a1); + return Module["dynCall_ji"](index,a1); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_v(index) { var sp = stackSave(); try { - dynCall_v(index); + Module["dynCall_v"](index); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_vi(index,a1) { var sp = stackSave(); try { - dynCall_vi(index,a1); + Module["dynCall_vi"](index,a1); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_vii(index,a1,a2) { var sp = stackSave(); try { - dynCall_vii(index,a1,a2); + Module["dynCall_vii"](index,a1,a2); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_viii(index,a1,a2,a3) { var sp = stackSave(); try { - dynCall_viii(index,a1,a2,a3); + Module["dynCall_viii"](index,a1,a2,a3); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); + } +} + +function invoke_viiii(index,a1,a2,a3,a4) { + var sp = stackSave(); + try { + Module["dynCall_viiii"](index,a1,a2,a3,a4); + } catch(e) { + stackRestore(sp); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); + } +} + +function invoke_viiiii(index,a1,a2,a3,a4,a5) { + var sp = stackSave(); + try { + Module["dynCall_viiiii"](index,a1,a2,a3,a4,a5); + } catch(e) { + stackRestore(sp); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); + } +} + +function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6) { + var sp = stackSave(); + try { + Module["dynCall_viiiiii"](index,a1,a2,a3,a4,a5,a6); + } catch(e) { + stackRestore(sp); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } function invoke_viji(index,a1,a2,a3,a4) { var sp = stackSave(); try { - dynCall_viji(index,a1,a2,a3,a4); + Module["dynCall_viji"](index,a1,a2,a3,a4); } catch(e) { stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); + if (typeof e !== 'number' && e !== 'longjmp') throw e; + Module["setThrew"](1, 0); } } -var asmGlobalArg = {}; +Module.asmGlobalArg = {}; -var asmLibraryArg = { - "abort": abort, - "setTempRet0": setTempRet0, - "getTempRet0": getTempRet0, - "invoke_ii": invoke_ii, - "invoke_iii": invoke_iii, - "invoke_iiii": invoke_iiii, - "invoke_iiiii": invoke_iiiii, - "invoke_iiiiii": invoke_iiiiii, - "invoke_iiiiiii": invoke_iiiiiii, - "invoke_ji": invoke_ji, - "invoke_v": invoke_v, - "invoke_vi": invoke_vi, - "invoke_vii": invoke_vii, - "invoke_viii": invoke_viii, - "invoke_viji": invoke_viji, - "___assert_fail": ___assert_fail, - "___cxa_allocate_exception": ___cxa_allocate_exception, - "___cxa_begin_catch": ___cxa_begin_catch, - "___cxa_end_catch": ___cxa_end_catch, - "___cxa_find_matching_catch": ___cxa_find_matching_catch, - "___cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, - "___cxa_find_matching_catch_3": ___cxa_find_matching_catch_3, - "___cxa_free_exception": ___cxa_free_exception, - "___cxa_pure_virtual": ___cxa_pure_virtual, - "___cxa_throw": ___cxa_throw, - "___cxa_uncaught_exceptions": ___cxa_uncaught_exceptions, - "___exception_addRef": ___exception_addRef, - "___exception_deAdjust": ___exception_deAdjust, - "___exception_decRef": ___exception_decRef, - "___gxx_personality_v0": ___gxx_personality_v0, - "___resumeException": ___resumeException, - "___setErrNo": ___setErrNo, - "___syscall140": ___syscall140, - "___syscall146": ___syscall146, - "___syscall54": ___syscall54, - "___syscall6": ___syscall6, - "_abort": _abort, - "_do_error": _do_error, - "_do_progress": _do_progress, - "_emscripten_get_heap_size": _emscripten_get_heap_size, - "_emscripten_memcpy_big": _emscripten_memcpy_big, - "_emscripten_resize_heap": _emscripten_resize_heap, - "_get_file_contents": _get_file_contents, - "_llvm_exp2_f32": _llvm_exp2_f32, - "_llvm_exp2_f64": _llvm_exp2_f64, - "_llvm_trap": _llvm_trap, - "_put_file_contents": _put_file_contents, - "_put_file_size": _put_file_size, - "abortOnCannotGrowMemory": abortOnCannotGrowMemory, - "emscripten_realloc_buffer": emscripten_realloc_buffer, - "flush_NO_FILESYSTEM": flush_NO_FILESYSTEM, - "tempDoublePtr": tempDoublePtr, - "DYNAMICTOP_PTR": DYNAMICTOP_PTR -}; +Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiii": invoke_iiiiii, "invoke_iiiiiii": invoke_iiiiiii, "invoke_ji": invoke_ji, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiii": invoke_viiiii, "invoke_viiiiii": invoke_viiiiii, "invoke_viji": invoke_viji, "__ZSt18uncaught_exceptionv": __ZSt18uncaught_exceptionv, "___assert_fail": ___assert_fail, "___cxa_allocate_exception": ___cxa_allocate_exception, "___cxa_find_matching_catch": ___cxa_find_matching_catch, "___cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, "___cxa_free_exception": ___cxa_free_exception, "___cxa_throw": ___cxa_throw, "___gxx_personality_v0": ___gxx_personality_v0, "___resumeException": ___resumeException, "___setErrNo": ___setErrNo, "_abort": _abort, "_do_error": _do_error, "_do_progress": _do_progress, "_emscripten_memcpy_big": _emscripten_memcpy_big, "_get_file_contents": _get_file_contents, "_llvm_exp2_f32": _llvm_exp2_f32, "_llvm_exp2_f64": _llvm_exp2_f64, "_put_file_contents": _put_file_contents, "_put_file_size": _put_file_size, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX }; // EMSCRIPTEN_START_ASM var asm =Module["asm"]// EMSCRIPTEN_END_ASM -(asmGlobalArg, asmLibraryArg, buffer); +(Module.asmGlobalArg, Module.asmLibraryArg, buffer); Module["asm"] = asm; -var _DApi_MpqCmp = Module["_DApi_MpqCmp"] = function() { - return Module["asm"]["_DApi_MpqCmp"].apply(null, arguments) -}; - -var __ZSt18uncaught_exceptionv = Module["__ZSt18uncaught_exceptionv"] = function() { - return Module["asm"]["__ZSt18uncaught_exceptionv"].apply(null, arguments) -}; - -var ___cxa_can_catch = Module["___cxa_can_catch"] = function() { - return Module["asm"]["___cxa_can_catch"].apply(null, arguments) -}; - -var ___cxa_is_pointer_type = Module["___cxa_is_pointer_type"] = function() { - return Module["asm"]["___cxa_is_pointer_type"].apply(null, arguments) -}; - -var ___em_js__do_error = Module["___em_js__do_error"] = function() { - return Module["asm"]["___em_js__do_error"].apply(null, arguments) -}; - -var ___em_js__do_progress = Module["___em_js__do_progress"] = function() { - return Module["asm"]["___em_js__do_progress"].apply(null, arguments) -}; - -var ___em_js__get_file_contents = Module["___em_js__get_file_contents"] = function() { - return Module["asm"]["___em_js__get_file_contents"].apply(null, arguments) -}; - -var ___em_js__put_file_contents = Module["___em_js__put_file_contents"] = function() { - return Module["asm"]["___em_js__put_file_contents"].apply(null, arguments) -}; - -var ___em_js__put_file_size = Module["___em_js__put_file_size"] = function() { - return Module["asm"]["___em_js__put_file_size"].apply(null, arguments) -}; - -var _emscripten_replace_memory = Module["_emscripten_replace_memory"] = function() { - return Module["asm"]["_emscripten_replace_memory"].apply(null, arguments) -}; - -var _free = Module["_free"] = function() { - return Module["asm"]["_free"].apply(null, arguments) -}; - -var _llvm_bswap_i32 = Module["_llvm_bswap_i32"] = function() { - return Module["asm"]["_llvm_bswap_i32"].apply(null, arguments) -}; - -var _malloc = Module["_malloc"] = function() { - return Module["asm"]["_malloc"].apply(null, arguments) -}; - -var _memcpy = Module["_memcpy"] = function() { - return Module["asm"]["_memcpy"].apply(null, arguments) -}; - -var _memmove = Module["_memmove"] = function() { - return Module["asm"]["_memmove"].apply(null, arguments) -}; - -var _memset = Module["_memset"] = function() { - return Module["asm"]["_memset"].apply(null, arguments) -}; - -var _sbrk = Module["_sbrk"] = function() { - return Module["asm"]["_sbrk"].apply(null, arguments) -}; - -var _setThrew = Module["_setThrew"] = function() { - return Module["asm"]["_setThrew"].apply(null, arguments) -}; - -var establishStackSpace = Module["establishStackSpace"] = function() { - return Module["asm"]["establishStackSpace"].apply(null, arguments) -}; - -var stackAlloc = Module["stackAlloc"] = function() { - return Module["asm"]["stackAlloc"].apply(null, arguments) -}; - -var stackRestore = Module["stackRestore"] = function() { - return Module["asm"]["stackRestore"].apply(null, arguments) -}; - -var stackSave = Module["stackSave"] = function() { - return Module["asm"]["stackSave"].apply(null, arguments) -}; - -var dynCall_ii = Module["dynCall_ii"] = function() { - return Module["asm"]["dynCall_ii"].apply(null, arguments) -}; - -var dynCall_iidiiii = Module["dynCall_iidiiii"] = function() { - return Module["asm"]["dynCall_iidiiii"].apply(null, arguments) -}; - -var dynCall_iii = Module["dynCall_iii"] = function() { - return Module["asm"]["dynCall_iii"].apply(null, arguments) -}; - -var dynCall_iiii = Module["dynCall_iiii"] = function() { - return Module["asm"]["dynCall_iiii"].apply(null, arguments) -}; - -var dynCall_iiiii = Module["dynCall_iiiii"] = function() { - return Module["asm"]["dynCall_iiiii"].apply(null, arguments) -}; - -var dynCall_iiiiii = Module["dynCall_iiiiii"] = function() { - return Module["asm"]["dynCall_iiiiii"].apply(null, arguments) -}; - -var dynCall_iiiiiii = Module["dynCall_iiiiiii"] = function() { - return Module["asm"]["dynCall_iiiiiii"].apply(null, arguments) -}; - -var dynCall_ji = Module["dynCall_ji"] = function() { - return Module["asm"]["dynCall_ji"].apply(null, arguments) -}; - -var dynCall_jiji = Module["dynCall_jiji"] = function() { - return Module["asm"]["dynCall_jiji"].apply(null, arguments) -}; - -var dynCall_v = Module["dynCall_v"] = function() { - return Module["asm"]["dynCall_v"].apply(null, arguments) -}; - -var dynCall_vi = Module["dynCall_vi"] = function() { - return Module["asm"]["dynCall_vi"].apply(null, arguments) -}; - -var dynCall_vii = Module["dynCall_vii"] = function() { - return Module["asm"]["dynCall_vii"].apply(null, arguments) -}; - -var dynCall_viii = Module["dynCall_viii"] = function() { - return Module["asm"]["dynCall_viii"].apply(null, arguments) -}; - -var dynCall_viiii = Module["dynCall_viiii"] = function() { - return Module["asm"]["dynCall_viiii"].apply(null, arguments) -}; - -var dynCall_viiiii = Module["dynCall_viiiii"] = function() { - return Module["asm"]["dynCall_viiiii"].apply(null, arguments) -}; - -var dynCall_viiiiii = Module["dynCall_viiiiii"] = function() { - return Module["asm"]["dynCall_viiiiii"].apply(null, arguments) -}; - -var dynCall_viji = Module["dynCall_viji"] = function() { - return Module["asm"]["dynCall_viji"].apply(null, arguments) -}; +var _DApi_MpqCmp = Module["_DApi_MpqCmp"] = function() { return Module["asm"]["_DApi_MpqCmp"].apply(null, arguments) }; +var ___cxa_can_catch = Module["___cxa_can_catch"] = function() { return Module["asm"]["___cxa_can_catch"].apply(null, arguments) }; +var ___cxa_is_pointer_type = Module["___cxa_is_pointer_type"] = function() { return Module["asm"]["___cxa_is_pointer_type"].apply(null, arguments) }; +var ___em_js__do_error = Module["___em_js__do_error"] = function() { return Module["asm"]["___em_js__do_error"].apply(null, arguments) }; +var ___em_js__do_progress = Module["___em_js__do_progress"] = function() { return Module["asm"]["___em_js__do_progress"].apply(null, arguments) }; +var ___em_js__get_file_contents = Module["___em_js__get_file_contents"] = function() { return Module["asm"]["___em_js__get_file_contents"].apply(null, arguments) }; +var ___em_js__put_file_contents = Module["___em_js__put_file_contents"] = function() { return Module["asm"]["___em_js__put_file_contents"].apply(null, arguments) }; +var ___em_js__put_file_size = Module["___em_js__put_file_size"] = function() { return Module["asm"]["___em_js__put_file_size"].apply(null, arguments) }; +var _emscripten_replace_memory = Module["_emscripten_replace_memory"] = function() { return Module["asm"]["_emscripten_replace_memory"].apply(null, arguments) }; +var _free = Module["_free"] = function() { return Module["asm"]["_free"].apply(null, arguments) }; +var _llvm_bswap_i32 = Module["_llvm_bswap_i32"] = function() { return Module["asm"]["_llvm_bswap_i32"].apply(null, arguments) }; +var _malloc = Module["_malloc"] = function() { return Module["asm"]["_malloc"].apply(null, arguments) }; +var _memcpy = Module["_memcpy"] = function() { return Module["asm"]["_memcpy"].apply(null, arguments) }; +var _memset = Module["_memset"] = function() { return Module["asm"]["_memset"].apply(null, arguments) }; +var _sbrk = Module["_sbrk"] = function() { return Module["asm"]["_sbrk"].apply(null, arguments) }; +var establishStackSpace = Module["establishStackSpace"] = function() { return Module["asm"]["establishStackSpace"].apply(null, arguments) }; +var getTempRet0 = Module["getTempRet0"] = function() { return Module["asm"]["getTempRet0"].apply(null, arguments) }; +var runPostSets = Module["runPostSets"] = function() { return Module["asm"]["runPostSets"].apply(null, arguments) }; +var setTempRet0 = Module["setTempRet0"] = function() { return Module["asm"]["setTempRet0"].apply(null, arguments) }; +var setThrew = Module["setThrew"] = function() { return Module["asm"]["setThrew"].apply(null, arguments) }; +var stackAlloc = Module["stackAlloc"] = function() { return Module["asm"]["stackAlloc"].apply(null, arguments) }; +var stackRestore = Module["stackRestore"] = function() { return Module["asm"]["stackRestore"].apply(null, arguments) }; +var stackSave = Module["stackSave"] = function() { return Module["asm"]["stackSave"].apply(null, arguments) }; +var dynCall_ii = Module["dynCall_ii"] = function() { return Module["asm"]["dynCall_ii"].apply(null, arguments) }; +var dynCall_iii = Module["dynCall_iii"] = function() { return Module["asm"]["dynCall_iii"].apply(null, arguments) }; +var dynCall_iiii = Module["dynCall_iiii"] = function() { return Module["asm"]["dynCall_iiii"].apply(null, arguments) }; +var dynCall_iiiii = Module["dynCall_iiiii"] = function() { return Module["asm"]["dynCall_iiiii"].apply(null, arguments) }; +var dynCall_iiiiii = Module["dynCall_iiiiii"] = function() { return Module["asm"]["dynCall_iiiiii"].apply(null, arguments) }; +var dynCall_iiiiiii = Module["dynCall_iiiiiii"] = function() { return Module["asm"]["dynCall_iiiiiii"].apply(null, arguments) }; +var dynCall_ji = Module["dynCall_ji"] = function() { return Module["asm"]["dynCall_ji"].apply(null, arguments) }; +var dynCall_v = Module["dynCall_v"] = function() { return Module["asm"]["dynCall_v"].apply(null, arguments) }; +var dynCall_vi = Module["dynCall_vi"] = function() { return Module["asm"]["dynCall_vi"].apply(null, arguments) }; +var dynCall_vii = Module["dynCall_vii"] = function() { return Module["asm"]["dynCall_vii"].apply(null, arguments) }; +var dynCall_viii = Module["dynCall_viii"] = function() { return Module["asm"]["dynCall_viii"].apply(null, arguments) }; +var dynCall_viiii = Module["dynCall_viiii"] = function() { return Module["asm"]["dynCall_viiii"].apply(null, arguments) }; +var dynCall_viiiii = Module["dynCall_viiiii"] = function() { return Module["asm"]["dynCall_viiiii"].apply(null, arguments) }; +var dynCall_viiiiii = Module["dynCall_viiiiii"] = function() { return Module["asm"]["dynCall_viiiiii"].apply(null, arguments) }; +var dynCall_viji = Module["dynCall_viji"] = function() { return Module["asm"]["dynCall_viji"].apply(null, arguments) }; ; @@ -2576,17 +2264,18 @@ function ExitStatus(status) { this.name = "ExitStatus"; this.message = "Program terminated with exit(" + status + ")"; this.status = status; -} +}; ExitStatus.prototype = new Error(); ExitStatus.prototype.constructor = ExitStatus; +var initialStackTop; var calledMain = false; dependenciesFulfilled = function runCaller() { // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) if (!Module['calledRun']) run(); if (!Module['calledRun']) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled -}; +} @@ -2612,7 +2301,7 @@ function run(args) { if (ABORT) return; - initRuntime(); + ensureInitRuntime(); preMain(); @@ -2652,6 +2341,7 @@ function exit(status, implicit) { ABORT = true; EXITSTATUS = status; + STACKTOP = initialStackTop; exitRuntime(); @@ -2668,9 +2358,13 @@ function abort(what) { Module['onAbort'](what); } - what += ''; - out(what); - err(what); + if (what !== undefined) { + out(what); + err(what); + what = JSON.stringify(what) + } else { + what = ''; + } ABORT = true; EXITSTATUS = 1; @@ -2679,6 +2373,8 @@ function abort(what) { } Module['abort'] = abort; +// {{PRE_RUN_ADDITIONS}} + if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; while (Module['preInit'].length > 0) { @@ -2687,10 +2383,12 @@ if (Module['preInit']) { } - Module["noExitRuntime"] = true; +Module["noExitRuntime"] = true; run(); +// {{POST_RUN_ADDITIONS}} + @@ -2711,14 +2409,14 @@ Module['ready'] = new Promise(function (resolve, reject) { - return MpqCmp + return MpqCmp; } ); })(); if (typeof exports === 'object' && typeof module === 'object') - module.exports = MpqCmp; - else if (typeof define === 'function' && define['amd']) - define([], function() { return MpqCmp; }); - else if (typeof exports === 'object') - exports["MpqCmp"] = MpqCmp; - \ No newline at end of file + module.exports = MpqCmp; + else if (typeof define === 'function' && define['amd']) + define([], function() { return MpqCmp; }); + else if (typeof exports === 'object') + exports["MpqCmp"] = MpqCmp; + \ No newline at end of file diff --git a/src/mpqcmp/MpqCmp.wasm b/src/mpqcmp/MpqCmp.wasm index 2e07d00..a1b8386 100644 Binary files a/src/mpqcmp/MpqCmp.wasm and b/src/mpqcmp/MpqCmp.wasm differ diff --git a/src/mpqcmp/compress.js b/src/mpqcmp/compress.js new file mode 100644 index 0000000..9f602a6 --- /dev/null +++ b/src/mpqcmp/compress.js @@ -0,0 +1,27 @@ +import Worker from './mpqcmp.worker.js'; + +export default function compress(mpq, progress) { + progress("Loading..."); + return new Promise((resolve, reject) => { + try { + const worker = new Worker(); + worker.addEventListener("message", ({data}) => { + switch (data.action) { + case "result": + resolve(data.result); + break; + case "error": + reject({message: data.error, stack: data.stack}); + break; + case "progress": + progress(data.text, data.loaded, data.total); + break; + default: + } + }); + worker.postMessage({action: "run", mpq}); + } catch (e) { + reject(e); + } + }); +} diff --git a/src/mpqcmp/index.js b/src/mpqcmp/index.js new file mode 100644 index 0000000..bce78d8 --- /dev/null +++ b/src/mpqcmp/index.js @@ -0,0 +1,84 @@ +import React from 'react'; +import compress from './compress'; + +export default class CompressMpq extends React.Component { + state = {}; + + parseFile = e => { + const files = e.target.files; + if (files.length > 0) { + this.start(files[0]); + } + } + + onProgress(progress) { + this.setState({progress}); + } + onDone = result => { + const blob = new Blob([result], {type: 'binary/octet-stream'}); + const url = URL.createObjectURL(blob); + this.setState({url}); + + const lnk = document.createElement('a'); + lnk.setAttribute('href', url); + lnk.setAttribute('download', 'DIABDAT.MPQ'); + document.body.appendChild(lnk); + lnk.click(); + document.body.removeChild(lnk); + } + onError(message, stack) { + const { api } = this.props; + api.setState({compress: false}); + api.onError(message, stack); + } + + onClose = () => { + if (this.state.url) { + URL.revokeObjectURL(this.state.url); + } + this.props.api.setState({compress: false}); + } + + start(file) { + this.setState({started: true}); + compress(file, (text, loaded, total) => this.onProgress({text, loaded, total})) + .then(this.onDone, e => this.onError(e.message, e.stack)); + } + + render() { + const { url, started, progress } = this.state; + if (url) { + return ( +
+

+ Click here if download doesn't start. +

+
Back
+
+ ); + } + if (started) { + return ( +
+ {(progress && progress.text) || 'Processing...'} + {progress != null && !!progress.total && ( + + )} +
+ ); + } + return ( +
+

+ You can use this tool to reduce the original MPQ to about half its size. It encodes sounds in MP3 format and uses better compression for regular files. + To begin, click the button below or drop the MPQ onto the page. +

+
+ + +
+
Back
+
+ ); + } +} diff --git a/src/mpqcmp/mpqcmp.worker.js b/src/mpqcmp/mpqcmp.worker.js index 2ffdbb9..6852f74 100644 --- a/src/mpqcmp/mpqcmp.worker.js +++ b/src/mpqcmp/mpqcmp.worker.js @@ -7,14 +7,6 @@ const MpqSize = 356747; /* eslint-disable-next-line no-restricted-globals */ const worker = self; -function onError(err, action="error") { - if (err instanceof Error) { - worker.postMessage({action, error: err.toString(), stack: err.stack}); - } else { - worker.postMessage({action, error: err.toString()}); - } -} - let input_file = null; let output_file = null; let last_progress = 0; @@ -31,6 +23,7 @@ const DApi = { array.set(input_file.subarray(offset, offset + array.byteLength)); }, put_file_size(size) { + debugger; output_file = new Uint8Array(size); }, put_file_contents(array, offset) { @@ -65,25 +58,27 @@ const readFile = (file, progress) => new Promise((resolve, reject) => { reader.readAsArrayBuffer(file); }); -async function initWasm(spawn, progress) { +async function initWasm(progress) { const binary = await axios.request({ - url: spawn ? SpawnBinary : DiabloBinary, + url: MpqBinary, responseType: 'arraybuffer', onDownloadProgress: progress, }); - const result = await (spawn ? SpawnModule : DiabloModule)({wasmBinary: binary.data}).ready; - progress({loaded: 2000000}); + const result = await MpqModule({ + wasmBinary: binary.data, + }).ready; + progress({loaded: MpqSize}); return result; } async function run(mpq) { progress("Loading..."); - let mpqLoaded = 0, mpqTotal = (mpq ? mpq.size : 0), wasmLoaded = 0, wasmTotal = (spawn ? SpawnSize : DiabloSize); + let mpqLoaded = 0, mpqTotal = (mpq ? mpq.size : 0), wasmLoaded = 0, wasmTotal = MpqSize; const wasmWeight = 5; function updateProgress() { progress("Loading...", mpqLoaded + wasmLoaded * wasmWeight, mpqTotal + wasmTotal * wasmWeight); } - const loadWasm = initWasm(spawn, e => { + const loadWasm = initWasm(e => { wasmLoaded = Math.min(e.loaded, wasmTotal); updateProgress(); }); @@ -95,7 +90,7 @@ async function run(mpq) { input_file = new Uint8Array(mpq); - progress("Initializing..."); + progress("Processing..."); wasm._DApi_MpqCmp(input_file.length); @@ -105,10 +100,10 @@ async function run(mpq) { worker.addEventListener("message", ({data}) => { switch (data.action) { case "run": - init_game(data.mpq).then( - res => worker.postMessage({action: "result", data: res}, [res]), - e => onError(e, "failed")); + run(data.mpq).then( + result => worker.postMessage({action: "result", result}, [result]), + err => worker.postMessage({action: "error", error: err.toString(), stack: err.stack})); break; default: } -}); +}); \ No newline at end of file