/******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./node_modules/neverthrow/dist/index.es.js": /*!**************************************************!*\ !*** ./node_modules/neverthrow/dist/index.es.js ***! \**************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ err: () => (/* binding */ err), /* harmony export */ ok: () => (/* binding */ ok) /* harmony export */ }); /* unused harmony exports Err, Ok, Result, ResultAsync, errAsync, fromAsyncThrowable, fromPromise, fromSafePromise, fromThrowable, okAsync, safeTry */ const defaultErrorConfig = { withStackTrace: false, }; // Custom error object // Context / discussion: https://github.com/supermacro/neverthrow/pull/215 const createNeverThrowError = (message, result, config = defaultErrorConfig) => { const data = result.isOk() ? { type: 'Ok', value: result.value } : { type: 'Err', value: result.error }; const maybeStack = config.withStackTrace ? new Error().stack : undefined; return { data, message, stack: maybeStack, }; }; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncDelegator(o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } } class ResultAsync { constructor(res) { this._promise = res; } static fromSafePromise(promise) { const newPromise = promise.then((value) => new Ok(value)); return new ResultAsync(newPromise); } static fromPromise(promise, errorFn) { const newPromise = promise .then((value) => new Ok(value)) .catch((e) => new Err(errorFn(e))); return new ResultAsync(newPromise); } // eslint-disable-next-line @typescript-eslint/no-explicit-any static fromThrowable(fn, errorFn) { return (...args) => { return new ResultAsync((() => __awaiter(this, void 0, void 0, function* () { try { return new Ok(yield fn(...args)); } catch (error) { return new Err(errorFn ? errorFn(error) : error); } }))()); }; } static combine(asyncResultList) { return combineResultAsyncList(asyncResultList); } static combineWithAllErrors(asyncResultList) { return combineResultAsyncListWithAllErrors(asyncResultList); } map(f) { return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () { if (res.isErr()) { return new Err(res.error); } return new Ok(yield f(res.value)); }))); } mapErr(f) { return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () { if (res.isOk()) { return new Ok(res.value); } return new Err(yield f(res.error)); }))); } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types andThen(f) { return new ResultAsync(this._promise.then((res) => { if (res.isErr()) { return new Err(res.error); } const newValue = f(res.value); return newValue instanceof ResultAsync ? newValue._promise : newValue; })); } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types orElse(f) { return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () { if (res.isErr()) { return f(res.error); } return new Ok(res.value); }))); } match(ok, _err) { return this._promise.then((res) => res.match(ok, _err)); } unwrapOr(t) { return this._promise.then((res) => res.unwrapOr(t)); } /** * Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`. */ safeUnwrap() { return __asyncGenerator(this, arguments, function* safeUnwrap_1() { return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap())))))); }); } // Makes ResultAsync implement PromiseLike then(successCallback, failureCallback) { return this._promise.then(successCallback, failureCallback); } } const okAsync = (value) => new ResultAsync(Promise.resolve(new Ok(value))); const errAsync = (err) => new ResultAsync(Promise.resolve(new Err(err))); const fromPromise = ResultAsync.fromPromise; const fromSafePromise = ResultAsync.fromSafePromise; const fromAsyncThrowable = ResultAsync.fromThrowable; /** * Short circuits on the FIRST Err value that we find */ const combineResultList = (resultList) => { let acc = ok([]); for (const result of resultList) { if (result.isErr()) { acc = err(result.error); break; } else { acc.map((list) => list.push(result.value)); } } return acc; }; /* This is the typesafe version of Promise.all * * Takes a list of ResultAsync and success if all inner results are Ok values * or fails if one (or more) of the inner results are Err values */ const combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList); /** * Give a list of all the errors we find */ const combineResultListWithAllErrors = (resultList) => { let acc = ok([]); for (const result of resultList) { if (result.isErr() && acc.isErr()) { acc.error.push(result.error); } else if (result.isErr() && acc.isOk()) { acc = err([result.error]); } else if (result.isOk() && acc.isOk()) { acc.value.push(result.value); } // do nothing when result.isOk() && acc.isErr() } return acc; }; const combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors); // eslint-disable-next-line @typescript-eslint/no-namespace var Result; (function (Result) { /** * Wraps a function with a try catch, creating a new function with the same * arguments but returning `Ok` if successful, `Err` if the function throws * * @param fn function to wrap with ok on success or err on failure * @param errorFn when an error is thrown, this will wrap the error result if provided */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function fromThrowable(fn, errorFn) { return (...args) => { try { const result = fn(...args); return ok(result); } catch (e) { return err(errorFn ? errorFn(e) : e); } }; } Result.fromThrowable = fromThrowable; function combine(resultList) { return combineResultList(resultList); } Result.combine = combine; function combineWithAllErrors(resultList) { return combineResultListWithAllErrors(resultList); } Result.combineWithAllErrors = combineWithAllErrors; })(Result || (Result = {})); const ok = (value) => new Ok(value); const err = (err) => new Err(err); function safeTry(body) { const n = body().next(); if (n instanceof Promise) { return n.then((r) => r.value); } return n.value; } class Ok { constructor(value) { this.value = value; } isOk() { return true; } isErr() { return !this.isOk(); } map(f) { return ok(f(this.value)); } // eslint-disable-next-line @typescript-eslint/no-unused-vars mapErr(_f) { return ok(this.value); } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types andThen(f) { return f(this.value); } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types orElse(_f) { return ok(this.value); } asyncAndThen(f) { return f(this.value); } asyncMap(f) { return ResultAsync.fromSafePromise(f(this.value)); } // eslint-disable-next-line @typescript-eslint/no-unused-vars unwrapOr(_v) { return this.value; } // eslint-disable-next-line @typescript-eslint/no-unused-vars match(ok, _err) { return ok(this.value); } safeUnwrap() { const value = this.value; /* eslint-disable-next-line require-yield */ return (function* () { return value; })(); } _unsafeUnwrap(_) { return this.value; } _unsafeUnwrapErr(config) { throw createNeverThrowError('Called `_unsafeUnwrapErr` on an Ok', this, config); } } class Err { constructor(error) { this.error = error; } isOk() { return false; } isErr() { return !this.isOk(); } // eslint-disable-next-line @typescript-eslint/no-unused-vars map(_f) { return err(this.error); } mapErr(f) { return err(f(this.error)); } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types andThen(_f) { return err(this.error); } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types orElse(f) { return f(this.error); } // eslint-disable-next-line @typescript-eslint/no-unused-vars asyncAndThen(_f) { return errAsync(this.error); } // eslint-disable-next-line @typescript-eslint/no-unused-vars asyncMap(_f) { return errAsync(this.error); } unwrapOr(v) { return v; } match(_ok, err) { return err(this.error); } safeUnwrap() { const error = this.error; return (function* () { yield err(error); throw new Error('Do not use this generator out of `safeTry`'); })(); } _unsafeUnwrap(config) { throw createNeverThrowError('Called `_unsafeUnwrap` on an Err', this, config); } _unsafeUnwrapErr(_) { return this.error; } } const fromThrowable = Result.fromThrowable; //#endregion /***/ }), /***/ "./src/errors/index.ts": /*!*****************************!*\ !*** ./src/errors/index.ts ***! \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ MetadataParsingError: () => (/* binding */ MetadataParsingError), /* harmony export */ UnresolvedMetadataError: () => (/* binding */ UnresolvedMetadataError) /* harmony export */ }); /* unused harmony export Il2CppContextCreationError */ class CustomError extends Error { constructor(message) { super(message); this.message = message; Object.setPrototypeOf(this, new.target.prototype); } print() { return this.name + ": " + this.message; } } class UnresolvedMetadataError extends CustomError { constructor() { super(...arguments); this.name = "UnresolvedMetadataError"; } } class MetadataParsingError extends CustomError { constructor() { super(...arguments); this.name = "MetadataParsingError"; } } class Il2CppContextCreationError extends CustomError { constructor() { super(...arguments); this.name = "Il2CppContextCreationError"; } } /***/ }), /***/ "./src/extras/index.ts": /*!*****************************!*\ !*** ./src/extras/index.ts ***! \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ KeyCode: () => (/* binding */ KeyCode), /* harmony export */ dataTypeSizes: () => (/* binding */ dataTypeSizes) /* harmony export */ }); const dataTypeSizes = { u8: 1, u32: 4, i32: 4, f32: 4, }; // TODO: Resolve and create these kinds of enums dynamically (and remove hardcoding) var KeyCode; (function (KeyCode) { KeyCode[KeyCode["None"] = 0] = "None"; KeyCode[KeyCode["Backspace"] = 8] = "Backspace"; KeyCode[KeyCode["Tab"] = 9] = "Tab"; KeyCode[KeyCode["Clear"] = 12] = "Clear"; KeyCode[KeyCode["Return"] = 13] = "Return"; KeyCode[KeyCode["Pause"] = 19] = "Pause"; KeyCode[KeyCode["Escape"] = 27] = "Escape"; KeyCode[KeyCode["Space"] = 32] = "Space"; KeyCode[KeyCode["Exclaim"] = 33] = "Exclaim"; KeyCode[KeyCode["DoubleQuote"] = 34] = "DoubleQuote"; KeyCode[KeyCode["Hash"] = 35] = "Hash"; KeyCode[KeyCode["Dollar"] = 36] = "Dollar"; KeyCode[KeyCode["Ampersand"] = 38] = "Ampersand"; KeyCode[KeyCode["Quote"] = 39] = "Quote"; KeyCode[KeyCode["LeftParen"] = 40] = "LeftParen"; KeyCode[KeyCode["RightParen"] = 41] = "RightParen"; KeyCode[KeyCode["Asterisk"] = 42] = "Asterisk"; KeyCode[KeyCode["Plus"] = 43] = "Plus"; KeyCode[KeyCode["Comma"] = 44] = "Comma"; KeyCode[KeyCode["Minus"] = 45] = "Minus"; KeyCode[KeyCode["Period"] = 46] = "Period"; KeyCode[KeyCode["Slash"] = 47] = "Slash"; KeyCode[KeyCode["Alpha0"] = 48] = "Alpha0"; KeyCode[KeyCode["Alpha1"] = 49] = "Alpha1"; KeyCode[KeyCode["Alpha2"] = 50] = "Alpha2"; KeyCode[KeyCode["Alpha3"] = 51] = "Alpha3"; KeyCode[KeyCode["Alpha4"] = 52] = "Alpha4"; KeyCode[KeyCode["Alpha5"] = 53] = "Alpha5"; KeyCode[KeyCode["Alpha6"] = 54] = "Alpha6"; KeyCode[KeyCode["Alpha7"] = 55] = "Alpha7"; KeyCode[KeyCode["Alpha8"] = 56] = "Alpha8"; KeyCode[KeyCode["Alpha9"] = 57] = "Alpha9"; KeyCode[KeyCode["Colon"] = 58] = "Colon"; KeyCode[KeyCode["Semicolon"] = 59] = "Semicolon"; KeyCode[KeyCode["Less"] = 60] = "Less"; KeyCode[KeyCode["Equals"] = 61] = "Equals"; KeyCode[KeyCode["Greater"] = 62] = "Greater"; KeyCode[KeyCode["Question"] = 63] = "Question"; KeyCode[KeyCode["At"] = 64] = "At"; KeyCode[KeyCode["LeftBracket"] = 91] = "LeftBracket"; KeyCode[KeyCode["Backslash"] = 92] = "Backslash"; KeyCode[KeyCode["RightBracket"] = 93] = "RightBracket"; KeyCode[KeyCode["Caret"] = 94] = "Caret"; KeyCode[KeyCode["Underscore"] = 95] = "Underscore"; KeyCode[KeyCode["BackQuote"] = 96] = "BackQuote"; KeyCode[KeyCode["A"] = 97] = "A"; KeyCode[KeyCode["B"] = 98] = "B"; KeyCode[KeyCode["C"] = 99] = "C"; KeyCode[KeyCode["D"] = 100] = "D"; KeyCode[KeyCode["E"] = 101] = "E"; KeyCode[KeyCode["F"] = 102] = "F"; KeyCode[KeyCode["G"] = 103] = "G"; KeyCode[KeyCode["H"] = 104] = "H"; KeyCode[KeyCode["I"] = 105] = "I"; KeyCode[KeyCode["J"] = 106] = "J"; KeyCode[KeyCode["K"] = 107] = "K"; KeyCode[KeyCode["L"] = 108] = "L"; KeyCode[KeyCode["M"] = 109] = "M"; KeyCode[KeyCode["N"] = 110] = "N"; KeyCode[KeyCode["O"] = 111] = "O"; KeyCode[KeyCode["P"] = 112] = "P"; KeyCode[KeyCode["Q"] = 113] = "Q"; KeyCode[KeyCode["R"] = 114] = "R"; KeyCode[KeyCode["S"] = 115] = "S"; KeyCode[KeyCode["T"] = 116] = "T"; KeyCode[KeyCode["U"] = 117] = "U"; KeyCode[KeyCode["V"] = 118] = "V"; KeyCode[KeyCode["W"] = 119] = "W"; KeyCode[KeyCode["X"] = 120] = "X"; KeyCode[KeyCode["Y"] = 121] = "Y"; KeyCode[KeyCode["Z"] = 122] = "Z"; KeyCode[KeyCode["Delete"] = 127] = "Delete"; KeyCode[KeyCode["Keypad0"] = 256] = "Keypad0"; KeyCode[KeyCode["Keypad1"] = 257] = "Keypad1"; KeyCode[KeyCode["Keypad2"] = 258] = "Keypad2"; KeyCode[KeyCode["Keypad3"] = 259] = "Keypad3"; KeyCode[KeyCode["Keypad4"] = 260] = "Keypad4"; KeyCode[KeyCode["Keypad5"] = 261] = "Keypad5"; KeyCode[KeyCode["Keypad6"] = 262] = "Keypad6"; KeyCode[KeyCode["Keypad7"] = 263] = "Keypad7"; KeyCode[KeyCode["Keypad8"] = 264] = "Keypad8"; KeyCode[KeyCode["Keypad9"] = 265] = "Keypad9"; KeyCode[KeyCode["KeypadPeriod"] = 266] = "KeypadPeriod"; KeyCode[KeyCode["KeypadDivide"] = 267] = "KeypadDivide"; KeyCode[KeyCode["KeypadMultiply"] = 268] = "KeypadMultiply"; KeyCode[KeyCode["KeypadMinus"] = 269] = "KeypadMinus"; KeyCode[KeyCode["KeypadPlus"] = 270] = "KeypadPlus"; KeyCode[KeyCode["KeypadEnter"] = 271] = "KeypadEnter"; KeyCode[KeyCode["KeypadEquals"] = 272] = "KeypadEquals"; KeyCode[KeyCode["UpArrow"] = 273] = "UpArrow"; KeyCode[KeyCode["DownArrow"] = 274] = "DownArrow"; KeyCode[KeyCode["RightArrow"] = 275] = "RightArrow"; KeyCode[KeyCode["LeftArrow"] = 276] = "LeftArrow"; KeyCode[KeyCode["Insert"] = 277] = "Insert"; KeyCode[KeyCode["Home"] = 278] = "Home"; KeyCode[KeyCode["End"] = 279] = "End"; KeyCode[KeyCode["PageUp"] = 280] = "PageUp"; KeyCode[KeyCode["PageDown"] = 281] = "PageDown"; KeyCode[KeyCode["F1"] = 282] = "F1"; KeyCode[KeyCode["F2"] = 283] = "F2"; KeyCode[KeyCode["F3"] = 284] = "F3"; KeyCode[KeyCode["F4"] = 285] = "F4"; KeyCode[KeyCode["F5"] = 286] = "F5"; KeyCode[KeyCode["F6"] = 287] = "F6"; KeyCode[KeyCode["F7"] = 288] = "F7"; KeyCode[KeyCode["F8"] = 289] = "F8"; KeyCode[KeyCode["F9"] = 290] = "F9"; KeyCode[KeyCode["F10"] = 291] = "F10"; KeyCode[KeyCode["F11"] = 292] = "F11"; KeyCode[KeyCode["F12"] = 293] = "F12"; KeyCode[KeyCode["F13"] = 294] = "F13"; KeyCode[KeyCode["F14"] = 295] = "F14"; KeyCode[KeyCode["F15"] = 296] = "F15"; KeyCode[KeyCode["Numlock"] = 300] = "Numlock"; KeyCode[KeyCode["CapsLock"] = 301] = "CapsLock"; KeyCode[KeyCode["ScrollLock"] = 302] = "ScrollLock"; KeyCode[KeyCode["RightShift"] = 303] = "RightShift"; KeyCode[KeyCode["LeftShift"] = 304] = "LeftShift"; KeyCode[KeyCode["RightControl"] = 305] = "RightControl"; KeyCode[KeyCode["LeftControl"] = 306] = "LeftControl"; KeyCode[KeyCode["RightAlt"] = 307] = "RightAlt"; KeyCode[KeyCode["LeftAlt"] = 308] = "LeftAlt"; KeyCode[KeyCode["RightApple"] = 309] = "RightApple"; KeyCode[KeyCode["RightCommand"] = 309] = "RightCommand"; KeyCode[KeyCode["LeftApple"] = 310] = "LeftApple"; KeyCode[KeyCode["LeftCommand"] = 310] = "LeftCommand"; KeyCode[KeyCode["LeftWindows"] = 311] = "LeftWindows"; KeyCode[KeyCode["RightWindows"] = 312] = "RightWindows"; KeyCode[KeyCode["AltGr"] = 313] = "AltGr"; KeyCode[KeyCode["Help"] = 315] = "Help"; KeyCode[KeyCode["Print"] = 316] = "Print"; KeyCode[KeyCode["SysReq"] = 317] = "SysReq"; KeyCode[KeyCode["Break"] = 318] = "Break"; KeyCode[KeyCode["Menu"] = 319] = "Menu"; KeyCode[KeyCode["Mouse0"] = 323] = "Mouse0"; KeyCode[KeyCode["Mouse1"] = 324] = "Mouse1"; KeyCode[KeyCode["Mouse2"] = 325] = "Mouse2"; KeyCode[KeyCode["Mouse3"] = 326] = "Mouse3"; KeyCode[KeyCode["Mouse4"] = 327] = "Mouse4"; KeyCode[KeyCode["Mouse5"] = 328] = "Mouse5"; KeyCode[KeyCode["Mouse6"] = 329] = "Mouse6"; KeyCode[KeyCode["JoystickButton0"] = 330] = "JoystickButton0"; KeyCode[KeyCode["JoystickButton1"] = 331] = "JoystickButton1"; KeyCode[KeyCode["JoystickButton2"] = 332] = "JoystickButton2"; KeyCode[KeyCode["JoystickButton3"] = 333] = "JoystickButton3"; KeyCode[KeyCode["JoystickButton4"] = 334] = "JoystickButton4"; KeyCode[KeyCode["JoystickButton5"] = 335] = "JoystickButton5"; KeyCode[KeyCode["JoystickButton6"] = 336] = "JoystickButton6"; KeyCode[KeyCode["JoystickButton7"] = 337] = "JoystickButton7"; KeyCode[KeyCode["JoystickButton8"] = 338] = "JoystickButton8"; KeyCode[KeyCode["JoystickButton9"] = 339] = "JoystickButton9"; KeyCode[KeyCode["JoystickButton10"] = 340] = "JoystickButton10"; KeyCode[KeyCode["JoystickButton11"] = 341] = "JoystickButton11"; KeyCode[KeyCode["JoystickButton12"] = 342] = "JoystickButton12"; KeyCode[KeyCode["JoystickButton13"] = 343] = "JoystickButton13"; KeyCode[KeyCode["JoystickButton14"] = 344] = "JoystickButton14"; KeyCode[KeyCode["JoystickButton15"] = 345] = "JoystickButton15"; KeyCode[KeyCode["JoystickButton16"] = 346] = "JoystickButton16"; KeyCode[KeyCode["JoystickButton17"] = 347] = "JoystickButton17"; KeyCode[KeyCode["JoystickButton18"] = 348] = "JoystickButton18"; KeyCode[KeyCode["JoystickButton19"] = 349] = "JoystickButton19"; KeyCode[KeyCode["Joystick1Button0"] = 350] = "Joystick1Button0"; KeyCode[KeyCode["Joystick1Button1"] = 351] = "Joystick1Button1"; KeyCode[KeyCode["Joystick1Button2"] = 352] = "Joystick1Button2"; KeyCode[KeyCode["Joystick1Button3"] = 353] = "Joystick1Button3"; KeyCode[KeyCode["Joystick1Button4"] = 354] = "Joystick1Button4"; KeyCode[KeyCode["Joystick1Button5"] = 355] = "Joystick1Button5"; KeyCode[KeyCode["Joystick1Button6"] = 356] = "Joystick1Button6"; KeyCode[KeyCode["Joystick1Button7"] = 357] = "Joystick1Button7"; KeyCode[KeyCode["Joystick1Button8"] = 358] = "Joystick1Button8"; KeyCode[KeyCode["Joystick1Button9"] = 359] = "Joystick1Button9"; KeyCode[KeyCode["Joystick1Button10"] = 360] = "Joystick1Button10"; KeyCode[KeyCode["Joystick1Button11"] = 361] = "Joystick1Button11"; KeyCode[KeyCode["Joystick1Button12"] = 362] = "Joystick1Button12"; KeyCode[KeyCode["Joystick1Button13"] = 363] = "Joystick1Button13"; KeyCode[KeyCode["Joystick1Button14"] = 364] = "Joystick1Button14"; KeyCode[KeyCode["Joystick1Button15"] = 365] = "Joystick1Button15"; KeyCode[KeyCode["Joystick1Button16"] = 366] = "Joystick1Button16"; KeyCode[KeyCode["Joystick1Button17"] = 367] = "Joystick1Button17"; KeyCode[KeyCode["Joystick1Button18"] = 368] = "Joystick1Button18"; KeyCode[KeyCode["Joystick1Button19"] = 369] = "Joystick1Button19"; KeyCode[KeyCode["Joystick2Button0"] = 370] = "Joystick2Button0"; KeyCode[KeyCode["Joystick2Button1"] = 371] = "Joystick2Button1"; KeyCode[KeyCode["Joystick2Button2"] = 372] = "Joystick2Button2"; KeyCode[KeyCode["Joystick2Button3"] = 373] = "Joystick2Button3"; KeyCode[KeyCode["Joystick2Button4"] = 374] = "Joystick2Button4"; KeyCode[KeyCode["Joystick2Button5"] = 375] = "Joystick2Button5"; KeyCode[KeyCode["Joystick2Button6"] = 376] = "Joystick2Button6"; KeyCode[KeyCode["Joystick2Button7"] = 377] = "Joystick2Button7"; KeyCode[KeyCode["Joystick2Button8"] = 378] = "Joystick2Button8"; KeyCode[KeyCode["Joystick2Button9"] = 379] = "Joystick2Button9"; KeyCode[KeyCode["Joystick2Button10"] = 380] = "Joystick2Button10"; KeyCode[KeyCode["Joystick2Button11"] = 381] = "Joystick2Button11"; KeyCode[KeyCode["Joystick2Button12"] = 382] = "Joystick2Button12"; KeyCode[KeyCode["Joystick2Button13"] = 383] = "Joystick2Button13"; KeyCode[KeyCode["Joystick2Button14"] = 384] = "Joystick2Button14"; KeyCode[KeyCode["Joystick2Button15"] = 385] = "Joystick2Button15"; KeyCode[KeyCode["Joystick2Button16"] = 386] = "Joystick2Button16"; KeyCode[KeyCode["Joystick2Button17"] = 387] = "Joystick2Button17"; KeyCode[KeyCode["Joystick2Button18"] = 388] = "Joystick2Button18"; KeyCode[KeyCode["Joystick2Button19"] = 389] = "Joystick2Button19"; KeyCode[KeyCode["Joystick3Button0"] = 390] = "Joystick3Button0"; KeyCode[KeyCode["Joystick3Button1"] = 391] = "Joystick3Button1"; KeyCode[KeyCode["Joystick3Button2"] = 392] = "Joystick3Button2"; KeyCode[KeyCode["Joystick3Button3"] = 393] = "Joystick3Button3"; KeyCode[KeyCode["Joystick3Button4"] = 394] = "Joystick3Button4"; KeyCode[KeyCode["Joystick3Button5"] = 395] = "Joystick3Button5"; KeyCode[KeyCode["Joystick3Button6"] = 396] = "Joystick3Button6"; KeyCode[KeyCode["Joystick3Button7"] = 397] = "Joystick3Button7"; KeyCode[KeyCode["Joystick3Button8"] = 398] = "Joystick3Button8"; KeyCode[KeyCode["Joystick3Button9"] = 399] = "Joystick3Button9"; KeyCode[KeyCode["Joystick3Button10"] = 400] = "Joystick3Button10"; KeyCode[KeyCode["Joystick3Button11"] = 401] = "Joystick3Button11"; KeyCode[KeyCode["Joystick3Button12"] = 402] = "Joystick3Button12"; KeyCode[KeyCode["Joystick3Button13"] = 403] = "Joystick3Button13"; KeyCode[KeyCode["Joystick3Button14"] = 404] = "Joystick3Button14"; KeyCode[KeyCode["Joystick3Button15"] = 405] = "Joystick3Button15"; KeyCode[KeyCode["Joystick3Button16"] = 406] = "Joystick3Button16"; KeyCode[KeyCode["Joystick3Button17"] = 407] = "Joystick3Button17"; KeyCode[KeyCode["Joystick3Button18"] = 408] = "Joystick3Button18"; KeyCode[KeyCode["Joystick3Button19"] = 409] = "Joystick3Button19"; KeyCode[KeyCode["Joystick4Button0"] = 410] = "Joystick4Button0"; KeyCode[KeyCode["Joystick4Button1"] = 411] = "Joystick4Button1"; KeyCode[KeyCode["Joystick4Button2"] = 412] = "Joystick4Button2"; KeyCode[KeyCode["Joystick4Button3"] = 413] = "Joystick4Button3"; KeyCode[KeyCode["Joystick4Button4"] = 414] = "Joystick4Button4"; KeyCode[KeyCode["Joystick4Button5"] = 415] = "Joystick4Button5"; KeyCode[KeyCode["Joystick4Button6"] = 416] = "Joystick4Button6"; KeyCode[KeyCode["Joystick4Button7"] = 417] = "Joystick4Button7"; KeyCode[KeyCode["Joystick4Button8"] = 418] = "Joystick4Button8"; KeyCode[KeyCode["Joystick4Button9"] = 419] = "Joystick4Button9"; KeyCode[KeyCode["Joystick4Button10"] = 420] = "Joystick4Button10"; KeyCode[KeyCode["Joystick4Button11"] = 421] = "Joystick4Button11"; KeyCode[KeyCode["Joystick4Button12"] = 422] = "Joystick4Button12"; KeyCode[KeyCode["Joystick4Button13"] = 423] = "Joystick4Button13"; KeyCode[KeyCode["Joystick4Button14"] = 424] = "Joystick4Button14"; KeyCode[KeyCode["Joystick4Button15"] = 425] = "Joystick4Button15"; KeyCode[KeyCode["Joystick4Button16"] = 426] = "Joystick4Button16"; KeyCode[KeyCode["Joystick4Button17"] = 427] = "Joystick4Button17"; KeyCode[KeyCode["Joystick4Button18"] = 428] = "Joystick4Button18"; KeyCode[KeyCode["Joystick4Button19"] = 429] = "Joystick4Button19"; KeyCode[KeyCode["Joystick5Button0"] = 430] = "Joystick5Button0"; KeyCode[KeyCode["Joystick5Button1"] = 431] = "Joystick5Button1"; KeyCode[KeyCode["Joystick5Button2"] = 432] = "Joystick5Button2"; KeyCode[KeyCode["Joystick5Button3"] = 433] = "Joystick5Button3"; KeyCode[KeyCode["Joystick5Button4"] = 434] = "Joystick5Button4"; KeyCode[KeyCode["Joystick5Button5"] = 435] = "Joystick5Button5"; KeyCode[KeyCode["Joystick5Button6"] = 436] = "Joystick5Button6"; KeyCode[KeyCode["Joystick5Button7"] = 437] = "Joystick5Button7"; KeyCode[KeyCode["Joystick5Button8"] = 438] = "Joystick5Button8"; KeyCode[KeyCode["Joystick5Button9"] = 439] = "Joystick5Button9"; KeyCode[KeyCode["Joystick5Button10"] = 440] = "Joystick5Button10"; KeyCode[KeyCode["Joystick5Button11"] = 441] = "Joystick5Button11"; KeyCode[KeyCode["Joystick5Button12"] = 442] = "Joystick5Button12"; KeyCode[KeyCode["Joystick5Button13"] = 443] = "Joystick5Button13"; KeyCode[KeyCode["Joystick5Button14"] = 444] = "Joystick5Button14"; KeyCode[KeyCode["Joystick5Button15"] = 445] = "Joystick5Button15"; KeyCode[KeyCode["Joystick5Button16"] = 446] = "Joystick5Button16"; KeyCode[KeyCode["Joystick5Button17"] = 447] = "Joystick5Button17"; KeyCode[KeyCode["Joystick5Button18"] = 448] = "Joystick5Button18"; KeyCode[KeyCode["Joystick5Button19"] = 449] = "Joystick5Button19"; KeyCode[KeyCode["Joystick6Button0"] = 450] = "Joystick6Button0"; KeyCode[KeyCode["Joystick6Button1"] = 451] = "Joystick6Button1"; KeyCode[KeyCode["Joystick6Button2"] = 452] = "Joystick6Button2"; KeyCode[KeyCode["Joystick6Button3"] = 453] = "Joystick6Button3"; KeyCode[KeyCode["Joystick6Button4"] = 454] = "Joystick6Button4"; KeyCode[KeyCode["Joystick6Button5"] = 455] = "Joystick6Button5"; KeyCode[KeyCode["Joystick6Button6"] = 456] = "Joystick6Button6"; KeyCode[KeyCode["Joystick6Button7"] = 457] = "Joystick6Button7"; KeyCode[KeyCode["Joystick6Button8"] = 458] = "Joystick6Button8"; KeyCode[KeyCode["Joystick6Button9"] = 459] = "Joystick6Button9"; KeyCode[KeyCode["Joystick6Button10"] = 460] = "Joystick6Button10"; KeyCode[KeyCode["Joystick6Button11"] = 461] = "Joystick6Button11"; KeyCode[KeyCode["Joystick6Button12"] = 462] = "Joystick6Button12"; KeyCode[KeyCode["Joystick6Button13"] = 463] = "Joystick6Button13"; KeyCode[KeyCode["Joystick6Button14"] = 464] = "Joystick6Button14"; KeyCode[KeyCode["Joystick6Button15"] = 465] = "Joystick6Button15"; KeyCode[KeyCode["Joystick6Button16"] = 466] = "Joystick6Button16"; KeyCode[KeyCode["Joystick6Button17"] = 467] = "Joystick6Button17"; KeyCode[KeyCode["Joystick6Button18"] = 468] = "Joystick6Button18"; KeyCode[KeyCode["Joystick6Button19"] = 469] = "Joystick6Button19"; KeyCode[KeyCode["Joystick7Button0"] = 470] = "Joystick7Button0"; KeyCode[KeyCode["Joystick7Button1"] = 471] = "Joystick7Button1"; KeyCode[KeyCode["Joystick7Button2"] = 472] = "Joystick7Button2"; KeyCode[KeyCode["Joystick7Button3"] = 473] = "Joystick7Button3"; KeyCode[KeyCode["Joystick7Button4"] = 474] = "Joystick7Button4"; KeyCode[KeyCode["Joystick7Button5"] = 475] = "Joystick7Button5"; KeyCode[KeyCode["Joystick7Button6"] = 476] = "Joystick7Button6"; KeyCode[KeyCode["Joystick7Button7"] = 477] = "Joystick7Button7"; KeyCode[KeyCode["Joystick7Button8"] = 478] = "Joystick7Button8"; KeyCode[KeyCode["Joystick7Button9"] = 479] = "Joystick7Button9"; KeyCode[KeyCode["Joystick7Button10"] = 480] = "Joystick7Button10"; KeyCode[KeyCode["Joystick7Button11"] = 481] = "Joystick7Button11"; KeyCode[KeyCode["Joystick7Button12"] = 482] = "Joystick7Button12"; KeyCode[KeyCode["Joystick7Button13"] = 483] = "Joystick7Button13"; KeyCode[KeyCode["Joystick7Button14"] = 484] = "Joystick7Button14"; KeyCode[KeyCode["Joystick7Button15"] = 485] = "Joystick7Button15"; KeyCode[KeyCode["Joystick7Button16"] = 486] = "Joystick7Button16"; KeyCode[KeyCode["Joystick7Button17"] = 487] = "Joystick7Button17"; KeyCode[KeyCode["Joystick7Button18"] = 488] = "Joystick7Button18"; KeyCode[KeyCode["Joystick7Button19"] = 489] = "Joystick7Button19"; KeyCode[KeyCode["Joystick8Button0"] = 490] = "Joystick8Button0"; KeyCode[KeyCode["Joystick8Button1"] = 491] = "Joystick8Button1"; KeyCode[KeyCode["Joystick8Button2"] = 492] = "Joystick8Button2"; KeyCode[KeyCode["Joystick8Button3"] = 493] = "Joystick8Button3"; KeyCode[KeyCode["Joystick8Button4"] = 494] = "Joystick8Button4"; KeyCode[KeyCode["Joystick8Button5"] = 495] = "Joystick8Button5"; KeyCode[KeyCode["Joystick8Button6"] = 496] = "Joystick8Button6"; KeyCode[KeyCode["Joystick8Button7"] = 497] = "Joystick8Button7"; KeyCode[KeyCode["Joystick8Button8"] = 498] = "Joystick8Button8"; KeyCode[KeyCode["Joystick8Button9"] = 499] = "Joystick8Button9"; KeyCode[KeyCode["Joystick8Button10"] = 500] = "Joystick8Button10"; KeyCode[KeyCode["Joystick8Button11"] = 501] = "Joystick8Button11"; KeyCode[KeyCode["Joystick8Button12"] = 502] = "Joystick8Button12"; KeyCode[KeyCode["Joystick8Button13"] = 503] = "Joystick8Button13"; KeyCode[KeyCode["Joystick8Button14"] = 504] = "Joystick8Button14"; KeyCode[KeyCode["Joystick8Button15"] = 505] = "Joystick8Button15"; KeyCode[KeyCode["Joystick8Button16"] = 506] = "Joystick8Button16"; KeyCode[KeyCode["Joystick8Button17"] = 507] = "Joystick8Button17"; KeyCode[KeyCode["Joystick8Button18"] = 508] = "Joystick8Button18"; KeyCode[KeyCode["Joystick8Button19"] = 509] = "Joystick8Button19"; })(KeyCode || (KeyCode = {})); /***/ }), /***/ "./src/il2cpp/index.ts": /*!*****************************!*\ !*** ./src/il2cpp/index.ts ***! \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ createIl2CppContext: () => (/* binding */ createIl2CppContext), /* harmony export */ createMetadata: () => (/* binding */ createMetadata) /* harmony export */ }); /* unused harmony export metadataVer */ /* harmony import */ var neverthrow__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! neverthrow */ "./node_modules/neverthrow/dist/index.es.js"); /* harmony import */ var _utils_binary__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/binary */ "./src/utils/binary/index.ts"); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../errors */ "./src/errors/index.ts"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../utils */ "./src/utils/index.ts"); var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; // updating this let metadataVer = 0; function createIl2CppContext(buffer, metadata, referencedAssemblies) { var _a, _b, _c, _d; console.debug("Creating IL2CPP Context"); const dataSections = []; const reader = new _utils_binary__WEBPACK_IMPORTED_MODULE_1__.BinaryReader(buffer); reader.seek(8); while (reader.offset < buffer.byteLength) { const id = reader.readULEB128(); const len = reader.readULEB128(); if (id !== 11) { // Skip until we reach data section reader.seek(reader.offset + len); continue; } const count = reader.readULEB128(); for (let i = 0; i < count; i++) { const index = reader.readULEB128(); reader.seek(reader.offset + 1); const offset = reader.readULEB128(); reader.seek(reader.offset + 1); const data = reader.readUint8Array(reader.readULEB128()); dataSections.push({ index, offset, data, }); } break; } const last = dataSections[dataSections.length - 1]; const bssStart = last.offset + last.data.length; // Initialized memory buffer const memoryBuffer = new ArrayBuffer(buffer.byteLength); const memoryReader = new _utils_binary__WEBPACK_IMPORTED_MODULE_1__.BinaryReader(memoryBuffer); const memoryWriter = new _utils_binary__WEBPACK_IMPORTED_MODULE_1__.BinaryWriter(memoryBuffer); dataSections.forEach((dataSection) => { memoryWriter.seek(dataSection.offset); memoryWriter.writeBytes(dataSection.data); }); // Plus search const sectionHelper = getSectionHelper(buffer.byteLength, memoryBuffer, bssStart, metadata.methodDefs.length, metadata.originalImageDefCount); const codeRegistration = sectionHelper.findCodeRegistration(); const pCodeRegistration = readCodeRegistration(memoryReader, codeRegistration); const pCodeGenModules = readCodeGenModules(memoryReader, pCodeRegistration.codeGenModules, pCodeRegistration.codeGenModulesCount); const codeGenModules = {}; const codeGenModuleMethodPointers = {}; for (let i = 0; i < pCodeGenModules.length; i++) { const pCodeGenModule = readCodeGenModule(memoryReader, pCodeGenModules[i]); memoryReader.seek(pCodeGenModule.moduleName); const moduleName = memoryReader.readNullTerminatedUTF8String(); if (!(referencedAssemblies === null || referencedAssemblies === void 0 ? void 0 : referencedAssemblies.includes(moduleName))) continue; codeGenModules[moduleName] = pCodeGenModule; const methodPointers = readCodeGenModuleMethodPointers(memoryReader, pCodeGenModule.methodPointers, pCodeGenModule.methodPointerCount); codeGenModuleMethodPointers[moduleName] = methodPointers; } const scriptData = {}; const metadataReader = new _utils_binary__WEBPACK_IMPORTED_MODULE_1__.BinaryReader(metadata.buffer); for (let j = 0; j < metadata.imageDefs.length; j++) { let imageDef = metadata.imageDefs[j]; let imageName = getStringFromIndex(metadataReader, (_a = metadata.header.stringOffset) !== null && _a !== void 0 ? _a : metadata.header.stringsOffset, imageDef.nameIndex); let typeEnd = imageDef.typeStart + imageDef.typeCount; for (let k = imageDef.typeStart; k < typeEnd; k++) { let typeDef = metadata.typeDefs.find((def) => def.typeIndex === k); if (!typeDef) continue; let typeName = getStringFromIndex(metadataReader, (_b = metadata.header.stringOffset) !== null && _b !== void 0 ? _b : metadata.header.stringsOffset, typeDef.nameIndex); const namespaceName = getStringFromIndex(metadataReader, (_c = metadata.header.stringOffset) !== null && _c !== void 0 ? _c : metadata.header.stringsOffset, typeDef.namespaceIndex); let methodEnd = typeDef.methodStart + typeDef.method_count; for (let l = typeDef.methodStart; l < methodEnd; l++) { let methodDef = metadata.methodDefs.find((def) => def.methodIndex === l); if (!methodDef) continue; let methodName = getStringFromIndex(metadataReader, (_d = metadata.header.stringOffset) !== null && _d !== void 0 ? _d : metadata.header.stringsOffset, methodDef.nameIndex); let methodToken = methodDef.token; let ptrs = codeGenModuleMethodPointers[imageName]; let methodPointerIndex = methodToken & 0x00ffffff; const ptr = ptrs[methodPointerIndex - 1]; const fullTypeName = namespaceName === "" ? typeName : namespaceName + "." + typeName; if (!scriptData[fullTypeName]) { scriptData[fullTypeName] = {}; // Create an empty object if it doesn't exist } if (scriptData[fullTypeName][methodName] !== undefined) { const ptrRef = scriptData[fullTypeName][methodName]; delete scriptData[fullTypeName][methodName]; scriptData[fullTypeName][methodName + "_" + ptrRef] = ptrRef; methodName = `${methodName}_${ptr}`; } scriptData[fullTypeName][methodName] = ptr; } } } metadata.typeDefs.forEach((def) => delete def.typeIndex); metadata.methodDefs.forEach((def) => delete def.methodIndex); return (0,neverthrow__WEBPACK_IMPORTED_MODULE_0__.ok)({ codeGenModules, codeGenModuleMethodPointers, scriptData, name: "il2cpp", }); } function createMetadata(buffer, referencedAssemblies) { return __awaiter(this, void 0, void 0, function* () { console.debug("Creating Metadata"); const reader = new _utils_binary__WEBPACK_IMPORTED_MODULE_1__.BinaryReader(buffer); const sanity = reader.readUint32(); if (sanity !== 0xfab11baf) return (0,neverthrow__WEBPACK_IMPORTED_MODULE_0__.err)(new _errors__WEBPACK_IMPORTED_MODULE_2__.MetadataParsingError("Metadata file supplied is not a valid metadata file.")); const version = reader.readUint32(); metadataVer = version; if (version < 0 || version > 1000) return (0,neverthrow__WEBPACK_IMPORTED_MODULE_0__.err)(new _errors__WEBPACK_IMPORTED_MODULE_2__.MetadataParsingError("Metadata file supplied is not a valid metadata file.")); // TODO: Support more metadata versions if (!(version == 31 || version == 29 || version == 35 || version == 39)) return (0,neverthrow__WEBPACK_IMPORTED_MODULE_0__.err)(new _errors__WEBPACK_IMPORTED_MODULE_2__.MetadataParsingError(`Metadata file supplied is not a supported version [${version}].`)); reader.seek(0); const header = readHeader(reader, version); // Calculate index sizes for v38+ const getIndexSize = (count) => { if (count <= 0xff) return 1; if (count <= 0xffff) return 2; return 4; }; const typeIndexSize = version >= 38 ? header.parametersSize / header.parametersCount - 8 : 4; const typeDefinitionIndexSize = version >= 38 ? getIndexSize(header.typeDefinitionsCount) : 4; const genericContainerIndexSize = version >= 38 ? getIndexSize(header.genericContainersCount) : 4; const parameterIndexSize = version >= 38 ? getIndexSize(header.parametersCount) : 4; const indexSizes = { typeIndex: typeIndexSize, typeDefinitionIndex: typeDefinitionIndexSize, genericContainerIndex: genericContainerIndexSize, parameterIndex: parameterIndexSize, }; // Helper to resolve offset/size field from the header, supporting both v<38 (e.g. imagesOffset) and v38+ (imagesOffset from "images" section) const hOffset = (legacyOffsetName, newSectionName) => { var _a, _b; return version >= 38 ? (_a = header[newSectionName + "Offset"]) !== null && _a !== void 0 ? _a : 0 : (_b = header[legacyOffsetName]) !== null && _b !== void 0 ? _b : 0; }; const hSize = (legacySizeName, newSectionName) => { var _a, _b; return version >= 38 ? (_a = header[newSectionName + "Size"]) !== null && _a !== void 0 ? _a : 0 : (_b = header[legacySizeName]) !== null && _b !== void 0 ? _b : 0; }; const hCount = (newSectionName) => { var _a; return (version >= 38 ? (_a = header[newSectionName + "Count"]) !== null && _a !== void 0 ? _a : 0 : 0); }; const imageOffset = hOffset("imagesOffset", "images"); const imageSize = hSize("imagesSize", "images"); console.debug("[DEBUG] version:", version); console.debug("[DEBUG] full header:", JSON.stringify(header)); console.debug("[DEBUG] indexSizes:", JSON.stringify(indexSizes)); console.debug("[DEBUG] imagesOffset:", imageOffset, "imagesSize:", imageSize); console.debug("[DEBUG] buffer byteLength:", buffer.byteLength); const imageDefs = readImageDefinitions(reader, imageOffset, imageSize, indexSizes, version); console.log("[DEBUG] imageDefs length:", imageDefs.length); console.log("[DEBUG] imageDefs:", JSON.stringify(imageDefs)); const referencedImageDefs = []; var i = 0, len = imageDefs.length; while (i < len) { const imageDef = imageDefs[i]; const imageName = getStringFromIndex(reader, hOffset("stringOffset", "strings"), imageDef.nameIndex); console.log("imgname is ", imageName); if (referencedAssemblies === null || referencedAssemblies === void 0 ? void 0 : referencedAssemblies.includes(imageName)) referencedImageDefs.push(imageDef); i++; } let typeDefs = readTypeDefinitions(reader, hOffset("typeDefinitionsOffset", "typeDefinitions"), hSize("typeDefinitionsSize", "typeDefinitions"), referencedImageDefs, indexSizes, version); const methodDefs = readMethodDefinitions(reader, hOffset("methodsOffset", "methods"), hSize("methodsSize", "methods"), indexSizes); const referencedMethodDefs = []; (i = 0), (len = methodDefs.length); while (i < len) { const methodDef = methodDefs[i]; if (typeDefs.findIndex((t) => t.typeIndex === methodDef.declaringType) !== -1) referencedMethodDefs.push(methodDef); i++; } const integrityHash = (0,_utils__WEBPACK_IMPORTED_MODULE_3__.bufToHex)(yield window.crypto.subtle.digest("SHA-256", buffer)); return (0,neverthrow__WEBPACK_IMPORTED_MODULE_0__.ok)({ buffer, header, imageDefs: referencedImageDefs, typeDefs, methodDefs, originalImageDefCount: imageDefs.length, originalMethodDefCount: methodDefs.length, version, name: "metadata", referencedAssemblies, integrityHash, }); }); } function getStringFromIndex(reader, base, offset) { reader.seek(base + offset); return reader.readNullTerminatedUTF8String(); } function isReferencedType(imageDefinitions, typeDefinitionsOffset, readerOffset, typeDefStructSize) { for (const imageDef of imageDefinitions) { let typeStart = imageDef.typeStart * typeDefStructSize + typeDefinitionsOffset; let typeCount = imageDef.typeCount * typeDefStructSize; let typeEnd = typeStart + typeCount; if (readerOffset >= typeStart && readerOffset < typeEnd) { return true; } } return false; } function readHeader(reader, version) { if (version >= 38) { const sanity = reader.readUint32(); const ver = reader.readInt32(); const header = { sanity, version: ver }; const fields = [ "stringLiterals", "stringLiteralData", "strings", "events", "properties", "methods", "parameterDefaultValues", "fieldDefaultValues", "fieldAndParameterDefaultValueData", "fieldMarshaledSizes", "parameters", "fields", "genericParameters", "genericParameterConstraints", "genericContainers", "nestedTypes", "interfaces", "vtableMethods", "interfaceOffsets", "typeDefinitions", "images", "assemblies", "fieldRefs", "referencedAssemblies", "attributeData", "attributeDataRanges", "unresolvedIndirectCallParameterTypes", "unresolvedIndirectCallParameterRanges", "windowsRuntimeTypeNames", "windowsRuntimeStrings", "exportedTypeDefinitions", ]; for (const f of fields) { header[f + "Offset"] = reader.readUint32(); header[f + "Size"] = reader.readInt32(); header[f + "Count"] = reader.readUint32(); } return header; } return { sanity: reader.readUint32(), version: reader.readInt32(), stringLiteralOffset: reader.readUint32(), stringLiteralSize: reader.readInt32(), stringLiteralDataOffset: reader.readUint32(), stringLiteralDataSize: reader.readInt32(), stringOffset: reader.readUint32(), stringSize: reader.readInt32(), eventsOffset: reader.readUint32(), eventsSize: reader.readInt32(), propertiesOffset: reader.readUint32(), propertiesSize: reader.readInt32(), methodsOffset: reader.readUint32(), methodsSize: reader.readInt32(), parameterDefaultValuesOffset: reader.readUint32(), parameterDefaultValuesSize: reader.readInt32(), fieldDefaultValuesOffset: reader.readUint32(), fieldDefaultValuesSize: reader.readInt32(), fieldAndParameterDefaultValueDataOffset: reader.readUint32(), fieldAndParameterDefaultValueDataSize: reader.readInt32(), fieldMarshaledSizesOffset: reader.readInt32(), fieldMarshaledSizesSize: reader.readInt32(), parametersOffset: reader.readUint32(), parametersSize: reader.readInt32(), fieldsOffset: reader.readUint32(), fieldsSize: reader.readInt32(), genericParametersOffset: reader.readUint32(), genericParametersSize: reader.readInt32(), genericParameterConstraintsOffset: reader.readUint32(), genericParameterConstraintsSize: reader.readInt32(), genericContainersOffset: reader.readUint32(), genericContainersSize: reader.readInt32(), nestedTypesOffset: reader.readUint32(), nestedTypesSize: reader.readInt32(), interfacesOffset: reader.readUint32(), interfacesSize: reader.readInt32(), vtableMethodsOffset: reader.readUint32(), vtableMethodsSize: reader.readInt32(), interfaceOffsetsOffset: reader.readInt32(), interfaceOffsetsSize: reader.readInt32(), typeDefinitionsOffset: reader.readUint32(), typeDefinitionsSize: reader.readInt32(), /*rgctxEntriesOffset: reader.readUint32(), Max v24.1 //rgctxEntriesCount: reader.readInt32(),*/ imagesOffset: reader.readUint32(), imagesSize: reader.readInt32(), assembliesOffset: reader.readUint32(), assembliesSize: reader.readInt32(), /*metadataUsageListsOffset: reader.readUint32(), Max v24.5 metadataUsageListsCount: reader.readInt32(), metadataUsagePairsOffset: reader.readUint32(), metadataUsagePairsCount: reader.readInt32(),*/ fieldRefsOffset: reader.readUint32(), fieldRefsSize: reader.readInt32(), referencedAssembliesOffset: reader.readInt32(), referencedAssembliesSize: reader.readInt32(), /*attributesInfoOffset: reader.readUint32(), Max v27.2 attributesInfoCount: reader.readInt32(), attributeTypesOffset: reader.readUint32(), attributeTypesCount: reader.readInt32(),*/ attributeDataOffset: reader.readUint32(), attributeDataSize: reader.readInt32(), attributeDataRangeOffset: reader.readUint32(), attributeDataRangeSize: reader.readInt32(), unresolvedVirtualCallParameterTypesOffset: reader.readInt32(), unresolvedVirtualCallParameterTypesSize: reader.readInt32(), unresolvedVirtualCallParameterRangesOffset: reader.readInt32(), unresolvedVirtualCallParameterRangesSize: reader.readInt32(), windowsRuntimeTypeNamesOffset: reader.readInt32(), windowsRuntimeTypeNamesSize: reader.readInt32(), windowsRuntimeStringsOffset: reader.readInt32(), windowsRuntimeStringsSize: reader.readInt32(), exportedTypeDefinitionsOffset: reader.readInt32(), exportedTypeDefinitionsSize: reader.readInt32(), }; } function readImageDefinitions(reader, offset, size, indexSizes, version) { var _a, _b; console.log("[DEBUG] readImageDefinitions called: offset=", offset, "size=", size, "version=", version); if (offset === 0 || size === 0) { console.error("[DEBUG] readImageDefinitions: offset or size is 0 � header was parsed incorrectly!"); return []; } reader.seek(offset); const imageDefinitions = []; const imagesEnd = offset + size; console.log("[DEBUG] imagesEnd:", imagesEnd, "buffer byteLength via reader:", (_b = (_a = reader._buffer) === null || _a === void 0 ? void 0 : _a.byteLength) !== null && _b !== void 0 ? _b : "unknown"); let loopCount = 0; while (reader.offset < imagesEnd) { loopCount++; const startOffset = reader.offset; const entry = { nameIndex: reader.readUint32(), assemblyIndex: reader.readInt32(), typeStart: reader.readIndex(indexSizes.typeDefinitionIndex), typeCount: reader.readUint32(), exportedTypeStart: version >= 24 ? reader.readIndex(indexSizes.typeDefinitionIndex) : 0, exportedTypeCount: version >= 24 ? reader.readUint32() : 0, entryPointIndex: reader.readInt32(), token: version >= 19 ? reader.readUint32() : 0, customAttributeStart: version >= 24.1 ? reader.readInt32() : 0, customAttributeCount: version >= 24.1 ? reader.readUint32() : 0, }; console.log(`[DEBUG] imageDef[${loopCount - 1}] startOffset=${startOffset} endOffset=${reader.offset}:`, JSON.stringify(entry)); imageDefinitions.push(entry); } console.log("[DEBUG] readImageDefinitions: parsed", imageDefinitions.length, "entries"); return imageDefinitions; } // updated function readTypeDefinitions(reader, offset, size, imageDefinitions, indexSizes, version) { console.debug("Reading Type Defs"); // Compute the per-struct byte size based on what we actually read: // 2x uint32 (nameIndex, namespaceIndex) = 8 // byvalTypeIndex(typeIndex), declaringTypeIndex(typeDef), parentIndex(typeIndex) = 3 * typeIndex or mixed // elementTypeIndex only for version < 35 = typeIndex // genericContainerIndex = genericContainerIndex // flags (uint32) = 4 // fieldStart..interfaceOffsetsStart = 8 x int32 = 32 // 8 x uint16 (counts) = 16 // bitfield + token = 8 const sz = indexSizes; const typeDefStructSize = 8 + // nameIndex, namespaceIndex sz.typeIndex + // byvalTypeIndex sz.typeDefinitionIndex + // declaringTypeIndex sz.typeIndex + // parentIndex (version < 35 ? sz.typeIndex : 0) + // elementTypeIndex sz.genericContainerIndex + // genericContainerIndex 4 + // flags 8 * 4 + // fieldStart..interfaceOffsetsStart (8 x int32) 8 * 2 + // method_count..interface_offsets_count (8 x uint16) 4 + 4; // bitfield, token reader.seek(offset); const typeDefinitions = []; const typesEnd = offset + size; let i = 0; while (reader.offset < typesEnd) { const typeDef = { typeIndex: i, nameIndex: reader.readUint32(), namespaceIndex: reader.readUint32(), byvalTypeIndex: reader.readIndex(indexSizes.typeIndex), declaringTypeIndex: reader.readIndex(indexSizes.typeDefinitionIndex), parentIndex: reader.readIndex(indexSizes.typeIndex), elementTypeIndex: version < 35 ? reader.readIndex(indexSizes.typeIndex) : 0, genericContainerIndex: reader.readIndex(indexSizes.genericContainerIndex), flags: reader.readUint32(), fieldStart: reader.readInt32(), methodStart: reader.readInt32(), eventStart: reader.readInt32(), propertyStart: reader.readInt32(), nestedTypesStart: reader.readInt32(), interfacesStart: reader.readInt32(), vtableStart: reader.readInt32(), interfaceOffsetsStart: reader.readInt32(), method_count: reader.readUint16(), property_count: reader.readUint16(), field_count: reader.readUint16(), event_count: reader.readUint16(), nested_type_count: reader.readUint16(), vtable_count: reader.readUint16(), interfaces_count: reader.readUint16(), interface_offsets_count: reader.readUint16(), bitfield: reader.readUint32(), token: reader.readUint32(), }; i++; if (!isReferencedType(imageDefinitions, offset, reader.offset - 1, typeDefStructSize)) continue; typeDefinitions.push(typeDef); } return typeDefinitions; } function readMethodDefinitions(reader, offset, size, indexSizes) { console.debug("Reading Method Defs"); reader.seek(offset); const methodDefinitions = []; const methodsEnd = offset + size; let i = 0; while (reader.offset < methodsEnd) { methodDefinitions.push({ methodIndex: i, nameIndex: reader.readUint32(), declaringType: reader.readIndex(indexSizes.typeDefinitionIndex), returnType: reader.readIndex(indexSizes.typeIndex), returnParameterToken: reader.readInt32(), parameterStart: reader.readIndex(indexSizes.parameterIndex), genericContainerIndex: reader.readIndex(indexSizes.genericContainerIndex), token: reader.readUint32(), flags: reader.readUint16(), iflags: reader.readUint16(), slot: reader.readUint16(), parameterCount: reader.readUint16(), }); i++; } return methodDefinitions; } function readCodeRegistration(reader, offset) { console.debug("Reading Code Registration"); reader.seek(offset); return { reversePInvokeWrapperCount: reader.readUint32(), reversePInvokeWrappers: reader.readUint32(), genericMethodPointersCount: reader.readUint32(), genericMethodPointers: reader.readUint32(), genericAdjustorThunks: reader.readUint32(), invokerPointersCount: reader.readUint32(), invokerPointers: reader.readUint32(), unresolvedVirtualCallCount: reader.readUint32(), unresolvedVirtualCallPointers: reader.readUint32(), interopDataCount: reader.readUint32(), interopData: reader.readUint32(), windowsRuntimeFactoryCount: reader.readUint32(), windowsRuntimeFactoryTable: reader.readUint32(), codeGenModulesCount: reader.readUint32(), codeGenModules: reader.readUint32(), }; } function readCodeGenModules(reader, offset, size) { console.debug("Reading CodeGen Modules"); reader.seek(offset); const modules = []; for (let i = 0; i < size; i++) { modules.push(reader.readUint32()); } return modules; } function readCodeGenModule(reader, offset) { reader.seek(offset); return { moduleName: reader.readUint32(), methodPointerCount: reader.readInt32(), methodPointers: reader.readUint32(), adjustorThunkCount: reader.readInt32(), adjustorThunks: reader.readUint32(), invokerIndices: reader.readUint32(), reversePInvokeWrapperCount: reader.readUint32(), reversePInvokeWrapperIndices: reader.readUint32(), rgctxRangesCount: reader.readInt32(), rgctxRanges: reader.readUint32(), rgctxsCount: reader.readInt32(), rgctxs: reader.readUint32(), debuggerMetadata: reader.readUint32(), moduleInitializer: reader.readUint32(), staticConstructorTypeIndices: reader.readUint32(), metadataRegistration: reader.readUint32(), codeRegistration: reader.readUint32(), }; } function readCodeGenModuleMethodPointers(reader, offset, size) { reader.seek(offset); const methodPointers = []; for (let i = 0; i < size; i++) { methodPointers.push(reader.readUint32()); } return methodPointers; } function getSectionHelper(length, memoryBuffer, bssStart, methodCount, imageCount) { const exec = { offset: 0, offsetEnd: methodCount, address: 0, addressEnd: methodCount, }; const data = { offset: 1024, offsetEnd: length, address: 1024, addressEnd: length, }; const bss = { offset: bssStart, offsetEnd: BigInt(9223372036854775807), address: bssStart, addressEnd: BigInt(9223372036854775807), }; const sectionHelper = new SectionHelper(memoryBuffer, imageCount); sectionHelper.setExecSection(exec); sectionHelper.setDataSection(data); sectionHelper.setBssSection(bss); return sectionHelper; } class SectionHelper { constructor(memoryBuffer, imageCount) { this.exec = []; this.data = []; this.bss = []; this.memoryReader = new _utils_binary__WEBPACK_IMPORTED_MODULE_1__.BinaryReader(memoryBuffer); this.imageCount = imageCount; } setExecSection(exec) { this.exec.push(exec); } setDataSection(data) { this.data.push(data); } setBssSection(bss) { this.bss.push(bss); } findCodeRegistration() { let codeRegistration = this.findCodeRegistrationData(); return codeRegistration; } findCodeRegistrationData() { return this.findCodeRegistration2019(this.data); } findCodeRegistration2019(secs) { for (let i = 0; i < secs.length; i++) { const sec = secs[i]; this.memoryReader.seek(sec.offset); const buff = this.memoryReader.readUint8Array(sec.offsetEnd - sec.offset); const matches = (0,_utils__WEBPACK_IMPORTED_MODULE_3__.patternSearch)(buff, SectionHelper.featureBytes); for (let j = 0; j < matches.length; j++) { const dllva = matches[j] + sec.address; const refvas = this.findReference(dllva); for (let k = 0; k < refvas.length; k++) { const refva = refvas[k]; const refva2s = this.findReference(refva); for (let l = 0; l < refva2s.length; l++) { const refva2 = refva2s[l]; for (let m = this.imageCount - 1; m >= 0; m--) { const refva3s = this.findReference(refva2 - m * 4); for (let n = 0; n < refva3s.length; n++) { const refva3 = refva3s[n]; this.memoryReader.seek(refva3 - 4); if (this.memoryReader.readInt32() === this.imageCount) { return refva3 - 4 * 14; } } } } } } } return 0; } findReference(addr) { const references = []; for (let i = 0; i < this.data.length; i++) { const dataSec = this.data[i]; var position = dataSec.offset; const end = Math.min(dataSec.offsetEnd, this.memoryReader.buffer.byteLength) - 4; while (position < end) { this.memoryReader.seek(position); if (this.memoryReader.readUint32() === addr) { references.push(position - dataSec.offset + dataSec.address); } position += 4; } } return references; } } SectionHelper.featureBytes = new Uint8Array([0x6d, 0x73, 0x63, 0x6f, 0x72, 0x6c, 0x69, 0x62, 0x2e, 0x64, 0x6c, 0x6c, 0x00]); /***/ }), /***/ "./src/logger/index.ts": /*!*****************************!*\ !*** ./src/logger/index.ts ***! \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ LogLevel: () => (/* binding */ LogLevel), /* harmony export */ Logger: () => (/* binding */ Logger) /* harmony export */ }); var LogLevel; (function (LogLevel) { LogLevel[LogLevel["NONE"] = 0] = "NONE"; LogLevel[LogLevel["ERROR"] = 1] = "ERROR"; LogLevel[LogLevel["WARN"] = 2] = "WARN"; LogLevel[LogLevel["INFO"] = 4] = "INFO"; LogLevel[LogLevel["DEBUG"] = 8] = "DEBUG"; LogLevel[LogLevel["MESSAGE"] = 16] = "MESSAGE"; LogLevel[LogLevel["ALL"] = 31] = "ALL"; })(LogLevel || (LogLevel = {})); class Logger { constructor(name) { this.name = name; } error(...args) { this.log(LogLevel.ERROR, ...args); } warn(...args) { this.log(LogLevel.WARN, ...args); } info(...args) { this.log(LogLevel.INFO, ...args); } debug(...args) { this.log(LogLevel.DEBUG, ...args); } message(...args) { this.log(LogLevel.MESSAGE, ...args); } log(level, ...args) { if (this.shouldLog(level) && args.length > 0) { const logPrefix = `%c[${this.name}] %c[${LogLevel[level]}]%c`; let message = args.shift(); if (typeof message !== "string") { args.push(message); message = ""; } else { message = " " + message; } let logStyles = "color: #fff;"; let messageStyles; switch (level) { case LogLevel.ERROR: messageStyles = "color: #FF6E74;"; break; case LogLevel.WARN: messageStyles = "color: #FFB36A;"; break; case LogLevel.INFO: messageStyles = "color: #35EA93;"; break; case LogLevel.DEBUG: messageStyles = "color: #BE7CFF;"; break; case LogLevel.MESSAGE: messageStyles = "color: #56C4FF;"; break; } console.log(logPrefix + message, logStyles, messageStyles, "color: default;", ...args); } } shouldLog(level) { if (level === LogLevel.DEBUG) // @ts-ignore return true; return true; } } /***/ }), /***/ "./src/mod.ts": /*!********************!*\ !*** ./src/mod.ts ***! \********************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ KeyCode: () => (/* reexport safe */ _extras__WEBPACK_IMPORTED_MODULE_2__.KeyCode), /* harmony export */ LogLevel: () => (/* reexport safe */ _logger__WEBPACK_IMPORTED_MODULE_1__.LogLevel), /* harmony export */ Logger: () => (/* reexport safe */ _logger__WEBPACK_IMPORTED_MODULE_1__.Logger), /* harmony export */ Runtime: () => (/* binding */ Runtime), /* harmony export */ ValueWrapper: () => (/* reexport safe */ _runtime__WEBPACK_IMPORTED_MODULE_0__.ValueWrapper), /* harmony export */ dataTypeSizes: () => (/* reexport safe */ _extras__WEBPACK_IMPORTED_MODULE_2__.dataTypeSizes), /* harmony export */ version: () => (/* binding */ version) /* harmony export */ }); /* harmony import */ var _runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./runtime */ "./src/runtime/index.ts"); /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logger */ "./src/logger/index.ts"); /* harmony import */ var _extras__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./extras */ "./src/extras/index.ts"); // Exports const Runtime = new _runtime__WEBPACK_IMPORTED_MODULE_0__.Runtime(); // @ts-ignore Set by webpack at bundle time const version = "1.1.0"; /***/ }), /***/ "./src/preloader/index.ts": /*!********************************!*\ !*** ./src/preloader/index.ts ***! \********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ preload: () => (/* binding */ preload) /* harmony export */ }); /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../logger */ "./src/logger/index.ts"); /* harmony import */ var _web_data__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../web-data */ "./src/web-data/index.ts"); /* harmony import */ var _mod__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../mod */ "./src/mod.ts"); var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const logger = new _logger__WEBPACK_IMPORTED_MODULE_0__.Logger("Preloader"); function preload() { logger.info("UnityWebModkit v%s - %s", _mod__WEBPACK_IMPORTED_MODULE_2__.version, window.location.hostname); // @ts-ignore Set by webpack at bundle time logger.info("Build hash: %s", __webpack_require__.h()); return preloadInternal(); } function preloadInternal() { return __awaiter(this, void 0, void 0, function* () { // Install the fetch hook FIRST before any async work, so we never miss // a Unity request that fires during or before cache clearing. logger.debug("[Preloader] Installing fetch hook early..."); const webDataPromise = fallbackInterceptFetch(); logger.debug("[Preloader] Clearing Unity caches..."); yield clearUnityCache(); logger.debug("[Preloader] Waiting for Unity web data..."); return yield webDataPromise; }); } // --------------------------------------------------------------------------- // Cache Clearing Utilities // --------------------------------------------------------------------------- function clearUnityIndexedDB() { return __awaiter(this, void 0, void 0, function* () { if (!window.indexedDB) return; try { const existing = yield indexedDB.databases(); const names = existing.map((db) => db.name); if (!names.includes("UnityCache")) return; } catch (_a) { // browsers that don't support indexedDB.databases() — fall through and try anyway } return new Promise((resolve) => { const req = indexedDB.deleteDatabase("UnityCache"); req.onsuccess = () => { console.log("[Preloader] UnityCache IndexedDB deleted."); resolve(); }; req.onerror = (e) => { console.warn("[Preloader] Failed to delete UnityCache IndexedDB.", e); resolve(); }; req.onblocked = () => { // Another connection is holding the DB open; don't hang — resolve and move on. console.warn("[Preloader] Delete blocked — another connection holds UnityCache. Continuing anyway."); resolve(); }; }); }); } function clearUnityCacheStorage() { return __awaiter(this, void 0, void 0, function* () { try { const deleted = yield caches.delete("UnityCache"); console.log("[Preloader] CacheStorage UnityCache deleted:", deleted); } catch (e) { console.warn("[Preloader] Failed to clear CacheStorage UnityCache:", e); } }); } function clearUnityCache() { return __awaiter(this, void 0, void 0, function* () { console.log("[Preloader] Clearing all Unity caches..."); // Fire-and-forget: don't await IDB deletion so a blocked connection never stalls the preloader. clearUnityIndexedDB(); yield clearUnityCacheStorage(); // Remove any other Unity-named caches try { const names = yield caches.keys(); for (const n of names) { if (n.toLowerCase().includes("unity")) { yield caches.delete(n); console.log("[Preloader] Deleted cache:", n); } } } catch (e) { console.warn("[Preloader] Failed checking CacheStorage keys:", e); } console.log("[Preloader] All Unity caches cleared."); }); } function fallbackInterceptFetch() { return __awaiter(this, void 0, void 0, function* () { logger.debug("Nothing in indexedDB cache, resorting to hooking Fetch API"); return new Promise((resolve) => { const originalFetch = window.fetch; window.fetch = function (url) { return __awaiter(this, arguments, void 0, function* () { if (url.includes(".data")) { logger.info("5: Found request!"); window.fetch = originalFetch; const response = yield originalFetch.apply(this, arguments); resolve(parseWebData(yield response.clone().arrayBuffer())); return response; } logger.info("5: Not matched %s", url); return originalFetch.apply(this, arguments); }); }; }); }); } function parseWebData(data) { return new _web_data__WEBPACK_IMPORTED_MODULE_1__.WebData(data, [["data.unity3d", 32], ["Il2CppData/Metadata/global-metadata.dat"]]); } /***/ }), /***/ "./src/runtime/index.ts": /*!******************************!*\ !*** ./src/runtime/index.ts ***! \******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ Runtime: () => (/* binding */ Runtime), /* harmony export */ ValueWrapper: () => (/* binding */ ValueWrapper) /* harmony export */ }); /* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../logger */ "./src/logger/index.ts"); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../errors */ "./src/errors/index.ts"); /* harmony import */ var _il2cpp__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../il2cpp */ "./src/il2cpp/index.ts"); /* harmony import */ var _preloader__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../preloader */ "./src/preloader/index.ts"); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils */ "./src/utils/index.ts"); /* harmony import */ var _wail__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../wail */ "./src/wail/index.js"); /* harmony import */ var _utils_binary__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/binary */ "./src/utils/binary/index.ts"); /* harmony import */ var _extras__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../extras */ "./src/extras/index.ts"); var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const debugMode = true; class Runtime { constructor() { this.plugins = []; this.startedInitializing = false; this.allReferencedAssemblies = []; this.resolvedIl2CppFunctions = {}; this.logger = new _logger__WEBPACK_IMPORTED_MODULE_0__.Logger("UnityWebModkit"); } createPlugin(opts) { if (!this.startedInitializing) this.initialize(); const plugin = new ModkitPlugin(opts.name, opts.version, opts.referencedAssemblies, this); this.plugins.push(plugin); return plugin; } initialize() { return __awaiter(this, void 0, void 0, function* () { if (typeof window === "undefined") { console.log("\x1b[37m[UnityWebModkit]\x1b[0m \x1b[33m[WARN]\x1b[0m Not running in a browser environment! Nothing will be executed."); return; } if (window.indexedDB) { const dbsToDelete = ["UnityCache"]; // names could vary, this is for 2022 indexedDB .databases() .then((existingDBs) => { const existingNames = existingDBs.map((db) => db.name); dbsToDelete.forEach((dbName) => { if (existingNames.includes(dbName)) { const request = indexedDB.deleteDatabase(dbName); request.onsuccess = () => this.logger.info(`Deleted IndexedDB: ${dbName}`); request.onerror = (event) => this.logger.error(`Error deleting IndexedDB ${dbName}:`, event); request.onblocked = () => this.logger.error(`Deletion blocked for IndexedDB ${dbName}`); } }); }) .catch((err) => this.logger.error("Error listing IndexedDBs:", err)); } this.startedInitializing = true; this.hookWasmInstantiate(); const webData = yield (0,_preloader__WEBPACK_IMPORTED_MODULE_3__.preload)(); this.logger.debug("Parsed web data into %d node(s)", webData.nodes.length); webData.unityVersion ? this.logger.info("Running under Unity %s", webData.unityVersion) : this.logger.warn("Unable to determine Unity version from web data!"); // Always rebuild — no caching. this.loadGlobalMetadata(webData); }); } loadGlobalMetadata(webData) { return __awaiter(this, void 0, void 0, function* () { const metadataNode = webData.getNode("Il2CppData/Metadata/global-metadata.dat"); if (!metadataNode || !metadataNode.data) { this.logger.error(new _errors__WEBPACK_IMPORTED_MODULE_1__.UnresolvedMetadataError("Unable to find global-metadata.dat! The game may be encrypted, corrupt or unsupported.").print()); return; } this.allReferencedAssemblies = this.plugins.flatMap((plugin) => plugin.referencedAssemblies); if (debugMode) { const blob = new Blob([metadataNode.data], { type: "application/octet-stream", }); const url = URL.createObjectURL(blob); this.logger.info("global-metadata.dat download url:", url); } const globalMetadata = yield (0,_il2cpp__WEBPACK_IMPORTED_MODULE_2__.createMetadata)(metadataNode.data, this.allReferencedAssemblies); if (globalMetadata.isErr()) { this.logger.error(globalMetadata.error.print()); return; } this.globalMetadata = globalMetadata.value; // No caching — rebuilt every time. }); } saveGlobalMetadata() { const request = window.indexedDB.open("UnityWebModkit", 2); request.onupgradeneeded = () => { const db = request.result; const objectStore = db.createObjectStore("storage", { keyPath: "name", }); objectStore.createIndex("name", "name", { unique: true }); objectStore.transaction.oncomplete = () => { const storageObjectStore = db.transaction("storage", "readwrite").objectStore("storage"); storageObjectStore.add(this.globalMetadata); }; }; } saveIl2CppContext() { const request = window.indexedDB.open("UnityWebModkit", 2); request.onsuccess = () => { const db = request.result; const storageObjectStore = db.transaction("storage", "readwrite").objectStore("storage"); storageObjectStore.add(this.il2CppContext); }; } readGlobalMetadataFromStorage(webData) { return new Promise((resolve, reject) => { // short circuit //reject(); //return; indexedDB.databases().then((databases) => __awaiter(this, void 0, void 0, function* () { const uwmStore = databases.findIndex((d) => d.name === "UnityWebModkit"); if (uwmStore == -1) { reject(); return; } const request = window.indexedDB.open("UnityWebModkit", 2); request.onsuccess = () => { const transaction = request.result.transaction(["storage"]); const objectStore = transaction.objectStore("storage"); const metadataRequest = objectStore.get("metadata"); metadataRequest.onsuccess = () => __awaiter(this, void 0, void 0, function* () { const metadataNode = webData.getNode("Il2CppData/Metadata/global-metadata.dat"); if (!metadataNode || !metadataNode.data) { reject(); return; } this.allReferencedAssemblies = this.plugins.flatMap((plugin) => plugin.referencedAssemblies); const globalMetadata = metadataRequest.result; this.logger.message(globalMetadata); if (JSON.stringify(this.allReferencedAssemblies.sort()) !== JSON.stringify(globalMetadata.referencedAssemblies.sort())) { reject(); return; } const currentHash = (0,_utils__WEBPACK_IMPORTED_MODULE_4__.bufToHex)(yield window.crypto.subtle.digest("SHA-256", metadataNode.data)); if (currentHash !== globalMetadata.integrityHash) { reject(); return; } this.globalMetadata = globalMetadata; resolve(); }); }; })); }); } readIl2CppContextFromStorage() { return new Promise((resolve, reject) => { indexedDB.databases().then((databases) => __awaiter(this, void 0, void 0, function* () { const uwmStore = databases.findIndex((d) => d.name === "UnityWebModkit"); if (uwmStore == -1) { reject(); return; } const request = window.indexedDB.open("UnityWebModkit", 2); request.onsuccess = () => { const transaction = request.result.transaction(["storage"]); const objectStore = transaction.objectStore("storage"); const il2CppRequest = objectStore.get("il2cpp"); il2CppRequest.onsuccess = () => __awaiter(this, void 0, void 0, function* () { this.il2CppContext = il2CppRequest.result; resolve(); }); }; })); }); } hookWasmInstantiate() { this.instantiateStreaming = WebAssembly.instantiateStreaming; WebAssembly.instantiateStreaming = this.onWebAssemblyInstantiateStreaming.bind(this); this.instantiate = WebAssembly.instantiate; WebAssembly.instantiate = this.onWebAssemblyInstantiate.bind(this); } onWebAssemblyInstantiateStreaming(source, importObject) { var _a; return __awaiter(this, void 0, void 0, function* () { // Wait for the Il2Cpp metadata to be resolved before continuing yield (0,_utils__WEBPACK_IMPORTED_MODULE_4__.waitFor)(() => this.globalMetadata); if (((_a = this.globalMetadata) === null || _a === void 0 ? void 0 : _a.imageDefs.length) === 0) return this.instantiateStreaming(source, importObject); let bufferSource; if (source instanceof Promise) { bufferSource = yield source.then((res) => res.arrayBuffer()); } else if (source instanceof Response) { bufferSource = yield source.arrayBuffer(); } else { this.logger.error("TypeError: Got an unexpected object type as the first argument to WebAssembly.instantiateStreaming;", typeof source); return Promise.reject(); } this.logger.debug("handling buffer ig"); return this.handleBuffer(bufferSource, importObject); }); } onWebAssemblyInstantiate(source, importObject) { var _a; return __awaiter(this, void 0, void 0, function* () { // Wait for the Il2Cpp metadata to be resolved before continuing yield (0,_utils__WEBPACK_IMPORTED_MODULE_4__.waitFor)(() => this.globalMetadata); if (((_a = this.globalMetadata) === null || _a === void 0 ? void 0 : _a.imageDefs.length) === 0) return this.instantiate(source, importObject); let bufferSource; if (source instanceof ArrayBuffer) { bufferSource = source; } else if (ArrayBuffer.isView(source)) { bufferSource = source.buffer; } else { this.logger.error("TypeError: Got an unexpected object type as the first argument to WebAssembly.instantiate;", typeof source); return Promise.reject(); } this.logger.debug("handling buffer ig"); return this.handleBuffer(bufferSource, importObject); }); } handleBuffer(bufferSource, importObject) { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { this.logger.debug("handling buffer"); if (!importObject) importObject = {}; // Always rebuild il2CppContext from the WASM binary — no caching. this.il2CppContext = undefined; this.searchWasmBinary(bufferSource); if (!this.il2CppContext) { this.logger.warn("no ctx - uh oh..."); debugger; reject(); return; } const bufferUint8Array = new Uint8Array(bufferSource); const wailPreparser = new _wail__WEBPACK_IMPORTED_MODULE_5__.WailParser(bufferUint8Array); wailPreparser._optionalSectionFlags |= 1 << _wail__WEBPACK_IMPORTED_MODULE_5__.SECTION_CODE; wailPreparser._optionalSectionFlags |= 1 << _wail__WEBPACK_IMPORTED_MODULE_5__.SECTION_ELEMENT; wailPreparser._optionalSectionFlags |= 1 << _wail__WEBPACK_IMPORTED_MODULE_5__.SECTION_TYPE; wailPreparser.parse(); // this.resolveIl2CppFunctions(importObject); const wail = new _wail__WEBPACK_IMPORTED_MODULE_5__.WailParser(bufferUint8Array); // this.exportIl2CppFunctions(wail); this.logger.message("Chainloader initialized"); this.logger.info("%d plugin(s) to load", this.plugins.length); const replacementFuncIndexes = []; const oldFuncIndexes = []; var i = 0, pluginLen = this.plugins.length; while (i < pluginLen) { const usePlugin = this.plugins[i]; this.logger.info("Loading [%s %s]", usePlugin.name, usePlugin.version); var j = 0, hookLen = usePlugin.hooks.length; while (j < hookLen) { const useHook = usePlugin.hooks[j]; useHook.tableIndex = this.getTableIndex(useHook.typeName, useHook.methodName); if (useHook.tableIndex === -1) { this.logger.warn("Hook '%s::%s' skipped — method not found in scriptData", useHook.typeName, useHook.methodName); ++j; continue; } useHook.index = this.getInternalIndex(useHook.tableIndex); if (useHook.index === undefined) { this.logger.warn("Hook '%s::%s' skipped — internalMappings lookup returned undefined for tableIndex %d", useHook.typeName, useHook.methodName, useHook.tableIndex); ++j; continue; } const injectName = useHook.typeName + "xx" + useHook.methodName + (0,_utils__WEBPACK_IMPORTED_MODULE_4__.makeId)(8); let injectFunc = null; if (!useHook.kind) { injectFunc = (...args) => __awaiter(this, void 0, void 0, function* () { // @ts-ignore yield (0,_utils__WEBPACK_IMPORTED_MODULE_4__.waitFor)(() => window.unityInstance || window.unityGame); // @ts-ignore const _game = window.unityInstance || window.unityGame; const tableName = this.tableName || this.resolveTableName(_game.Module.asm); const originalFunction = _game.Module.asm[tableName].get(useHook.tableIndex); if (!useHook.enabled) { if (useHook.returnType) { return originalFunction(...args); } originalFunction(...args); return; } const wrappedArgs = args.map((arg) => new ValueWrapper(arg)); const result = useHook.callback(...wrappedArgs); // Unwrap arguments in case they were changed in the callback function args = wrappedArgs.map((arg) => arg.val()); if (result === undefined || result === true) { if (useHook.returnType) { return originalFunction(...args); } originalFunction(...args); } }); } else { injectFunc = (...args) => __awaiter(this, void 0, void 0, function* () { // @ts-ignore yield (0,_utils__WEBPACK_IMPORTED_MODULE_4__.waitFor)(() => window.unityInstance || window.unityGame); // @ts-ignore const _game = window.unityInstance || window.unityGame || game; const tableName = this.tableName || this.resolveTableName(_game.Module.asm); const originalFunction = _game.Module.asm[tableName].get(useHook.tableIndex); let originalResult = originalFunction(...args); if (!useHook.enabled) return useHook.returnType ? originalResult : undefined; if (originalResult !== undefined) originalResult = new ValueWrapper(originalResult); const wrappedArgs = args.map((arg) => new ValueWrapper(arg)); useHook.callback(originalResult, ...wrappedArgs); return originalResult === null || originalResult === void 0 ? void 0 : originalResult.val(); }); } importObject.env = importObject.env || {}; importObject.env[injectName] = injectFunc; const injectType = this.internalWasmTypes.findIndex((type) => JSON.stringify(type.params) === JSON.stringify(useHook.params) && type.returnType === useHook.returnType); const replacementFuncIndex = wail.addImportEntry({ moduleStr: "env", fieldStr: injectName, kind: "func", type: injectType, }); replacementFuncIndexes.push(replacementFuncIndex); console.log("[Runtime] Calling wail.getFunctionIndex for hook '%s::%s' | useHook.index:", useHook.typeName, useHook.methodName, useHook.index, "| tableIndex:", useHook.tableIndex); const oldFuncIndex = wail.getFunctionIndex(useHook.index); console.log("[Runtime] wail.getFunctionIndex returned:", oldFuncIndex); oldFuncIndexes.push(oldFuncIndex); ++j; } if (usePlugin.onLoaded) usePlugin.onLoaded(); ++i; } this.logger.debug("after plugin loading importobj.env is", importObject.env); this.resolveIl2CppFunctions(importObject); this.exportIl2CppFunctions(wail); wail.addInstructionParser(_wail__WEBPACK_IMPORTED_MODULE_5__.OP_CALL, (instrBytes) => { const mappedOldFuncIndexes = oldFuncIndexes.map((item) => item.i32()); const reader = new _wail__WEBPACK_IMPORTED_MODULE_5__.BufferReader(instrBytes); const opcode = reader.readUint8(); const callTarget = reader.readVarUint32(); if (mappedOldFuncIndexes.includes(callTarget)) { const workingIndex = mappedOldFuncIndexes.indexOf(callTarget); const workingHook = this.getHookByIndex(workingIndex); if (workingHook) workingHook.applied = true; return new Uint8Array([opcode, ...(0,_wail__WEBPACK_IMPORTED_MODULE_5__.VarUint32ToArray)(replacementFuncIndexes[workingIndex].i32())]); } return instrBytes; }); wail.parse(); this.logger.debug("after wail parse importobj.env is", importObject.env); function makeWasmFunc(params, results, jsImpl) { function wasmType(t) { switch (t) { case "i32": return 0x7f; case "i64": return 0x7e; case "f32": return 0x7d; case "f64": return 0x7c; default: throw new Error("Unsupported type " + t); } } const paramTypes = params.map(wasmType); const resultTypes = results.map(wasmType); const typeVec = [0x60, paramTypes.length, ...paramTypes, resultTypes.length, ...resultTypes]; const typeSection = [0x01, typeVec.length + 1, 0x01, ...typeVec]; const importEntry = [0x01, 0x65, 0x01, 0x66, 0x00, 0x00]; const importSection = [0x02, importEntry.length + 1, 0x01, ...importEntry]; const exportEntry = [0x01, 0x67, 0x00, 0x00]; const exportSection = [0x07, exportEntry.length + 1, 0x01, ...exportEntry]; const bytes = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, ...typeSection, ...importSection, ...exportSection]); const mod = new WebAssembly.Module(bytes); const inst = new WebAssembly.Instance(mod, { e: { f: jsImpl } }); return inst.exports.g; } const wasmOutput = wail.write(); this.logger.debug("wail.write() produced %d bytes, calling instantiate", wasmOutput.byteLength); this.instantiate(wasmOutput, importObject).then((instantiatedSource) => { try { const unappliedHooks = this.getUnappliedHooks(); const tableName = this.tableName || this.resolveTableName(instantiatedSource.instance.exports); unappliedHooks.forEach((hook) => { if (!hook.tableIndex || !hook.index) { hook.tableIndex = this.getTableIndex(hook.typeName, hook.methodName); if (hook.tableIndex === -1) { this.logger.warn("Unapplied hook '%s::%s' skipped — method not found in scriptData", hook.typeName, hook.methodName); return; } hook.index = this.getInternalIndex(hook.tableIndex); if (hook.index === undefined) { this.logger.warn("Unapplied hook '%s::%s' skipped — internalMappings lookup returned undefined for tableIndex %d", hook.typeName, hook.methodName, hook.tableIndex); return; } } // @ts-ignore const table = instantiatedSource.instance.exports[tableName]; // @ts-ignore const originalFunc = table.get(hook.tableIndex); const hookResults = hook.returnType ? [hook.returnType] : []; const jsImpl = !hook.kind ? (...args) => { // PREFIX if (!hook.enabled) return hook.returnType ? originalFunc(...args) : originalFunc(...args); const wrappedArgs = args.map((arg) => new ValueWrapper(arg)); const result = hook.callback(...wrappedArgs); args = wrappedArgs.map((arg) => arg.val()); if (result === undefined || result === true) return hook.returnType ? originalFunc(...args) : originalFunc(...args); } : (...args) => { // POSTFIX let originalResult = originalFunc(...args); if (!hook.enabled) return hook.returnType ? originalResult : undefined; if (originalResult !== undefined) originalResult = new ValueWrapper(originalResult); const wrappedArgs = args.map((arg) => new ValueWrapper(arg)); hook.callback(originalResult, ...wrappedArgs); return originalResult === null || originalResult === void 0 ? void 0 : originalResult.val(); }; // @ts-ignore table.set(hook.tableIndex, makeWasmFunc(hook.params, hookResults, jsImpl)); hook.applied = true; }); this.logger.message("Chainloader startup complete"); resolve(instantiatedSource); } catch (err) { this.logger.error("Error in post-instantiate hook setup:", err); reject(err); } }).catch((err) => { this.logger.error("WebAssembly.instantiate failed — modified WASM binary may be invalid:", err); reject(err); }); this.logger.debug("at end of handle buffer importobj.env is", importObject.env); })); } searchWasmBinary(bufferSource) { if (!this.globalMetadata) return; const il2CppContext = (0,_il2cpp__WEBPACK_IMPORTED_MODULE_2__.createIl2CppContext)(bufferSource, this.globalMetadata, this.allReferencedAssemblies); if (il2CppContext.isErr()) { this.logger.error(il2CppContext.error.print()); return; } this.il2CppContext = il2CppContext.value; // No caching — rebuilt every time. } // public for debugging purposes resolveIl2CppFunctions(importObject) { const il2CppStringNew = 2169; // dev5 only (gone lol) this.resolvedIl2CppFunctions["il2cpp_string_new"] = 2169; // TODO: This is a hack, but seems to work consistently with Unity 2021.3.15f1 (hopefully 2023 too) this.resolvedIl2CppFunctions["il2cpp_object_new"] = 2158; this.logger.info("resolved funcs i hope"); } exportIl2CppFunctions(wail) { console.log("[Runtime] exportIl2CppFunctions | resolvedIl2CppFunctions:", this.resolvedIl2CppFunctions); for (const key in this.resolvedIl2CppFunctions) { const rawIndex = this.resolvedIl2CppFunctions[key]; console.log("[Runtime] exportIl2CppFunctions: calling getFunctionIndex for '%s' | rawIndex:", key, rawIndex); const value = wail.getFunctionIndex(rawIndex); console.log("[Runtime] exportIl2CppFunctions: getFunctionIndex returned:", value); wail.addExportEntry(value, { fieldStr: key, kind: "func", }); } this.logger.info("Exported %d Il2Cpp functions", Object.keys(this.resolvedIl2CppFunctions).length); } resolveTableName(asm) { return Object.keys(asm).find((key) => asm[key].constructor.name == "Table") || "Unknown"; } createObject(typeInfo) { // @ts-ignore const _game = window.unityInstance || game; console.log("creating object with typeinfo", typeInfo); const result = _game.Module.asm.il2cpp_object_new(typeInfo instanceof ValueWrapper ? typeInfo.val() : typeInfo); console.log("created object result at", result); return result; } createMstr(char) { // @ts-ignore const _game = window.unityInstance || game; const charAlloc = this.malloc(char.length); (0,_utils__WEBPACK_IMPORTED_MODULE_4__.writeUint8ArrayAtOffset)(_game.Module.HEAPU8, new TextEncoder().encode(char), charAlloc); // mscorlib needs to be referenced when this is called return _game.Module.asm.il2cpp_string_new(charAlloc, char.length); } memory(address, size) { // @ts-ignore const _game = window.unityInstance || game; if (address instanceof ValueWrapper) address = address.val(); return _game.Module.HEAPU8.slice(address, address + size); } malloc(size) { // @ts-ignore const _game = window.unityInstance || game; return _game.Module.asm.malloc(size); } free(block) { // @ts-ignore const _game = window.unityInstance || game; _game.Module._free(block instanceof ValueWrapper ? block.val() : block); } getTableIndex(targetClass, targetMethod) { var _a; if (!((_a = this.il2CppContext) === null || _a === void 0 ? void 0 : _a.scriptData[targetClass])) return -1; const result = this.il2CppContext.scriptData[targetClass][targetMethod]; if (!result) return -1; return result; } getInternalIndex(tableIndex) { console.log("[Runtime] getInternalIndex called | tableIndex:", tableIndex); console.log("[Runtime] getInternalIndex | internalMappings:", this.internalMappings); if (!this.internalMappings || this.internalMappings.length === 0) { console.error("[Runtime] getInternalIndex: internalMappings is empty or undefined!"); throw new Error(`getInternalIndex: internalMappings is not populated (tableIndex=${tableIndex})`); } const mapping = this.internalMappings[0]; if (!mapping || !mapping.elements) { console.error("[Runtime] getInternalIndex: internalMappings[0] or its .elements is missing!", mapping); throw new Error(`getInternalIndex: internalMappings[0].elements is undefined (tableIndex=${tableIndex})`); } const lookupIndex = tableIndex - 1; const result = mapping.elements[lookupIndex]; console.log("[Runtime] getInternalIndex | lookupIndex:", lookupIndex, "| elements.length:", mapping.elements.length, "| result:", result); if (result === undefined) { console.error("[Runtime] getInternalIndex: index out of bounds!", { tableIndex, lookupIndex, elementsLength: mapping.elements.length }); } return result; } getHookByIndex(index) { let totalHooksCount = 0; for (const plugin of this.plugins) { const hooksCount = plugin.hooks.length; // Check if the index is within the current plugin's hooks range if (index < totalHooksCount + hooksCount) { const hookIndex = index - totalHooksCount; return plugin.hooks[hookIndex]; } totalHooksCount += hooksCount; } // If the index is out of range, return null return null; } getUnappliedHooks() { return this.plugins.flatMap((plugin) => plugin.hooks).filter((hook) => !hook.applied); } } class ModkitPlugin { constructor(name, version, referencedAssemblies, runtime) { this.onLoaded = undefined; this._referencedAssemblies = []; this._hooks = []; this.name = name; this.version = version || "1.0.0"; this.logger = new _logger__WEBPACK_IMPORTED_MODULE_0__.Logger(name); this._referencedAssemblies = referencedAssemblies || []; this._runtime = runtime; } get hooks() { return this._hooks; } get referencedAssemblies() { return this._referencedAssemblies; } hookPrefix(target, callback) { return this.hook(target, callback, 0); } hookPostfix(target, callback) { return this.hook(target, callback, 1); } hook(target, callback, kind) { const hook = { typeName: target.typeName, methodName: target.methodName, params: target.params, returnType: target.returnType, applied: false, enabled: true, kind, callback, }; this._hooks.push(hook); return hook; } // public call(target: string, args: any[]): void; // public call(targetClass: string, targetMethod: string, args: any[]): void; call(target, targetMethodOrArgs, args) { // @ts-ignore const _game = window.unityInstance || game; const tableName = this._runtime.tableName || this._runtime.resolveTableName(_game.Module.asm); if (typeof targetMethodOrArgs === "string") { const tableIndex = this._runtime.getTableIndex(target, targetMethodOrArgs); if (tableIndex === -1) throw new Error(`Failed to invoke function! Could not find table index for ${target + "$$" + targetMethodOrArgs}`); if (args) args = args.map((arg) => (arg instanceof ValueWrapper ? arg.val() : arg)); const result = _game.Module.asm[tableName].get(tableIndex)(...args); return new ValueWrapper(result); } else if (typeof targetMethodOrArgs === "object" || typeof targetMethodOrArgs === "undefined") { const [typeName, methodName] = target.replace("::", "$$").split("$$"); const tableIndex = this._runtime.getTableIndex(typeName, methodName); if (tableIndex === -1) throw new Error(`Failed to invoke function! Could not find table index for ${typeName + "$$" + methodName}`); if (!targetMethodOrArgs) targetMethodOrArgs = []; targetMethodOrArgs = targetMethodOrArgs.map((arg) => (arg instanceof ValueWrapper ? arg.val() : arg)); const result = _game.Module.asm[tableName].get(tableIndex)(...targetMethodOrArgs); return new ValueWrapper(result); } } createObject(typeInfo) { return new ValueWrapper(this._runtime.createObject(typeInfo)); } createMstr(char) { // @ts-ignore const _game = window.unityInstance || game; const charArray = new TextEncoder().encode(char); const nullTerminatedArray = new Uint8Array(charArray.length + 1); nullTerminatedArray.set(charArray); nullTerminatedArray[charArray.length] = 0; const charAlloc = this._runtime.malloc(nullTerminatedArray.length); (0,_utils__WEBPACK_IMPORTED_MODULE_4__.writeUint8ArrayAtOffset)(_game.Module.HEAPU8, nullTerminatedArray, charAlloc); const res = this.call("System.Runtime.InteropServices.Marshal", "PtrToStringAnsi", [charAlloc]); return res; } slice(address, size = 256) { return this._runtime.memory(address, size); } malloc(size) { return new ValueWrapper(this._runtime.malloc(size)); } free(block) { this._runtime.free(block); } memcpy(dest, src, count) { // @ts-ignore const _game = window.unityInstance || game; (0,_utils__WEBPACK_IMPORTED_MODULE_4__.writeUint8ArrayAtOffset)(_game.Module.HEAPU8, this.slice(src, count), dest instanceof ValueWrapper ? dest.val() : dest); } } class ValueWrapper { constructor(result) { this._result = result; } set(value) { this._result = value instanceof ValueWrapper ? value.val() : value; } val() { return this._result; } mstr() { return ValueWrapper.readUtf16Char(this._result + 12); } deref() { var _a; const val = (_a = this.readField(0, "u32")) === null || _a === void 0 ? void 0 : _a.val(); // BUG: 0 is falsy here return val ? new ValueWrapper(val) : undefined; } getClassName() { try { // @ts-ignore const _game = window.unityInstance || game; const classPtr = new DataView(_game.Module.HEAPU8.slice(this._result, this._result + 4).buffer).getUint32(0, true); let classNamePtr = new DataView(_game.Module.HEAPU8.slice(classPtr + 8, classPtr + 12).buffer).getUint32(0, true); const classNameReader = new _utils_binary__WEBPACK_IMPORTED_MODULE_6__.BinaryReader(_game.Module.HEAPU8.slice(classNamePtr, classNamePtr + 128).buffer); return classNameReader.readNullTerminatedUTF8String(); } catch (_a) { return null; } } readField(offset, type) { // @ts-ignore const _game = window.unityInstance || game; const valAddress = this._result + offset; let valArray = _game.Module.HEAPU8.slice(valAddress, valAddress + 4); const reader = new _utils_binary__WEBPACK_IMPORTED_MODULE_6__.BinaryReader(valArray.buffer); switch (type) { case "i8": return new ValueWrapper(reader.readInt8()); case "i16": return new ValueWrapper(reader.readInt16()); case "i32": return new ValueWrapper(reader.readInt32()); case "f32": return new ValueWrapper(reader.readFloat()); case "u8": return new ValueWrapper(reader.readUint8()); case "u16": return new ValueWrapper(reader.readUint16()); case "u32": return new ValueWrapper(reader.readUint32()); } } writeField(offset, type, value) { // @ts-ignore const _game = window.unityInstance || game; let size = _extras__WEBPACK_IMPORTED_MODULE_7__.dataTypeSizes[type]; const writer = new _utils_binary__WEBPACK_IMPORTED_MODULE_6__.BinaryWriter(new ArrayBuffer(size)); if (value instanceof ValueWrapper) value = value.val(); switch (type) { case "i8": writer.writeInt8(value); break; case "i16": writer.writeInt16(value); break; case "i32": writer.writeInt32(value); break; case "f32": writer.writeFloat(value); break; case "u8": writer.writeUint8(value); break; case "u16": writer.writeUint16(value); break; case "u32": writer.writeUint32(value); break; } (0,_utils__WEBPACK_IMPORTED_MODULE_4__.writeUint8ArrayAtOffset)(_game.Module.HEAPU8, writer.finalize(), this._result + offset); } static readUtf16Char(ptr) { // @ts-ignore const _game = window.unityInstance || game; let buffer = new Uint16Array(_game.Module.HEAPU8.buffer); let offset = ptr / 2; // divide by 2 to convert from byte offset to character offset let subarray = []; let charCode = buffer[offset]; while (charCode !== 0) { subarray.push(charCode); offset++; charCode = buffer[offset]; } // subarray.pop(); // remove the weird stuff that happens at the end (hopefully works lol) let decoder = new TextDecoder("utf-16le"); return decoder.decode(new Uint16Array(subarray)); } } /***/ }), /***/ "./src/utils/binary/index.ts": /*!***********************************!*\ !*** ./src/utils/binary/index.ts ***! \***********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ BinaryReader: () => (/* binding */ BinaryReader), /* harmony export */ BinaryWriter: () => (/* binding */ BinaryWriter) /* harmony export */ }); class BinaryReader { constructor(arrayBuffer, littleEndian = true) { this._view = new DataView(arrayBuffer); this._buffer = arrayBuffer; this._offset = 0; this._littleEndian = littleEndian; this._utf8decoder = new TextDecoder("utf-8"); } get offset() { return this._offset; } get buffer() { return this._buffer; } seek(offset) { this._offset = offset; } readNullTerminatedUTF8String() { const startOffset = this._offset; while (this._view.getUint8(this._offset++) !== 0) { } const utf8String = this._utf8decoder.decode(this._view.buffer.slice(startOffset, this._offset - 1)); return utf8String; } readUTF8StringWithLength() { const stringLength = this.readUint32(); const utf8String = this._utf8decoder.decode(this._view.buffer.slice(this._offset, this._offset + stringLength)); this._offset += stringLength; return utf8String; } readUint8() { const value = this._view.getUint8(this._offset); this._offset++; return value; } readInt8() { const value = this._view.getInt8(this._offset); this._offset++; return value; } readUint16() { const value = this._view.getUint16(this._offset, this._littleEndian); this._offset += 2; return value; } readInt16() { const value = this._view.getInt16(this._offset, this._littleEndian); this._offset += 2; return value; } readInt32() { const value = this._view.getInt32(this._offset, this._littleEndian); this._offset += 4; return value; } readUint32() { const value = this._view.getUint32(this._offset, this._littleEndian); this._offset += 4; return value; } readIndex(size) { if (size === 1) return this.readUint8(); if (size === 2) return this.readUint16(); return this.readInt32(); // 4 } readFloat() { const value = this._view.getFloat32(this._offset, this._littleEndian); this._offset += 4; return value; } readULEB128() { let result = 0; let shift = 0; let byte; do { byte = this.readUint8(); result |= (byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); return result; } readUint8Array(length) { const slice = this.readSlice(this.offset, length); this._offset += length; return new Uint8Array(slice); } readSlice(offset, length) { return this._view.buffer.slice(offset, offset + length); } } class BinaryWriter { constructor(buffer, littleEndian = true) { this._view = new DataView(buffer); this._offset = 0; this._littleEndian = littleEndian; } seek(offset) { if (offset >= 0 && offset < this._view.byteLength) { this._offset = offset; } else { throw new Error("Invalid offset value."); } } writeUint8(value) { if (this._offset < this._view.byteLength) { this._view.setUint8(this._offset, value); this._offset += 1; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeInt8(value) { if (this._offset < this._view.byteLength) { this._view.setInt8(this._offset, value); this._offset += 1; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeUint16(value) { if (this._offset < this._view.byteLength) { this._view.setUint16(this._offset, value, this._littleEndian); this._offset += 2; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeInt16(value) { if (this._offset < this._view.byteLength) { this._view.setInt16(this._offset, value, this._littleEndian); this._offset += 2; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeInt32(value) { if (this._offset < this._view.byteLength) { this._view.setInt32(this._offset, value, this._littleEndian); this._offset += 4; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeUint32(value) { if (this._offset < this._view.byteLength) { this._view.setUint32(this._offset, value, this._littleEndian); this._offset += 4; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeFloat(value) { if (this._offset < this._view.byteLength) { this._view.setFloat32(this._offset, value, this._littleEndian); this._offset += 4; } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } writeBytes(bytes) { const bytesToWrite = new Uint8Array(bytes); const remainingSpace = this._view.byteLength - this._offset; const bytesToWriteLength = bytesToWrite.length; if (bytesToWriteLength <= remainingSpace) { for (let i = 0; i < bytesToWriteLength; i++) { this._view.setUint8(this._offset, bytesToWrite[i]); this._offset++; } } else { throw new Error("Buffer overflow: Cannot write beyond the ArrayBuffer length."); } } finalize() { return new Uint8Array(this._view.buffer); } } /***/ }), /***/ "./src/utils/index.ts": /*!****************************!*\ !*** ./src/utils/index.ts ***! \****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ bufToHex: () => (/* binding */ bufToHex), /* harmony export */ makeId: () => (/* binding */ makeId), /* harmony export */ patternSearch: () => (/* binding */ patternSearch), /* harmony export */ waitFor: () => (/* binding */ waitFor), /* harmony export */ writeUint8ArrayAtOffset: () => (/* binding */ writeUint8ArrayAtOffset) /* harmony export */ }); /* unused harmony exports concatenateUint8Arrays, uint8ArrayStartsWith */ function waitFor(conditionFunction) { return new Promise((resolve) => { const poll = () => { if (conditionFunction()) { resolve(); } else { setTimeout(poll, 400); } }; poll(); }); } function makeId(length) { let text = ""; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for (let i = 0; i < length; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } function patternSearch(mainArray, subArray) { const indexes = []; if (subArray.length === 0) return indexes; const lps = generateLPSArray(subArray); let i = 0; let j = 0; while (i < mainArray.length) { if (mainArray[i] === subArray[j]) { i++; j++; } if (j === subArray.length) { indexes.push(i - j); j = lps[j - 1]; } else if (i < mainArray.length && mainArray[i] !== subArray[j]) { if (j !== 0) { j = lps[j - 1]; } else { i++; } } } return indexes; } function concatenateUint8Arrays(arrays) { // Calculate the total length of the concatenated array let totalLength = 0; arrays.forEach((array) => { totalLength += array.length; }); // Create a new Uint8Array with the total length const concatenatedArray = new Uint8Array(totalLength); // Use the set() method to copy the contents of each Uint8Array into the concatenated array let offset = 0; arrays.forEach((array) => { concatenatedArray.set(array, offset); offset += array.length; }); return concatenatedArray; } function uint8ArrayStartsWith(array, expectedNumbers) { if (array.length < expectedNumbers.length) { return false; } for (let i = 0; i < expectedNumbers.length; i++) { if (array[i] !== expectedNumbers[i]) { return false; } } return true; } function writeUint8ArrayAtOffset(destination, source, offset) { if (offset + source.length > destination.length) { throw new Error("Source array does not fit at the specified offset in the destination array."); } for (let i = 0; i < source.length; i++) { destination[offset + i] = source[i]; } } function bufToHex(buffer) { return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, "0")).join(""); } function generateLPSArray(pattern) { const lps = []; lps[0] = 0; let len = 0; let i = 1; while (i < pattern.length) { if (pattern[i] === pattern[len]) { len++; lps[i] = len; i++; } else { if (len !== 0) { len = lps[len - 1]; } else { lps[i] = 0; i++; } } } return lps; } /***/ }), /***/ "./src/web-data/index.ts": /*!*******************************!*\ !*** ./src/web-data/index.ts ***! \*******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ WebData: () => (/* binding */ WebData) /* harmony export */ }); /* harmony import */ var _utils_binary__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/binary */ "./src/utils/binary/index.ts"); class WebData { constructor(buffer, resolvableNodes) { var _a; this.nodes = []; const reader = new _utils_binary__WEBPACK_IMPORTED_MODULE_0__.BinaryReader(buffer); this.signature = reader.readNullTerminatedUTF8String(); this.headLen = reader.readUint32(); while (reader.offset < this.headLen) { const node = { offset: reader.readUint32(), size: reader.readUint32(), name: reader.readUTF8StringWithLength(), }; const resolvableNode = resolvableNodes === null || resolvableNodes === void 0 ? void 0 : resolvableNodes.find((item) => item[0] === node.name); if (!resolvableNode) continue; node.size = (_a = resolvableNode[1]) !== null && _a !== void 0 ? _a : node.size; this.nodes.push(node); } for (const node of this.nodes) { node.data = reader.readSlice(node.offset, node.size); } this.resolveUnityVersion(reader); } getNode(name) { return this.nodes.find((n) => n.name === name); } resolveUnityVersion(reader) { const dataUnity3dNode = this.getNode("data.unity3d"); if (!dataUnity3dNode || !dataUnity3dNode.data) return; const dataUnity3dReader = new _utils_binary__WEBPACK_IMPORTED_MODULE_0__.BinaryReader(dataUnity3dNode.data); dataUnity3dReader.seek(18); this.unityVersion = dataUnity3dReader.readNullTerminatedUTF8String(); } } /***/ }), /***/ "./src/wail/index.js": /*!***************************!*\ !*** ./src/wail/index.js ***! \***************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ BufferReader: () => (/* binding */ BufferReader), /* harmony export */ OP_CALL: () => (/* binding */ OP_CALL), /* harmony export */ SECTION_CODE: () => (/* binding */ SECTION_CODE), /* harmony export */ SECTION_ELEMENT: () => (/* binding */ SECTION_ELEMENT), /* harmony export */ SECTION_TYPE: () => (/* binding */ SECTION_TYPE), /* harmony export */ VarUint32ToArray: () => (/* binding */ VarUint32ToArray), /* harmony export */ WailParser: () => (/* binding */ WailParser) /* harmony export */ }); /* unused harmony export WailVariable */ /** Copyright 2019 Jack Baker Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * @ignore */ const SECTION_CUSTOM = 0; const SECTION_TYPE = 1; const SECTION_IMPORT = 2; const SECTION_FUNCTION = 3; const SECTION_TABLE = 4; const SECTION_MEMORY = 5; const SECTION_GLOBAL = 6; const SECTION_EXPORT = 7; const SECTION_START = 8; const SECTION_ELEMENT = 9; const SECTION_CODE = 10; const SECTION_DATA = 11; const SECTION_DATACOUNT = 12; const SECTION_TAG = 13; const MAX_SECTION_ID = 13; const KIND_FUNC = 0x00; const KIND_TABLE = 0x01; const KIND_MEMORY = 0x02; const KIND_GLOBAL = 0x03; const KIND_TAG = 0x04; const kindStr = { func: KIND_FUNC, table: KIND_TABLE, memory: KIND_MEMORY, global: KIND_GLOBAL, tag: KIND_TAG, }; // inverse Object.entries(kindStr).forEach(([str, code]) => (kindStr[code] = str)); const convertKind = function (string) { const kindVal = kindStr[string]; if (typeof kindVal === "undefined") { throw new Error("Invalid kind " + string); } return kindVal; }; const VALUE_TYPE_I32 = 0x7f; const VALUE_TYPE_I64 = 0x7e; const VALUE_TYPE_F32 = 0x7d; const VALUE_TYPE_F64 = 0x7c; const VALUE_TYPE_ANYFUNC = 0x70; const VALUE_TYPE_FUNC = 0x60; const VALUE_TYPE_BLOCK = 0x40; const valueTypeStr = { i32: VALUE_TYPE_I32, i64: VALUE_TYPE_I64, f32: VALUE_TYPE_F32, f64: VALUE_TYPE_F64, anyfunc: VALUE_TYPE_ANYFUNC, func: VALUE_TYPE_FUNC, block: VALUE_TYPE_BLOCK, }; Object.entries(valueTypeStr).forEach(([str, code]) => (valueTypeStr[code] = str)); const convertValueType = function (string) { const typeVal = valueTypeStr[string]; if (typeof typeVal === "undefined") { throw new Error("Invalid value type " + string); } return typeVal; }; const OP_UNREACHABLE = 0x00; const OP_NOP = 0x01; const OP_BLOCK = 0x02; const OP_LOOP = 0x03; const OP_IF = 0x04; const OP_ELSE = 0x05; const OP_THROW = 0x08; const OP_THROW_REF = 0x0a; const OP_END = 0x0b; const OP_BR = 0x0c; const OP_BR_IF = 0x0d; const OP_BR_TABLE = 0x0e; const OP_RETURN = 0x0f; const OP_CALL = 0x10; const OP_CALL_INDIRECT = 0x11; const OP_DROP = 0x1a; const OP_SELECT = 0x1b; const OP_TRY_TABLE = 0x1f; const OP_GET_LOCAL = 0x20; const OP_SET_LOCAL = 0x21; const OP_TEE_LOCAL = 0x22; const OP_GET_GLOBAL = 0x23; const OP_SET_GLOBAL = 0x24; const OP_I32_LOAD = 0x28; const OP_I64_LOAD = 0x29; const OP_F32_LOAD = 0x2a; const OP_F64_LOAD = 0x2b; const OP_I32_LOAD8_S = 0x2c; const OP_I32_LOAD8_U = 0x2d; const OP_I32_LOAD16_S = 0x2e; const OP_I32_LOAD16_U = 0x2f; const OP_I64_LOAD8_S = 0x30; const OP_I64_LOAD8_U = 0x31; const OP_I64_LOAD16_S = 0x32; const OP_I64_LOAD16_U = 0x33; const OP_I64_LOAD32_S = 0x34; const OP_I64_LOAD32_U = 0x35; const OP_I32_STORE = 0x36; const OP_I64_STORE = 0x37; const OP_F32_STORE = 0x38; const OP_F64_STORE = 0x39; const OP_I32_STORE8 = 0x3a; const OP_I32_STORE16 = 0x3b; const OP_I64_STORE8 = 0x3c; const OP_I64_STORE16 = 0x3d; const OP_I64_STORE32 = 0x3e; const OP_MEMORY_SIZE = 0x3f; const OP_MEMORY_GROW = 0x40; const OP_I32_CONST = 0x41; const OP_I64_CONST = 0x42; const OP_F32_CONST = 0x43; const OP_F64_CONST = 0x44; const OP_I32_EQZ = 0x45; const OP_I32_EQ = 0x46; const OP_I32_NE = 0x47; const OP_I32_LT_S = 0x48; const OP_I32_LT_U = 0x49; const OP_I32_GT_S = 0x4a; const OP_I32_GT_U = 0x4b; const OP_I32_LE_S = 0x4c; const OP_I32_LE_U = 0x4d; const OP_I32_GE_S = 0x4e; const OP_I32_GE_U = 0x4f; const OP_I64_EQZ = 0x50; const OP_I64_EQ = 0x51; const OP_I64_NE = 0x52; const OP_I64_LT_S = 0x53; const OP_I64_LT_U = 0x54; const OP_I64_GT_S = 0x55; const OP_I64_GT_U = 0x56; const OP_I64_LE_S = 0x57; const OP_I64_LE_U = 0x58; const OP_I64_GE_S = 0x59; const OP_I64_GE_U = 0x5a; const OP_F32_EQ = 0x5b; const OP_F32_NE = 0x5c; const OP_F32_LT = 0x5d; const OP_F32_GT = 0x5e; const OP_F32_LE = 0x5f; const OP_F32_GE = 0x60; const OP_F64_EQ = 0x61; const OP_F64_NE = 0x62; const OP_F64_LT = 0x63; const OP_F64_GT = 0x64; const OP_F64_LE = 0x65; const OP_F64_GE = 0x66; const OP_I32_CLZ = 0x67; const OP_I32_CTZ = 0x68; const OP_I32_POPCNT = 0x69; const OP_I32_ADD = 0x6a; const OP_I32_SUB = 0x6b; const OP_I32_MUL = 0x6c; const OP_I32_DIV_S = 0x6d; const OP_I32_DIV_U = 0x6e; const OP_I32_REM_S = 0x6f; const OP_I32_REM_U = 0x70; const OP_I32_AND = 0x71; const OP_I32_OR = 0x72; const OP_I32_XOR = 0x73; const OP_I32_SHL = 0x74; const OP_I32_SHR_S = 0x75; const OP_I32_SHR_U = 0x76; const OP_I32_ROTL = 0x77; const OP_I32_ROTR = 0x78; const OP_I64_CLZ = 0x79; const OP_I64_CTZ = 0x7a; const OP_I64_POPCNT = 0x7b; const OP_I64_ADD = 0x7c; const OP_I64_SUB = 0x7d; const OP_I64_MUL = 0x7e; const OP_I64_DIV_S = 0x7f; const OP_I64_DIV_U = 0x80; const OP_I64_REM_S = 0x81; const OP_I64_REM_U = 0x82; const OP_I64_AND = 0x83; const OP_I64_OR = 0x84; const OP_I64_XOR = 0x85; const OP_I64_SHL = 0x86; const OP_I64_SHR_S = 0x87; const OP_I64_SHR_U = 0x88; const OP_I64_ROTL = 0x89; const OP_I64_ROTR = 0x8a; const OP_F32_ABS = 0x8b; const OP_F32_NEG = 0x8c; const OP_F32_CEIL = 0x8d; const OP_F32_FLOOR = 0x8e; const OP_F32_TRUNC = 0x8f; const OP_F32_NEAREST = 0x90; const OP_F32_SQRT = 0x91; const OP_F32_ADD = 0x92; const OP_F32_SUB = 0x93; const OP_F32_MUL = 0x94; const OP_F32_DIV = 0x95; const OP_F32_MIN = 0x96; const OP_F32_MAX = 0x97; const OP_F32_COPYSIGN = 0x98; const OP_F64_ABS = 0x99; const OP_F64_NEG = 0x9a; const OP_F64_CEIL = 0x9b; const OP_F64_FLOOR = 0x9c; const OP_F64_TRUNC = 0x9d; const OP_F64_NEAREST = 0x9e; const OP_F64_SQRT = 0x9f; const OP_F64_ADD = 0xa0; const OP_F64_SUB = 0xa1; const OP_F64_MUL = 0xa2; const OP_F64_DIV = 0xa3; const OP_F64_MIN = 0xa4; const OP_F64_MAX = 0xa5; const OP_F64_COPYSIGN = 0xa6; const OP_I32_WRAP_I64 = 0xa7; const OP_I32_TRUNC_S_F32 = 0xa8; const OP_I32_TRUNC_U_F32 = 0xa9; const OP_I32_TRUNC_S_F64 = 0xaa; const OP_I32_TRUNC_U_F64 = 0xab; const OP_I64_EXTEND_S_I32 = 0xac; const OP_I64_EXTEND_U_I32 = 0xad; const OP_I64_TRUNC_S_F32 = 0xae; const OP_I64_TRUNC_U_F32 = 0xaf; const OP_I64_TRUNC_S_F64 = 0xb0; const OP_I64_TRUNC_U_F64 = 0xb1; const OP_F32_CONVERT_S_I32 = 0xb2; const OP_F32_CONVERT_U_I32 = 0xb3; const OP_F32_CONVERT_S_I64 = 0xb4; const OP_F32_CONVERT_U_I64 = 0xb5; const OP_F32_DEMOTE_F64 = 0xb6; const OP_F64_CONVERT_S_I32 = 0xb7; const OP_F64_CONVERT_U_I32 = 0xb8; const OP_F64_CONVERT_S_I64 = 0xb9; const OP_F64_CONVERT_U_I64 = 0xba; const OP_F64_PROMOTE_F32 = 0xbb; const OP_I32_REINTERPRET_F32 = 0xbc; const OP_I64_REINTERPRET_F64 = 0xbd; const OP_F32_REINTERPRET_I32 = 0xbe; const OP_F64_REINTERPRET_I64 = 0xbf; const OP_I32_EXTEND8_S = 0xc0; const OP_I32_EXTEND16_S = 0xc1; const OP_I64_EXTEND8_S = 0xc2; const OP_I64_EXTEND16_S = 0xc3; const OP_I64_EXTEND32_S = 0xc4; const OP_BULK_MEMORY = 0xfc; const OP_SIMD = 0xfd; const OP_ATOMIC = 0xfe; const ARG_MEMORY_INIT = 0x08; const ARG_DATA_DROP = 0x09; const ARG_MEMORY_COPY = 0x0a; const ARG_MEMORY_FILL = 0x0b; const ARG_TABLE_INIT = 0x0c; const ARG_ELEM_DROP = 0x0d; const ARG_TABLE_COPY = 0x0e; const SIMD_V128_LOAD = 0x00; const SIMD_V128_LOAD8X8_S = 0x01; const SIMD_V128_LOAD8X8_U = 0x02; const SIMD_V128_LOAD16X4_S = 0x03; const SIMD_V128_LOAD16X4_U = 0x04; const SIMD_V128_LOAD32X2_S = 0x05; const SIMD_V128_LOAD32X2_U = 0x06; const SIMD_V128_LOAD8_SPLAT = 0x07; const SIMD_V128_LOAD16_SPLAT = 0x08; const SIMD_V128_LOAD32_SPLAT = 0x09; const SIMD_V128_LOAD64_SPLAT = 0x0a; const SIMD_V128_STORE = 0x0b; const SIMD_V128_CONST = 0x0c; const SIMD_I8X16_SHUFFLE = 0x0d; const SIMD_I8X16_SWIZZLE = 0x0e; const SIMD_I8X16_SPLAT = 0x0f; const SIMD_I16X8_SPLAT = 0x10; const SIMD_I32X4_SPLAT = 0x11; const SIMD_I64X2_SPLAT = 0x12; const SIMD_F32X4_SPLAT = 0x13; const SIMD_F64X2_SPLAT = 0x14; const SIMD_I8X16_EXTRACT_LANE_S = 0x15; const SIMD_I8X16_EXTRACT_LANE_U = 0x16; const SIMD_I8X16_REPLACE_LANE = 0x17; const SIMD_I16X8_EXTRACT_LANE_S = 0x18; const SIMD_I16X8_EXTRACT_LANE_U = 0x19; const SIMD_I16X8_REPLACE_LANE = 0x1a; const SIMD_I32X4_EXTRACT_LANE = 0x1b; const SIMD_I32X4_REPLACE_LANE = 0x1c; const SIMD_I64X2_EXTRACT_LANE = 0x1d; const SIMD_I64X2_REPLACE_LANE = 0x1e; const SIMD_F32X4_EXTRACT_LANE = 0x1f; const SIMD_F32X4_REPLACE_LANE = 0x20; const SIMD_F64X2_EXTRACT_LANE = 0x21; const SIMD_F64X2_REPLACE_LANE = 0x22; const SIMD_I8X16_EQ = 0x23; const SIMD_I8X16_NE = 0x24; const SIMD_I8X16_LT_S = 0x25; const SIMD_I8X16_LT_U = 0x26; const SIMD_I8X16_GT_S = 0x27; const SIMD_I8X16_GT_U = 0x28; const SIMD_I8X16_LE_S = 0x29; const SIMD_I8X16_LE_U = 0x2a; const SIMD_I8X16_GE_S = 0x2b; const SIMD_I8X16_GE_U = 0x2c; const SIMD_I16X8_EQ = 0x2d; const SIMD_I16X8_NE = 0x2e; const SIMD_I16X8_LT_S = 0x2f; const SIMD_I16X8_LT_U = 0x30; const SIMD_I16X8_GT_S = 0x31; const SIMD_I16X8_GT_U = 0x32; const SIMD_I16X8_LE_S = 0x33; const SIMD_I16X8_LE_U = 0x34; const SIMD_I16X8_GE_S = 0x35; const SIMD_I16X8_GE_U = 0x36; const SIMD_I32X4_EQ = 0x37; const SIMD_I32X4_NE = 0x38; const SIMD_I32X4_LT_S = 0x39; const SIMD_I32X4_LT_U = 0x3a; const SIMD_I32X4_GT_S = 0x3b; const SIMD_I32X4_GT_U = 0x3c; const SIMD_I32X4_LE_S = 0x3d; const SIMD_I32X4_LE_U = 0x3e; const SIMD_I32X4_GE_S = 0x3f; const SIMD_I32X4_GE_U = 0x40; const SIMD_F32X4_EQ = 0x41; const SIMD_F32X4_NE = 0x42; const SIMD_F32X4_LT = 0x43; const SIMD_F32X4_GT = 0x44; const SIMD_F32X4_LE = 0x45; const SIMD_F32X4_GE = 0x46; const SIMD_F64X2_EQ = 0x47; const SIMD_F64X2_NE = 0x48; const SIMD_F64X2_LT = 0x49; const SIMD_F64X2_GT = 0x4a; const SIMD_F64X2_LE = 0x4b; const SIMD_F64X2_GE = 0x4c; const SIMD_V128_NOT = 0x4d; const SIMD_V128_AND = 0x4e; const SIMD_V128_ANDNOT = 0x4f; const SIMD_V128_OR = 0x50; const SIMD_V128_XOR = 0x51; const SIMD_V128_BITSELECT = 0x52; const SIMD_I8X16_ABS = 0x60; const SIMD_I8X16_NEG = 0x61; const SIMD_I8X16_ALL_TRUE = 0x63; const SIMD_I8X16_BITMASK = 0x64; const SIMD_I8X16_NARROW_I16X8_S = 0x65; const SIMD_I8X16_NARROW_I16X8_U = 0x66; const SIMD_I8X16_SHL = 0x6b; const SIMD_I8X16_SHR_S = 0x6c; const SIMD_I8X16_SHR_U = 0x6d; const SIMD_I8X16_ADD = 0x6e; const SIMD_I8X16_ADD_SAT_S = 0x6f; const SIMD_I8X16_ADD_SAT_U = 0x70; const SIMD_I8X16_SUB = 0x71; const SIMD_I8X16_SUB_SAT_S = 0x72; const SIMD_I8X16_SUB_SAT_U = 0x73; const SIMD_I8X16_MIN_S = 0x76; const SIMD_I8X16_MIN_U = 0x77; const SIMD_I8X16_MAX_S = 0x78; const SIMD_I8X16_MAX_U = 0x79; const SIMD_I8X16_AVGR_U = 0x7b; const SIMD_I16X8_ABS = 0x80; const SIMD_I16X8_NEG = 0x81; const SIMD_I16X8_ALL_TRUE = 0x83; const SIMD_I16X8_BITMASK = 0x84; const SIMD_I16X8_NARROW_I32X4_S = 0x85; const SIMD_I16X8_NARROW_I32X4_U = 0x86; const SIMD_I16X8_EXTEND_LOW_I8X16_S = 0x87; const SIMD_I16X8_EXTEND_HIGH_I8X16_S = 0x88; const SIMD_I16X8_EXTEND_LOW_I8X16_U = 0x89; const SIMD_I16X8_EXTEND_HIGH_I8X16_U = 0x8a; const SIMD_I16X8_SHL = 0x8b; const SIMD_I16X8_SHR_S = 0x8c; const SIMD_I16X8_SHR_U = 0x8d; const SIMD_I16X8_ADD = 0x8e; const SIMD_I16X8_ADD_SAT_S = 0x8f; const SIMD_I16X8_ADD_SAT_U = 0x90; const SIMD_I16X8_SUB = 0x91; const SIMD_I16X8_SUB_SAT_S = 0x92; const SIMD_I16X8_SUB_SAT_U = 0x93; const SIMD_I16X8_MUL = 0x95; const SIMD_I16X8_MIN_S = 0x96; const SIMD_I16X8_MIN_U = 0x97; const SIMD_I16X8_MAX_S = 0x98; const SIMD_I16X8_MAX_U = 0x99; const SIMD_I16X8_AVGR_U = 0x9b; const SIMD_I32X4_ABS = 0xa0; const SIMD_I32X4_NEG = 0xa1; const SIMD_I32X4_ALL_TRUE = 0xa3; const SIMD_I32X4_BITMASK = 0xa4; const SIMD_I32X4_EXTEND_LOW_I16X8_S = 0xa7; const SIMD_I32X4_EXTEND_HIGH_I16X8_S = 0xa8; const SIMD_I32X4_EXTEND_LOW_I16X8_U = 0xa9; const SIMD_I32X4_EXTEND_HIGH_I16X8_U = 0xaa; const SIMD_I32X4_SHL = 0xab; const SIMD_I32X4_SHR_S = 0xac; const SIMD_I32X4_SHR_U = 0xad; const SIMD_I32X4_ADD = 0xae; const SIMD_I32X4_SUB = 0xb1; const SIMD_I32X4_MUL = 0xb5; const SIMD_I32X4_MIN_S = 0xb6; const SIMD_I32X4_MIN_U = 0xb7; const SIMD_I32X4_MAX_S = 0xb8; const SIMD_I32X4_MAX_U = 0xb9; const SIMD_I32X4_DOT_I16X8_S = 0xba; const SIMD_I64X2_ABS = 0xc0; const SIMD_I64X2_NEG = 0xc1; const SIMD_I64X2_BITMASK = 0xc4; const SIMD_I64X2_EXTEND_LOW_I32X4_S = 0xc7; const SIMD_I64X2_EXTEND_HIGH_I32X4_S = 0xc8; const SIMD_I64X2_EXTEND_LOW_I32X4_U = 0xc9; const SIMD_I64X2_EXTEND_HIGH_I32X4_U = 0xca; const SIMD_I64X2_SHL = 0xcb; const SIMD_I64X2_SHR_S = 0xcc; const SIMD_I64X2_SHR_U = 0xcd; const SIMD_I64X2_ADD = 0xce; const SIMD_I64X2_SUB = 0xd1; const SIMD_I64X2_MUL = 0xd5; const SIMD_F32X4_CEIL = 0x67; const SIMD_F32X4_FLOOR = 0x68; const SIMD_F32X4_TRUNC = 0x69; const SIMD_F32X4_NEAREST = 0x6a; const SIMD_F64X2_CEIL = 0x74; const SIMD_F64X2_FLOOR = 0x75; const SIMD_F64X2_TRUNC = 0x7a; const SIMD_F64X2_NEAREST = 0x94; const SIMD_F32X4_ABS = 0xe0; const SIMD_F32X4_NEG = 0xe1; const SIMD_F32X4_SQRT = 0xe3; const SIMD_F32X4_ADD = 0xe4; const SIMD_F32X4_SUB = 0xe5; const SIMD_F32X4_MUL = 0xe6; const SIMD_F32X4_DIV = 0xe7; const SIMD_F32X4_MIN = 0xe8; const SIMD_F32X4_MAX = 0xe9; const SIMD_F32X4_PMIN = 0xea; const SIMD_F32X4_PMAX = 0xeb; const SIMD_F64X2_ABS = 0xec; const SIMD_F64X2_NEG = 0xed; const SIMD_F64X2_SQRT = 0xef; const SIMD_F64X2_ADD = 0xf0; const SIMD_F64X2_SUB = 0xf1; const SIMD_F64X2_MUL = 0xf2; const SIMD_F64X2_DIV = 0xf3; const SIMD_F64X2_MIN = 0xf4; const SIMD_F64X2_MAX = 0xf5; const SIMD_F64X2_PMIN = 0xf6; const SIMD_F64X2_PMAX = 0xf7; const SIMD_I32X4_TRUNC_SAT_F32X4_S = 0xf8; const SIMD_I32X4_TRUNC_SAT_F32X4_U = 0xf9; const SIMD_F32X4_CONVERT_I32X4_S = 0xfa; const SIMD_F32X4_CONVERT_I32X4_U = 0xfb; const SIMD_V128_LOAD32_ZERO = 0x5c; const SIMD_V128_LOAD64_ZERO = 0x5d; const SIMD_I16X8_EXTMUL_LOW_I8X16_S = 0x9c; const SIMD_I16X8_EXTMUL_HIGH_I8X16_S = 0x9d; const SIMD_I16X8_EXTMUL_LOW_I8X16_U = 0x9e; const SIMD_I16X8_EXTMUL_HIGH_I8X16_U = 0x9f; const SIMD_I32X4_EXTMUL_LOW_I16X8_S = 0xbc; const SIMD_I32X4_EXTMUL_HIGH_I16X8_S = 0xbd; const SIMD_I32X4_EXTMUL_LOW_I16X8_U = 0xbe; const SIMD_I32X4_EXTMUL_HIGH_I16X8_U = 0xbf; const SIMD_I64X2_EXTMUL_LOW_I32X4_S = 0xdc; const SIMD_I64X2_EXTMUL_HIGH_I32X4_S = 0xdd; const SIMD_I64X2_EXTMUL_LOW_I32X4_U = 0xde; const SIMD_I64X2_EXTMUL_HIGH_I32X4_U = 0xdf; const SIMD_I16X8_Q15MULR_SAT_S = 0x82; const SIMD_V128_ANY_TRUE = 0x53; const SIMD_V128_LOAD8_LANE = 0x54; const SIMD_V128_LOAD16_LANE = 0x55; const SIMD_V128_LOAD32_LANE = 0x56; const SIMD_V128_LOAD64_LANE = 0x57; const SIMD_V128_STORE8_LANE = 0x58; const SIMD_V128_STORE16_LANE = 0x59; const SIMD_V128_STORE32_LANE = 0x5a; const SIMD_V128_STORE64_LANE = 0x5b; const SIMD_I64X2_EQ = 0xd6; const SIMD_I64X2_NE = 0xd7; const SIMD_I64X2_LT_S = 0xd8; const SIMD_I64X2_GT_S = 0xd9; const SIMD_I64X2_LE_S = 0xda; const SIMD_I64X2_GE_S = 0xdb; const SIMD_I64X2_ALL_TRUE = 0xc3; const SIMD_F64X2_CONVERT_LOW_I32X4_S = 0xfe; const SIMD_F64X2_CONVERT_LOW_I32X4_U = 0xff; const SIMD_I32X4_TRUNC_SAT_F64X2_S_ZERO = 0xfc; const SIMD_I32X4_TRUNC_SAT_F64X2_U_ZERO = 0xfd; const SIMD_F32X4_DEMOTE_F64X2_ZERO = 0x5e; const SIMD_F64X2_PROMOTE_LOW_F32X4 = 0x5f; const SIMD_I8X16_POPCNT = 0x62; const SIMD_I16X8_EXTADD_PAIRWISE_I8X16_S = 0x7c; const SIMD_I16X8_EXTADD_PAIRWISE_I8X16_U = 0x7d; const SIMD_I32X4_EXTADD_PAIRWISE_I16X8_S = 0x7e; const SIMD_I32X4_EXTADD_PAIRWISE_I16X8_U = 0x7f; const ARG_ATOMIC_WAKE = 0x00; const ARG_I32_ATOMIC_WAIT = 0x01; const ARG_I64_ATOMIC_WAIT = 0x02; const ARG_I32_ATOMIC_LOAD = 0x10; const ARG_I64_ATOMIC_LOAD = 0x11; const ARG_I32_ATOMIC_LOAD_8U = 0x12; const ARG_I32_ATOMIC_LOAD_16U = 0x13; const ARG_I64_ATOMIC_LOAD_8U = 0x14; const ARG_I64_ATOMIC_LOAD_16U = 0x15; const ARG_I64_ATOMIC_LOAD_32U = 0x16; const ARG_I32_ATOMIC_STORE = 0x17; const ARG_I64_ATOMIC_STORE = 0x18; const ARG_I32_ATOMIC_STORE_8 = 0x19; const ARG_I32_ATOMIC_STORE_16 = 0x1a; const ARG_I64_ATOMIC_STORE_8 = 0x1b; const ARG_I64_ATOMIC_STORE_16 = 0x1c; const ARG_I64_ATOMIC_STORE_32 = 0x1d; const ARG_I32_ATOMIC_RMW_ADD = 0x1e; const ARG_I64_ATOMIC_RMW_ADD = 0x1f; const ARG_I32_ATOMIC_RMW_ADD_8U = 0x20; const ARG_I32_ATOMIC_RMW_ADD_16U = 0x21; const ARG_I64_ATOMIC_RMW_ADD_8U = 0x22; const ARG_I64_ATOMIC_RMW_ADD_16U = 0x23; const ARG_I64_ATOMIC_RMW_ADD_32U = 0x24; const ARG_I32_ATOMIC_RMW_SUB = 0x25; const ARG_I64_ATOMIC_RMW_SUB = 0x26; const ARG_I32_ATOMIC_RMW_SUB_8U = 0x27; const ARG_I32_ATOMIC_RMW_SUB_16U = 0x28; const ARG_I64_ATOMIC_RMW_SUB_8U = 0x29; const ARG_I64_ATOMIC_RMW_SUB_16U = 0x2a; const ARG_I64_ATOMIC_RMW_SUB_32U = 0x2b; const ARG_I32_ATOMIC_RMW_AND = 0x2c; const ARG_I64_ATOMIC_RMW_AND = 0x2d; const ARG_I32_ATOMIC_RMW_AND_8U = 0x2e; const ARG_I32_ATOMIC_RMW_AND_16U = 0x2f; const ARG_I64_ATOMIC_RMW_AND_8U = 0x30; const ARG_I64_ATOMIC_RMW_AND_16U = 0x31; const ARG_I64_ATOMIC_RMW_AND_32U = 0x32; const ARG_I32_ATOMIC_RMW_OR = 0x33; const ARG_I64_ATOMIC_RMW_OR = 0x34; const ARG_I32_ATOMIC_RMW_OR_8U = 0x35; const ARG_I32_ATOMIC_RMW_OR_16U = 0x36; const ARG_I64_ATOMIC_RMW_OR_8U = 0x37; const ARG_I64_ATOMIC_RMW_OR_16U = 0x38; const ARG_I64_ATOMIC_RMW_OR_32U = 0x39; const ARG_I32_ATOMIC_RMW_XOR = 0x3a; const ARG_I64_ATOMIC_RMW_XOR = 0x3b; const ARG_I32_ATOMIC_RMW_XOR_8U = 0x3c; const ARG_I32_ATOMIC_RMW_XOR_16U = 0x3d; const ARG_I64_ATOMIC_RMW_XOR_8U = 0x3e; const ARG_I64_ATOMIC_RMW_XOR_16U = 0x3f; const ARG_I64_ATOMIC_RMW_XOR_32U = 0x40; const ARG_I32_ATOMIC_RMW_XCHG = 0x41; const ARG_I64_ATOMIC_RMW_XCHG = 0x42; const ARG_I32_ATOMIC_RMW_XCHG_8U = 0x43; const ARG_I32_ATOMIC_RMW_XCHG_16U = 0x44; const ARG_I64_ATOMIC_RMW_XCHG_8U = 0x45; const ARG_I64_ATOMIC_RMW_XCHG_16U = 0x46; const ARG_I64_ATOMIC_RMW_XCHG_32U = 0x47; const ARG_I32_ATOMIC_RMW_CMPXCHG = 0x48; const ARG_I64_ATOMIC_RMW_CMPXCHG = 0x49; const ARG_I32_ATOMIC_RMW_CMPXCHG_8U = 0x4a; const ARG_I32_ATOMIC_RMW_CMPXCHG_16U = 0x4b; const ARG_I64_ATOMIC_RMW_CMPXCHG_8U = 0x4c; const ARG_I64_ATOMIC_RMW_CMPXCHG_16U = 0x4d; const ARG_I64_ATOMIC_RMW_CMPXCHG_32U = 0x4e; const convertOpcode = function (string) { const opcodeVal = opcodeStr[string]; if (typeof opcodeVal === "undefined") { throw new Error("Invalid opcode " + string); } return opcodeVal; }; const convertOpcodeArray = function (opcodeArray) { const result = []; for (let i = 0; i < opcodeArray.length; i++) { const thisElement = opcodeArray[i]; let convertedElement = thisElement; if (typeof thisElement === "string") { convertedElement = convertOpcode(thisElement); } result.push(convertedElement); } return result; }; const Uint8ToArray = function (x) { return [x & 0xff]; }; const Uint32ToArray = function (x) { return [x & 0x000000ff, (x & 0x0000ff00) >> 8, (x & 0x00ff0000) >> 16, (x & 0xff000000) >> 24]; }; const Uint64ToArray = function (x) { return [ x & 0x00000000000000ff, (x & 0x000000000000ff00) >> 8, (x & 0x0000000000ff0000) >> 16, (x & 0x00000000ff000000) >> 24, (x & 0x000000ff00000000) >> 32, (x & 0x0000ff0000000000) >> 40, (x & 0x00ff000000000000) >> 48, (x & 0xff00000000000000) >> 56, ]; }; const VarUint32ToArray = function (x) { const result = []; let current = x; if (x == 0) { return [0]; } while (current > 0) { let thisByte = current & 0x7f; current >>= 7; if (current) { thisByte |= 0x80; } result.push(thisByte); } return result; }; const VarSint32ToArray = function (x) { const result = []; let current = x; while (1) { thisByte = current & 0x7f; current >>= 7; if (current == -1 && thisByte & 0x40) { result.push(thisByte); break; } else if (current == 0 && !(thisByte & 0x40)) { result.push(thisByte); break; } else { thisByte |= 0x80; result.push(thisByte); } } return result; }; // From https://stackoverflow.com/questions/16893817/javascript-ascii-string-to-hex-byte-array const stringToByteArray = function (str) { return str.split("").map(function (c) { return c.charCodeAt(0); }); }; const VarUint32 = function (value) { if (typeof value == "number") { return VarUint32ToArray(value); } else if (value instanceof WailVariable) { return value.varUint32(); } else { // TODO Handle error } }; // WailVariable is the base class representing values that will be resolved while parsing. // Users can dictate the particular binary representation of a WailVariable by using // the type methods (i32(), f32(), etc) // If a representation is not explicitly selected, Wail will select a representation // contextually if possible, or throw an exception if not class WailVariable { constructor() { this._value = null; } get value() { if (this._value === null) { throw new Error("Attempted to resolve WailVariable before set"); } return this._value; } set value(newValue) { this._value = newValue; } i32() { if (this._value !== null) { return this.value; } return new WailI32(this); } f32() { if (this._value !== null) { const f32Array = new Float32Array([this._value]); return new Uint8Array(f32Array.buffer); } return new WailF32(this); } i64() { if (this._value !== null) { return this.value; } return new WailI64(this); } f64() { if (this._value !== null) { const f64Array = new Float64Array([this._value]); return new Uint8Array(f64Array.buffer); } return new WailF64(this); } varUint32() { if (this._value !== null) { return VarUint32(this.value); } return new WailVarUint32(this); } } class TypedWailVariable { constructor(parentVariable) { this._parent = parentVariable; } } class WailI32 extends TypedWailVariable { get value() { return Uint32ToArray(this._parent.value); } } class WailF32 extends TypedWailVariable { get value() { // TODO Fix return Uint32ToArray(this._parent.value); } } class WailI64 extends TypedWailVariable { get value() { return Uint64ToArray(this._parent.value); } } class WailF64 extends TypedWailVariable { get value() { // TODO Fix return Uint64ToArray(this._parent.value); } } class WailVarUint32 extends TypedWailVariable { get value() { return VarUint32ToArray(this._parent.value); } } const BufferReader = class { constructor(buffer) { this.inBuffer = null; this.outBuffer = null; if (typeof buffer !== "undefined") { this.inBuffer = new Uint8Array(buffer); // Allocate an ArrayBuffer larger than original ArrayBuffer // We'll create a larger array later if necessary this.outBuffer = new Uint8Array(buffer.length * 2); } else { this.outBuffer = new Uint8Array(1); } this.inPos = 0; this._copyPos = 0; this.outPos = 0; this._anchor = null; } load(buffer) { this.inBuffer = new Uint8Array(buffer); this.outBuffer = new Uint8Array(this.inBuffer.length * 2); } resize() { if (this.outBuffer.length == 0) { throw new Error("Attempted to resize 0-length buffer"); } const newBuffer = new Uint8Array(Math.ceil(this.outBuffer.length * 1.25)); for (let i = 0; i < this.outPos; i++) { newBuffer[i] = this.outBuffer[i]; } this.outBuffer = newBuffer; } readUint8() { return this.inBuffer[this.inPos++]; } readUint32() { const b1 = this.inBuffer[this.inPos++]; const b2 = this.inBuffer[this.inPos++]; const b3 = this.inBuffer[this.inPos++]; const b4 = this.inBuffer[this.inPos++]; return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); } readVarUint32() { let result = 0; let shift = 0; let currentByte; do { currentByte = this.readUint8(); result |= (currentByte & 0x7f) << shift; shift += 7; } while (currentByte & 0x80); return result; } readUint64() { const b1 = this.inBuffer[this.inPos++]; const b2 = this.inBuffer[this.inPos++]; const b3 = this.inBuffer[this.inPos++]; const b4 = this.inBuffer[this.inPos++]; const b5 = this.inBuffer[this.inPos++]; const b6 = this.inBuffer[this.inPos++]; const b7 = this.inBuffer[this.inPos++]; const b8 = this.inBuffer[this.inPos++]; return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); } readUint128() { const b1 = this.inBuffer[this.inPos++]; const b2 = this.inBuffer[this.inPos++]; const b3 = this.inBuffer[this.inPos++]; const b4 = this.inBuffer[this.inPos++]; const b5 = this.inBuffer[this.inPos++]; const b6 = this.inBuffer[this.inPos++]; const b7 = this.inBuffer[this.inPos++]; const b8 = this.inBuffer[this.inPos++]; const b9 = this.inBuffer[this.inPos++]; const b10 = this.inBuffer[this.inPos++]; const b11 = this.inBuffer[this.inPos++]; const b12 = this.inBuffer[this.inPos++]; const b13 = this.inBuffer[this.inPos++]; const b14 = this.inBuffer[this.inPos++]; const b15 = this.inBuffer[this.inPos++]; const b16 = this.inBuffer[this.inPos++]; return ( b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56) | (b9 << 64) | (b10 << 72) | (b11 << 80) | (b12 << 88) | (b13 << 96) | (b14 << 104) | (b15 << 112) | (b16 << 120) ); } readBytes(length) { const result = new Uint8Array(length); for (let i = 0; i < length; i++) { result[i] = this.inBuffer[this.inPos++]; } return result; } copyBuffer(buffer) { while (buffer.length + this.outPos > this.outBuffer.length) { this.resize(); } for (let i = 0; i < buffer.length; i++, this.outPos++) { this.outBuffer[this.outPos] = buffer[i]; } this.updateCopyPosition(); } commitBytes() { while (this.inPos - this._copyPos + this.outPos > this.outBuffer.length) { this.resize(); } for (; this._copyPos < this.inPos; this._copyPos++, this.outPos++) { this.outBuffer[this.outPos] = this.inBuffer[this._copyPos]; } } updateCopyPosition() { this._copyPos = this.inPos; } setAnchor() { this._anchor = this.outPos; } readFromAnchor() { return this.outBuffer.slice(this._anchor, this.outPos); } writeAtAnchor(buffer) { while (buffer.length + this.outPos > this.outBuffer.length) { this.resize(); } for (let i = 0; i < buffer.length; i++) { this.outBuffer[this._anchor + i] = buffer[i]; } this.outPos = this._anchor + buffer.length; } write() { return this.outBuffer.slice(0, this.outPos); } }; class WailParser extends BufferReader { constructor(bufferSource) { super(bufferSource); this._finished = false; this._newSections = []; this._removeSectionIds = []; this._resolvedTables = false; // We need to keep track of how many imported functions there are to // properly rebuild function table this._importFuncCount = 0; this._importFuncNewCount = 0; // Same logic as with imported functions this._importGlobalCount = 0; this._importGlobalNewCount = 0; this._globalImportCallback = null; this._importCallbacks = []; this._globalExportCallback = null; this._exportCallbacks = []; this._globalFunctionCallback = null; this._functionCallbacks = []; this._globalInstructionCallback = null; this._instructionCallbacks = {}; this._sectionOptions = {}; // Each section has three sets of parameters that can be set before parsing begins // "newEntries" includes newly created section entries // "existingExtries" includes parameters used to modify existing entries // "pending" includes variables that require info from this section to resolve for (let i = 0; i <= MAX_SECTION_ID; i++) { this._sectionOptions[i] = { newEntries: [], existingEntries: [], pending: [], }; } // To keep parsing as minimal as possible, we keep two masks of sections the user // has requested we parse // // The first defines sections that the binary must have. If the binary does not already // have this section, we will add it // // The second defines sections that should be parsed if they exist. There is no reason // to add these new sections if they do not exist this._requiredSectionFlags = 0; this._optionalSectionFlags = 0; this._parsedSections = 0; this.__variables = []; } parse() { // Read magic and version in one operation if possible const magic = this.readUint32(); // Early exit for invalid magic if (magic !== 0x6d736100) { throw new Error("Invalid magic. Probably not a WebAssembly binary"); } // Read version (still needed for validation but we don't use it) this.readUint32(); // Cache buffer length for faster access const bufferLength = this.inBuffer.length; // Only log in development mode if (true) { console.log(`buflen is ${bufferLength}`); } // Pre-calculate threshold to avoid repeated subtraction // Most WebAssembly binaries have sections, but handle edge case if (bufferLength > 0) { // Batch process sections to reduce loop overhead const batchSize = 10; // Process 10 sections at a time let processedCount = 0; while (this.inPos < bufferLength) { // Batch processing - process multiple sections in optimized loop const batchEnd = Math.min(this.inPos + batchSize * 128, bufferLength); // Estimate section size // Optimized loop with minimal logging if ( true && processedCount % 100 === 0) { console.log(`Processing section ${processedCount} at position ${this.inPos}`); } while (this.inPos < batchEnd && this.inPos < bufferLength) { this._readSection(); processedCount++; } // Early exit check if (this.inPos >= bufferLength) { break; } } } // Single commit at the end (more efficient than multiple commits) this.commitBytes(); // Set finished flag this._finished = true; // Optional: return this for method chaining return this; } // TODO Support removing sections by name removeSection(id) { if (typeof id === "number") { this._removeSectionIds.push(id); } else { throw new Error("Invalid argument to removeSection()"); } } addTypeEntry(options) { const newEntry = {}; const form = options.form; if (typeof form === "number") { newEntry.form = form; } else { newEntry.form = convertValueType(form); } const params = options.params; if (params instanceof Array) { const convertedParams = []; for (let i = 0; i < params.length; i++) { const thisParam = params[i]; if (typeof thisParam === "number") { convertedParams.push(thisParam); } else { convertedParams.push(convertValueType(thisParam)); } } newEntry.params = convertedParams; } else { newEntry.params = []; } const returnType = options.returnType; if (typeof returnType === "number") { newEntry.returnType = returnType; } else if (typeof returnType === "string") { newEntry.returnType = convertValueType(returnType); } const newVariable = this._createVariable(); newEntry.variable = newVariable; this._sectionOptions[SECTION_TYPE].newEntries.push(newEntry); this._requiredSectionFlags |= 1 << SECTION_TYPE; return newVariable; } editTypeEntry(index, options) { const savedEntry = {}; // As is, editTypeEntry() has no purpose to receive WailVariables // So unlike some other edit functions, it only accepts numeric indices if (typeof index !== "number") { throw new Error("Invalid index in editTypeEntry()"); } savedEntry.index = index; const params = options.params; if (params instanceof Array) { savedEntry.params = params; } else { savedEntry.params = []; } const returnType = options.returnType; if (returnType) { savedEntry.returnType = returnType; } this._sectionOptions[SECTION_TYPE].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_TYPE; } addImportEntry(options) { const newEntry = {}; const moduleStr = options.moduleStr; if (typeof moduleStr == "string" || moduleStr instanceof String) { newEntry.moduleStr = moduleStr; } else { throw new Error("Invalid moduleStr"); } const fieldStr = options.fieldStr; if (typeof fieldStr == "string" || fieldStr instanceof String) { newEntry.fieldStr = fieldStr; } else { throw new Error("Invalid fieldStr"); } const kind = options.kind; let convertedKind; if (typeof kind === "number") { convertedKind = kind; } else { convertedKind = convertKind(kind); } let type = options.type; newEntry.kind = convertedKind; switch (convertedKind) { case KIND_FUNC: this._importFuncNewCount++; if (typeof type === "number") { newEntry.type = type; } else if (type instanceof WailVarUint32) { newEntry.type = type; } else if (type instanceof WailVariable) { newEntry.type = type.varUint32(); } else { throw new Error("Invalid type"); } break; case KIND_GLOBAL: this._importGlobalNewCount++; if (typeof type === "number") { newEntry.type = type; } else { throw new Error("Invalid type"); } // Mutable imported globals is not supported by all browsers, but // we allow it regardless. if (options.mutability === 0 || options.mutability === 1 || options.mutability === true || options.mutability === false) { newEntry.mutability = options.mutability; } else { throw new Error("Invalid mutability"); } break; case KIND_MEMORY: throw new Error("Adding new memory object not currently supported"); case KIND_TABLE: throw new Error("Adding new table object not currently supported"); default: throw new Error("Invalid kind"); } const newVariable = this._createVariable(); newEntry.variable = newVariable; this._sectionOptions[SECTION_IMPORT].newEntries.push(newEntry); this._requiredSectionFlags |= 1 << SECTION_IMPORT; // Adding functions to the import section changes the function table. // This means we need to patch up any other section that contains function indexes if (this._importFuncNewCount > 0) { this._optionalSectionFlags |= 1 << SECTION_EXPORT; this._optionalSectionFlags |= 1 << SECTION_ELEMENT; this._optionalSectionFlags |= 1 << SECTION_CODE; this._optionalSectionFlags |= 1 << SECTION_START; } // Same logic as above. If we add an imported global, we need to parse // any potentially affected sections. if (this._importGlobalNewCount > 0) { this._optionalSectionFlags |= 1 << SECTION_EXPORT; this._optionalSectionFlags |= 1 << SECTION_CODE; } return newVariable; } // TODO WAIL does not currently support modifying the "kind" of an existing import // Is there any realistic reason to do so? editImportEntry(index, options) { const savedEntry = {}; savedEntry.index = index; if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Invalid index in editImportEntry()"); } const moduleStr = options.moduleStr; if (typeof moduleStr == "string" || moduleStr instanceof String) { savedEntry.moduleStr = stringToByteArray(moduleStr); } const fieldStr = options.fieldStr; if (typeof fieldStr == "string" || fieldStr instanceof String) { savedEntry.fieldStr = stringToByteArray(fieldStr); } this._sectionOptions[SECTION_IMPORT].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_IMPORT; } addImportElementParser(index, callback) { if (typeof callback !== "function") { throw new Error("Bad callback in addImportElementParser()"); } if (index === null) { this._globalImportCallback = callback; } else if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Bad id " + index + " in addImportElementParser()"); } else { const callbackObj = {}; callbackObj.index = index; callbackObj.callback = callback; this._importCallbacks.push(callbackObj); } this._optionalSectionFlags |= 1 << SECTION_IMPORT; } addFunctionEntry(options) { const newEntry = {}; const type = options.type; if (typeof type === "number") { newEntry.type = type; } else if (type instanceof WailVarUint32) { newEntry.type = type; } else if (type instanceof WailVariable) { newEntry.type = type.varUint32(); } else { throw new Error("Invalid type"); } const newVariable = this._createVariable(); newEntry.variable = newVariable; this._sectionOptions[SECTION_FUNCTION].newEntries.push(newEntry); this._requiredSectionFlags |= 1 << SECTION_FUNCTION; return newVariable; } editFunctionEntry(index, options) { const savedEntry = {}; savedEntry.index = index; if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Invalid index in editFunctionEntry()"); } const givenType = options.type; if (typeof givenType !== "number") { throw new Error("Invalid type in editFunctionEntry()"); } savedEntry.type = givenType; this._sectionOptions[SECTION_FUNCTION].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_FUNCTION; } getFunctionIndex(oldIndex) { console.log("[WAIL] getFunctionIndex called | oldIndex:", oldIndex, "| type:", typeof oldIndex, "| _finished:", this._finished); if (this._finished) { console.log("[WAIL] getFunctionIndex: parser is finished, using fast-path resolution"); if (oldIndex instanceof WailVariable) { console.log("[WAIL] getFunctionIndex: oldIndex is a WailVariable, returning as-is | value:", oldIndex.value); return oldIndex; } else { const newVariable = this._createVariable(); console.log("[WAIL] getFunctionIndex: calling _getAdjustedFunctionIndex with oldIndex =", oldIndex); const adjusted = this._getAdjustedFunctionIndex(oldIndex); console.log("[WAIL] getFunctionIndex: _getAdjustedFunctionIndex returned:", adjusted); newVariable.value = adjusted; return newVariable; } } const newVariable = this._createVariable(); if (typeof oldIndex !== "number") { console.error("[WAIL] getFunctionIndex: INVALID oldIndex — expected number, got:", typeof oldIndex, "| value:", oldIndex); throw new Error("Invalid index in getFunctionIndex()"); } const pendingOptions = { oldIndex: oldIndex, variable: newVariable, }; console.log("[WAIL] getFunctionIndex: queuing pending resolution | oldIndex:", oldIndex, "| SECTION_FUNCTION pending count:", this._sectionOptions[SECTION_FUNCTION].pending.length + 1); this._sectionOptions[SECTION_FUNCTION].pending.push(pendingOptions); // Resolving function indexes can be done by only parsing the IMPORT section // since newly added FUNCTION entries will be added to the end of the list this._optionalSectionFlags |= 1 << SECTION_IMPORT; console.log("[WAIL] getFunctionIndex: returning pending variable for oldIndex:", oldIndex); return newVariable; } addGlobalEntry(options) { const newEntry = {}; newEntry.globalType = {}; if (typeof options.globalType === "undefined") { throw new Error("Invalid globalType"); } if (typeof options.globalType.contentType === "number") { newEntry.globalType.contentType = options.globalType.contentType; } else { newEntry.globalType.contentType = convertValueType(options.globalType.contentType); } const mutability = options.globalType.mutability; if (mutability == true) { newEntry.globalType.mutability = 1; } else if (mutability == false) { newEntry.globalType.mutability = 0; } else { throw new Error("Invalid mutability"); } if (options.initExpr instanceof Array) { newEntry.initExpr = convertOpcodeArray(options.initExpr); } else { // Default to initExpr value of "i32.const 0" if not specified newEntry.initExpr = [OP_I32_CONST, VarUint32(0x00), OP_END]; } const newVariable = this._createVariable(); newEntry.variable = newVariable; this._sectionOptions[SECTION_GLOBAL].newEntries.push(newEntry); this._requiredSectionFlags |= 1 << SECTION_GLOBAL; return newVariable; } // TODO Handle editing initExpr editGlobalEntry(globalIndex, options) { const savedEntry = {}; if (typeof globalIndex === "number") { console.warn("Using raw indexes in editGlobalEntry() can have unpredictable " + "results. Consider using getGlobalIndex() instead"); } else if (!(globalIndex instanceof WailVariable)) { throw new Error("Invalid globalIndex in addCodeEntry()"); } savedEntry.index = globalIndex; savedEntry.globalType = {}; if (typeof options.globalType === "undefined") { throw new Error("Invalid globalType"); } if (typeof options.globalType.contentType === "number") { savedEntry.globalType.contentType = options.globalType.contentType; } else { savedEntry.globalType.contentType = convertValueType(options.globalType.contentType); } const mutability = options.globalType.mutability; if (mutability == true) { savedEntry.globalType.mutability = 1; } else if (mutability == false) { savedEntry.globalType.mutability = 0; } else { throw new Error("Invalid mutability"); } this._sectionOptions[SECTION_GLOBAL].existingEntries.push(savedEntry); this._requiredSectionFlags |= 1 << SECTION_GLOBAL; } getGlobalIndex(oldIndex) { if (this._finished) { if (oldIndex instanceof WailVariable) { return oldIndex.value; } else { return this._getAdjustedGlobalIndex(oldIndex); } } const newVariable = this._createVariable(); if (typeof oldIndex !== "number") { throw new Error("Invalid index in getGlobalIndex()"); } const pendingOptions = { oldIndex: oldIndex, variable: newVariable, }; this._sectionOptions[SECTION_GLOBAL].pending.push(pendingOptions); // Resolving function indexes can be done by only parsing the IMPORT section // since newly added GLOBAL entries will be added to the end of the list this._optionalSectionFlags |= 1 << SECTION_IMPORT; return newVariable; } addExportEntry(index, options) { const newEntry = {}; if (typeof options.fieldStr == "string" || options.fieldStr instanceof String) { newEntry.fieldStr = options.fieldStr; } else { throw new Error("Invalid fieldStr"); } if (typeof options.kind == "number") { newEntry.kind = options.kind; } else { newEntry.kind = convertKind(options.kind); } if (typeof index === "number") { newEntry.index = index; } else if (index instanceof WailVarUint32) { newEntry.index = index; } else if (index instanceof WailVariable) { newEntry.index = index.varUint32(); } else { throw new Error("Invalid type"); } const newVariable = this._createVariable(); newEntry.variable = newVariable; this._sectionOptions[SECTION_EXPORT].newEntries.push(newEntry); this._requiredSectionFlags |= 1 << SECTION_EXPORT; return newVariable; } editExportEntry(index, options) { const savedEntry = {}; savedEntry.index = index; if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Invalid index in editExportEntry()"); } const fieldStr = options.fieldStr; if (typeof fieldStr == "string" || fieldStr instanceof String) { savedEntry.fieldStr = stringToByteArray(fieldStr); } // TODO Validate savedEntry.kind = options.kind; savedEntry.funcIndex = options.index; this._sectionOptions[SECTION_EXPORT].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_EXPORT; } addExportElementParser(index, callback) { if (typeof callback !== "function") { throw new Error("Bad callback in addExportElementParser()"); } if (index === null) { this._globalExportCallback = callback; } else if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Bad id " + index + " in addExportElementParser()"); } else { const callbackObj = {}; callbackObj.index = index; callbackObj.callback = callback; this._exportCallbacks.push(callbackObj); } this._optionalSectionFlags |= 1 << SECTION_EXPORT; } // There is no addStartEntry since the start section can only have one element editStartEntry(newIndex) { if (typeof newIndex !== "number" && !(newIndex instanceof WailVariable)) { throw new Error("Invalid index in editStartEntry()"); } this._sectionOptions[SECTION_START].existingEntries.push(newIndex); // Unlike other edit functions, editing the START entry should add the // section if it doesn't exist this._requiredSectionFlags |= 1 << SECTION_START; } // TODO Validate addElementEntry(options) { const newVariable = this._createVariable(); options.variable = newVariable; this._sectionOptions[SECTION_ELEMENT].newEntries.push(options); this._requiredSectionFlags |= 1 << SECTION_ELEMENT; return newVariable; } editElementEntry(index, options) { const savedEntry = {}; savedEntry.index = index; if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Invalid index in editElementEntry()"); } savedEntry.elems = []; this._sectionOptions[SECTION_ELEMENT].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_ELEMENT; } addCodeEntry(funcIndex, options) { const newEntry = {}; if (typeof funcIndex === "number") { console.warn("Using raw indexes in addCodeEntry() can have unpredictable " + "results. Consider using getFunctionIndex() instead"); } else if (!(funcIndex instanceof WailVariable)) { throw new Error("Invalid funcIndex in addCodeEntry()"); } newEntry.index = funcIndex; const locals = options.locals; if (locals instanceof Array) { const fixedLocals = []; for (let i = 0; i < locals.length; i++) { const thisLocal = locals[i]; if (typeof thisLocal === "number") { fixedLocals.push(thisLocal); } else if (typeof thisLocal === "string") { fixedLocals.push(convertValueType(thisLocal)); } else { throw new Error("Invalid local entry in addCodeEntry()"); } } newEntry.locals = fixedLocals; } else { newEntry.locals = []; } const code = options.code; if (code instanceof Array) { newEntry.code = convertOpcodeArray(code); } else { throw new Error("Invalid code"); } const newVariable = this._createVariable(); newEntry.variable = newVariable; this._sectionOptions[SECTION_CODE].newEntries.push(newEntry); this._requiredSectionFlags |= 1 << SECTION_CODE; return newVariable; } editCodeEntry(funcIndex, options) { const savedEntry = {}; if (typeof funcIndex === "number") { console.warn("Using raw indexes in editCodeEntry() can have unpredictable " + "results. Consider using getFunctionIndex() instead"); } else if (!(funcIndex instanceof WailVariable)) { throw new Error("Invalid funcIndex in addCodeEntry()"); } savedEntry.index = funcIndex; const locals = options.locals; if (locals instanceof Array) { const fixedLocals = []; for (let i = 0; i < locals.length; i++) { const thisLocal = locals[i]; if (typeof thisLocal === "number") { fixedLocals.push(thisLocal); } else if (typeof thisLocal === "string") { fixedLocals.push(convertValueType(thisLocal)); } else { throw new Error("Invalid local entry in addCodeEntry()"); } } savedEntry.locals = fixedLocals; } else { savedEntry.locals = []; } const code = options.code; if (code instanceof Array) { savedEntry.code = convertOpcodeArray(code); } else { throw new Error("Invalid code"); } this._sectionOptions[SECTION_CODE].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_IMPORT; this._optionalSectionFlags |= 1 << SECTION_CODE; } // TODO Validate addDataEntry(options) { const newVariable = this._createVariable(); options.variable = newVariable; this._sectionOptions[SECTION_DATA].newEntries.push(options); this._requiredSectionFlags |= 1 << SECTION_DATA; return newVariable; } // TODO Validate editDataEntry(index, options) { const savedEntry = {}; if (typeof index !== "number") { throw new Error("Invalid index in editTypeEntry()"); } savedEntry.index = index; if (typeof options.data === "string") { savedEntry.data = stringToByteArray(options.data); } else { savedEntry.data = options.data; } this._sectionOptions[SECTION_DATA].existingEntries.push(savedEntry); this._optionalSectionFlags |= 1 << SECTION_DATA; } addCodeElementParser(index, callback) { if (typeof callback !== "function") { throw new Error("Bad callback in addCodeElementParser()"); } if (index === null) { this._globalFunctionCallback = callback; } else if (typeof index !== "number" && !(index instanceof WailVariable)) { throw new Error("Bad id " + index + " in addCodeElementParser()"); } else { const callbackObj = {}; callbackObj.index = index; callbackObj.callback = callback; this._functionCallbacks.push(callbackObj); } this._optionalSectionFlags |= 1 << SECTION_IMPORT; this._optionalSectionFlags |= 1 << SECTION_CODE; } // TODO Global callbacks addInstructionParser(opcode, callback) { if (typeof callback !== "function") { throw new Error("Bad callback in addInstructionParser()"); } if (opcode === null) { this._globalInstructionCallback = callback; } else if (isNaN(opcode) && !(opcode instanceof WailVariable)) { throw new Error("Bad opcode " + opcode + " in addCodeElementParser()"); } else { this._instructionCallbacks[opcode] = callback; } this._optionalSectionFlags |= 1 << SECTION_CODE; } addRawSection(id, sectionBytes) { const sectionEntry = {}; if (typeof id !== "number") { throw new Error("Bad section index " + index + " in addRawSection()"); } sectionEntry.id = id; sectionEntry.bytes = sectionBytes; this._newSections.push(sectionEntry); } _createVariable() { const variableId = this.__variables.length; const newVariable = new WailVariable(this, variableId); this.__variables.push(newVariable); return newVariable; } _getVariable(id) { return this.__variables[id]; } _setVariable(id, value) { this.__variables[id] = value; } // Parses an array in order to expand any variables to their proper representation. // Throws an exception if the user has not specified a binary representation // (Such as passing a WailVariable instead of a TypedWailVariable _expandArrayVariables(array) { for (let i = 0; i < array.length; i++) { const currentValue = array[i]; // TODO Remove spread operator since it's so slow if (currentValue instanceof Array) { array.splice(i, 1); array.splice(i, 0, ...currentValue); } else if (currentValue instanceof TypedWailVariable) { const thisVariable = currentValue; array.splice(i, 1); array.splice(i, 0, ...thisVariable.value); } // TODO Improve else if (currentValue instanceof WailVariable) { throw new Error("Untyped WailVariable in " + "_expandArrayVariables()"); } } return array; } _readSection() { this.commitBytes(); const id = this.readUint8(); if (id > MAX_SECTION_ID) { throw new Error("Illegal section ID " + id + ". Probably parsing incorrectly"); } let payloadLen; // Skip over removed sections if (this._removeSectionIds.includes(id)) { payloadLen = this.readVarUint32(); this.readBytes(payloadLen); this.updateCopyPosition(); return; } let parseSection = false; if (this._requiredSectionFlags & (1 << id) || this._optionalSectionFlags & (1 << id)) { parseSection = true; } // The DataCount section violates the usual rule that non-custom sections must occur in // numeric order. As a result, we must not assume a section is missing just because we have // encountered the DataCount section if (id != SECTION_DATACOUNT || id != SECTION_TAG) { // At this point we want to check if a required section does not exist // If so, we want to add an empty version of that section and add any new // elements to it for (let missingId = 0; missingId < id; missingId++) { const thisFlag = 1 << missingId; const thisSectionRequired = this._requiredSectionFlags & thisFlag; if (thisSectionRequired && !(thisSectionRequired & this._parsedSections)) { switch (missingId) { case SECTION_TYPE: this._addTypeSection(); break; case SECTION_IMPORT: this._addImportSection(); break; case SECTION_FUNCTION: this._addFunctionSection(); break; case SECTION_GLOBAL: this._addGlobalSection(); break; case SECTION_EXPORT: this._addExportSection(); break; case SECTION_START: this._addStartSection(); break; case SECTION_ELEMENT: this._addElementSection(); break; case SECTION_CODE: this._addCodeSection(); break; case SECTION_DATA: this._addDataSection(); break; default: throw new Error("Attempted to add unhandled section"); } this._parsedSections |= thisFlag; // FIXME This breaks if we need to add 2 missing sections consecutively // See https://www.y8.com/games/slope_football for a testcase this.copyBuffer([id]); } } } for (let i = 0; i < this._newSections.length; i++) { const thisNewSection = this._newSections[i]; if (id > thisNewSection.id) { const newPayload = thisNewSection.bytes; const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([thisNewSection.id]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } } // Skip over the section if the user has not requested we parse it if (!parseSection) { payloadLen = this.readVarUint32(); this.readBytes(payloadLen); return; } // If we have passed the IMPORT section (Regardless of whether or not it exists) // it is safe to resolve any pending function indices if (id > SECTION_IMPORT && this._resolvedTables == false) { this._resolveTableIndices(); } // Now we can handle the new section switch (id) { case SECTION_TYPE: this._parseTypeSection(); break; case SECTION_IMPORT: this._parseImportSection(); break; case SECTION_FUNCTION: this._parseFunctionSection(); break; case SECTION_GLOBAL: this._parseGlobalSection(); break; case SECTION_EXPORT: this._parseExportSection(); break; case SECTION_START: this._parseStartSection(); break; case SECTION_ELEMENT: this._parseElementSection(); break; case SECTION_CODE: this._parseCodeSection(); break; case SECTION_DATA: this._parseDataSection(); break; default: throw new Error("Attempted to parse unhandled section"); } this._parsedSections |= 1 << id; } // This function will resolve any WailVariables referring to indices into the function // or global tables. We need to wait until after the IMPORT section has been parsed // to do this because we need to know the count of imported functions/globals in // order to properly build the associated tables _resolveTableIndices() { const pendingFuncs = this._sectionOptions[SECTION_FUNCTION].pending; for (let i = 0; i < pendingFuncs.length; i++) { const oldIndex = pendingFuncs[i].oldIndex; const variable = pendingFuncs[i].variable; variable.value = this._getAdjustedFunctionIndex(oldIndex); } // Same logic as above, but with global indexes const pendingGlobals = this._sectionOptions[SECTION_GLOBAL].pending; for (let i = 0; i < pendingGlobals.length; i++) { const oldIndex = pendingGlobals[i].oldIndex; const variable = pendingGlobals[i].variable; variable.value = this._getAdjustedGlobalIndex(oldIndex); } this._resolvedTables = true; } _addTypeSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_TYPE].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; const form = optionsEntry.form; const params = optionsEntry.params; let returnType = null; if (typeof optionsEntry.returnType !== "undefined") { returnType = optionsEntry.returnType; } if (optionsEntry.variable instanceof WailVariable) { optionsEntry.variable.value = oldCount + i; } reader.copyBuffer(Uint8ToArray(form)); reader.copyBuffer(VarUint32ToArray(params.length)); reader.copyBuffer(params); if (returnType !== null) { reader.copyBuffer(Uint8ToArray(1)); reader.copyBuffer(Uint8ToArray(returnType)); } else { reader.copyBuffer(Uint8ToArray(0)); } } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_TYPE]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseTypeSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); const start = this.inPos; const oldCount = this.readVarUint32(); const end = this.inPos; const oldCountLength = end - start; const oldPayload = this.readBytes(oldPayloadLen - oldCountLength); const reader = new BufferReader(oldPayload); const newEntries = this._sectionOptions[SECTION_TYPE].newEntries; const existingEntries = this._sectionOptions[SECTION_TYPE].existingEntries; const newCount = oldCount + newEntries.length; let populateThisTimeOnly = false; if (!window.UnityWebModkit.Runtime || !window.UnityWebModkit.Runtime.internalWasmTypes) { populateThisTimeOnly = true; window.UnityWebModkit.Runtime.internalWasmTypes = []; } reader.copyBuffer(VarUint32ToArray(newCount)); for (let typeIndex = 0; typeIndex < oldCount; typeIndex++) { // TODO Is there any purpose to modifying form? const form = reader.readUint8(); let paramCount = reader.readVarUint32(); let params = []; for (let j = 0; j < paramCount; j++) { params.push(reader.readUint8()); } let returnCount = reader.readUint8(); let returnType = null; if (returnCount == 1) { returnType = reader.readUint8(); } // Return count can only be 1 or 0 else if (returnCount != 0) { throw new Error("Invalid returnCount"); } for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; const thisIndex = thisEntry.index; if (typeIndex == thisIndex) { if (typeof thisEntry.params !== "undefined") { params = mod.params; } // TODO This doesn't allow for the possibility of removing return if (typeof thisEntry.returnType !== "undefined") { returnCount = 1; returnType = mod.returnType; } } } reader.copyBuffer(Uint8ToArray(form)); reader.copyBuffer(VarUint32ToArray(params.length)); reader.copyBuffer(params); if (returnCount) { reader.copyBuffer(Uint8ToArray(1)); reader.copyBuffer(Uint8ToArray(returnType)); } else { reader.copyBuffer(Uint8ToArray(0)); } if (!populateThisTimeOnly) continue; window.UnityWebModkit.Runtime.internalWasmTypes.push({ form: valueTypeStr[form], params: params.map((code) => valueTypeStr[code]), returnType: valueTypeStr[returnType], }); } for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; const form = optionsEntry.form; const params = optionsEntry.params; let returnType = null; if (typeof optionsEntry.returnType !== "undefined") { returnType = optionsEntry.returnType; } if (optionsEntry.variable instanceof WailVariable) { optionsEntry.variable.value = oldCount + i; } reader.copyBuffer(Uint8ToArray(form)); reader.copyBuffer(VarUint32ToArray(params.length)); reader.copyBuffer(params); if (returnType !== null) { reader.copyBuffer(Uint8ToArray(1)); reader.copyBuffer(Uint8ToArray(returnType)); } else { reader.copyBuffer(Uint8ToArray(0)); } } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _addImportSection() { const newEntries = this._sectionOptions[SECTION_IMPORT].newEntries; if (newEntries.length === 0) return; // Pre-allocate buffer with estimated size const estimatedSize = newEntries.length * 64; // Rough estimate const writer = new BufferWriter(estimatedSize); writer.writeVarUint32(newEntries.length); let importFuncIndex = 0; let importGlobalIndex = 0; const importFuncCount = this._importFuncCount; const importGlobalCount = this._importGlobalCount; for (let i = 0; i < newEntries.length; i++) { const entry = newEntries[i]; // Convert strings to bytes once const moduleBytes = stringToByteArray(entry.moduleStr); const fieldBytes = stringToByteArray(entry.fieldStr); writer.writeVarUint32(moduleBytes.length); writer.writeBytes(moduleBytes); writer.writeVarUint32(fieldBytes.length); writer.writeBytes(fieldBytes); // Write kind writer.writeUint8(entry.kind); // Handle type based on kind if (entry.kind === KIND_FUNC) { let typeBytes; if (entry.type instanceof TypedWailVariable) { typeBytes = entry.type.value; } else if (entry.type instanceof WailVariable) { throw new Error("Untyped WailVariable in _addImportSection()"); } else { typeBytes = VarUint32ToArray(entry.type); } writer.writeBytes(typeBytes); // Set variable value if (entry.variable instanceof WailVariable) { entry.variable.value = importFuncCount + importFuncIndex; } importFuncIndex++; } else if (entry.kind === KIND_GLOBAL) { writer.writeUint8(entry.type); writer.writeUint8(entry.mutability); // Set variable value if (entry.variable instanceof WailVariable) { entry.variable.value = importGlobalCount + importGlobalIndex; } importGlobalIndex++; } // Other kinds could be added here if needed } const newPayload = writer.toUint8Array(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_IMPORT]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); // Resolve pending indices this._resolvePendingIndices(); } // Helper method to resolve all pending indices _resolvePendingIndices() { // Resolve function indices const pendingFuncs = this._sectionOptions[SECTION_FUNCTION].pending; for (let i = 0; i < pendingFuncs.length; i++) { const pending = pendingFuncs[i]; pending.variable.value = this._getAdjustedFunctionIndex(pending.oldIndex); } // Resolve global indices const pendingGlobals = this._sectionOptions[SECTION_GLOBAL].pending; for (let i = 0; i < pendingGlobals.length; i++) { const pending = pendingGlobals[i]; pending.variable.value = this._getAdjustedGlobalIndex(pending.oldIndex); } } _parseImportSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); // Optimized position tracking const countStart = this.inPos; const oldCount = this.readVarUint32(); const oldCountLength = this.inPos - countStart; const oldPayload = this.readBytes(oldPayloadLen - oldCountLength); const reader = new BufferReader(oldPayload); const newEntries = this._sectionOptions[SECTION_IMPORT].newEntries; const existingEntries = this._sectionOptions[SECTION_IMPORT].existingEntries; // Create lookup map for existing entries const existingEntriesMap = new Map(); for (let i = 0; i < existingEntries.length; i++) { const entry = existingEntries[i]; existingEntriesMap.set(entry.index, entry); } const globalImportCallback = this._globalImportCallback; const hasGlobalCallback = typeof globalImportCallback === "function"; // Process existing imports for (let importIndex = 0; importIndex < oldCount; importIndex++) { reader.commitBytes(); // Read module let moduleLen = reader.readVarUint32(); let moduleBytes = reader.readBytes(moduleLen); // Read field let fieldLen = reader.readVarUint32(); let fieldBytes = reader.readBytes(fieldLen); // Check for modifications const existingEntry = existingEntriesMap.get(importIndex); if (existingEntry) { if (existingEntry.moduleStr) { moduleBytes = existingEntry.moduleStr; moduleLen = moduleBytes.length; } if (existingEntry.fieldStr) { fieldBytes = existingEntry.fieldStr; fieldLen = fieldBytes.length; } } // Write back module and field reader.copyBuffer(VarUint32ToArray(moduleLen)); reader.copyBuffer(moduleBytes); reader.copyBuffer(VarUint32ToArray(fieldLen)); reader.copyBuffer(fieldBytes); // Read and process kind const kind = reader.readUint8(); // Handle based on kind switch (kind) { case KIND_FUNC: this._importFuncCount++; reader.readVarUint32(); // Skip type index break; case KIND_TABLE: reader.readUint8(); // Skip elem_type const tableFlags = reader.readUint8(); reader.readVarUint32(); // Skip initial if (tableFlags) { reader.readVarUint32(); // Skip maximum } break; case KIND_MEMORY: const memoryFlags = reader.readUint8(); reader.readVarUint32(); // Skip initial if (memoryFlags) { reader.readVarUint32(); // Skip maximum } break; case KIND_GLOBAL: this._importGlobalCount++; reader.readUint8(); // Skip value_type reader.readUint8(); // Skip mutability break; default: throw new Error(`Invalid type kind: ${kind}`); } // Call global callback if exists if (hasGlobalCallback) { const parameters = { module: moduleBytes, field: fieldBytes, kind: kind, }; globalImportCallback(parameters); } reader.commitBytes(); } // Process new entries let newCount = oldCount; const importFuncCount = this._importFuncCount; const importGlobalCount = this._importGlobalCount; // Pre-allocate arrays for new entries if there are many if (newEntries.length > 0) { for (let i = 0; i < newEntries.length; i++, newCount++) { const entry = newEntries[i]; // Convert strings once const moduleBytes = stringToByteArray(entry.moduleStr); const fieldBytes = stringToByteArray(entry.fieldStr); reader.copyBuffer(VarUint32ToArray(moduleBytes.length)); reader.copyBuffer(moduleBytes); reader.copyBuffer(VarUint32ToArray(fieldBytes.length)); reader.copyBuffer(fieldBytes); reader.copyBuffer([entry.kind]); if (entry.kind === KIND_FUNC) { let typeBytes; if (entry.type instanceof TypedWailVariable) { typeBytes = entry.type.value; } else if (entry.type instanceof WailVariable) { throw new Error("Untyped WailVariable in _parseImportSection()"); } else { typeBytes = VarUint32ToArray(entry.type); } reader.copyBuffer(typeBytes); if (entry.variable instanceof WailVariable) { entry.variable.value = importFuncCount + i; } } else if (entry.kind === KIND_GLOBAL) { reader.copyBuffer([entry.type, entry.mutability]); if (entry.variable instanceof WailVariable) { entry.variable.value = importGlobalCount + i; } } } } // Write final output const newCountArray = VarUint32ToArray(newCount); const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newCountArray.length + newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newCountArray); this.copyBuffer(newPayload); // Resolve pending indices if needed if (!this._resolvedTables) { this._resolveTableIndices(); } } _addFunctionSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_FUNCTION].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); for (let i = 0; i < newEntries.length; i++) { let optionsEntry = newEntries[i]; let type; if (optionsEntry.type instanceof TypedWailVariable) { type = optionsEntry.type.value; } else if (optionsEntry.type instanceof WailVariable) { throw new Error("Untyped WailVariable in _parseFunctionSection()"); } else { type = VarUint32ToArray(optionsEntry.type); } reader.copyBuffer(type); if (optionsEntry.variable instanceof WailVariable) { const functionIndex = i + this._importFuncCount; optionsEntry.variable.value = this._getAdjustedFunctionIndex(functionIndex); } } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_FUNCTION]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseFunctionSection() { this.commitBytes(); // Cache references for faster access const newEntries = this._sectionOptions[SECTION_FUNCTION].newEntries; const existingEntries = this._sectionOptions[SECTION_FUNCTION].existingEntries; const oldPayloadLen = this.readVarUint32(); // Read old count with single position tracking const countStart = this.inPos; const oldCount = this.readVarUint32(); const oldCountLength = this.inPos - countStart; const oldPayload = this.readBytes(oldPayloadLen - oldCountLength); const reader = new BufferReader(oldPayload); // Optimize Unity runtime check let populateThisTimeOnly = false; const unityRuntime = window.UnityWebModkit?.Runtime; if (unityRuntime) { if (!unityRuntime.internalWasmFunctions) { populateThisTimeOnly = true; unityRuntime.internalWasmFunctions = []; } } const internalWasmFunctions = unityRuntime?.internalWasmFunctions; // Create lookup map for existing entries to avoid O(n^2) search const existingEntriesMap = new Map(); if (existingEntries.length > 0) { for (let i = 0; i < existingEntries.length; i++) { const entry = existingEntries[i]; existingEntriesMap.set(entry.index, entry); } } // Pre-allocate array if populating (better for V8 optimization) let wasmFunctionsArray = null; if (populateThisTimeOnly && internalWasmFunctions) { wasmFunctionsArray = internalWasmFunctions; } // Process existing functions for (let funcIndex = 0; funcIndex < oldCount; funcIndex++) { reader.commitBytes(); // Read current function type let funcType = reader.readVarUint32(); // Check if we need to modify this function const existingEntry = existingEntriesMap.get(funcIndex); if (existingEntry) { funcType = existingEntry.type; } // Write back the (possibly modified) function type reader.copyBuffer(VarUint32ToArray(funcType)); // Only populate if needed if (wasmFunctionsArray) { wasmFunctionsArray.push({ funcType }); } } // Process new entries let newCount = oldCount; const importFuncCount = this._importFuncCount; for (let i = 0; i < newEntries.length; i++, newCount++) { const optionsEntry = newEntries[i]; // Determine type with minimal branching let typeBytes; const typeField = optionsEntry.type; if (typeField instanceof TypedWailVariable) { typeBytes = typeField.value; } else if (typeField instanceof WailVariable) { throw new Error("Untyped WailVariable in _parseFunctionSection()"); } else { typeBytes = VarUint32ToArray(typeField); } reader.copyBuffer(typeBytes); // Set variable value if needed const variable = optionsEntry.variable; if (variable instanceof WailVariable) { const functionIndex = newCount + importFuncCount; variable.value = this._getAdjustedFunctionIndex(functionIndex); } } // Write final output const newCountArray = VarUint32ToArray(newCount); const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newCountArray.length + newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newCountArray); this.copyBuffer(newPayload); } _addGlobalSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_GLOBAL].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; reader.copyBuffer(Uint8ToArray(optionsEntry.globalType.contentType)); reader.copyBuffer(Uint8ToArray(optionsEntry.globalType.mutability)); const initExpr = this._expandArrayVariables(optionsEntry.initExpr); reader.copyBuffer(initExpr); if (optionsEntry.variable instanceof WailVariable) { optionsEntry.variable.value = this._importGlobalCount + i; } } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_GLOBAL]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseGlobalSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); const start = this.inPos; const oldCount = this.readVarUint32(); const end = this.inPos; const oldCountLength = end - start; const oldPayload = this.readBytes(oldPayloadLen - oldCountLength); const reader = new BufferReader(oldPayload); const newEntries = this._sectionOptions[SECTION_GLOBAL].newEntries; const existingEntries = this._sectionOptions[SECTION_GLOBAL].existingEntries; const newCount = oldCount + newEntries.length; reader.copyBuffer(VarUint32ToArray(newCount)); for (let globalIndex = 0; globalIndex < oldCount; globalIndex++) { let newContentType; let newMutability; for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; let thisIndex = thisEntry.index; if (thisIndex instanceof WailVariable) { thisIndex = thisIndex.value; } if (globalIndex == thisIndex) { newContentType = thisEntry.globalType.contentType; newMutability = thisEntry.globalType.mutability; } } let contentType = reader.readUint8(); if (typeof newContentType !== "undefined") { reader.copyBuffer([newContentType]); } reader.commitBytes(); let mutability = reader.readUint8(); if (typeof newMutability !== "undefined") { reader.copyBuffer([newMutability]); } let current; do { current = this._readInstruction(reader); } while (current[0] != OP_END); reader.commitBytes(); } for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; reader.copyBuffer([optionsEntry.globalType.contentType]); reader.copyBuffer([optionsEntry.globalType.mutability]); reader.copyBuffer(this._expandArrayVariables(optionsEntry.initExpr)); if (optionsEntry.variable instanceof WailVariable) { optionsEntry.variable.value = this._importGlobalCount + oldCount + i; } } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _addExportSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_EXPORT].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; const fieldStr = stringToByteArray(optionsEntry.fieldStr); const fieldLen = VarUint32ToArray(fieldStr.length); const kind = Uint8ToArray(optionsEntry.kind); let index; if (optionsEntry.index instanceof TypedWailVariable) { index = optionsEntry.index.value; } else if (optionsEntry.index instanceof WailVariable) { throw new Error("Untyped WailVariable in _parseExportSection()"); } else { index = VarUint32ToArray(optionsEntry.index); } reader.copyBuffer(fieldLen); reader.copyBuffer(fieldStr); reader.copyBuffer(kind); reader.copyBuffer(index); } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_EXPORT]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseExportSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); const oldPayload = this.readBytes(oldPayloadLen); const reader = new BufferReader(oldPayload); const oldCount = reader.readVarUint32(); const newEntries = this._sectionOptions[SECTION_EXPORT].newEntries; const existingEntries = this._sectionOptions[SECTION_EXPORT].existingEntries; const newCount = oldCount + newEntries.length; reader.copyBuffer(VarUint32ToArray(newCount)); for (let exportIndex = 0; exportIndex < oldCount; exportIndex++) { reader.commitBytes(); let fieldLen = reader.readVarUint32(); let fieldStr = reader.readBytes(fieldLen); let kind = reader.readUint8(); let oldIndex = reader.readVarUint32(); for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; const thisIndex = thisEntry.index; if (exportIndex == thisIndex) { if (typeof thisEntry.fieldStr !== "undefined") { fieldStr = thisEntry.fieldStr; fieldLen = thisEntry.fieldStr.length; } if (typeof thisEntry.kind !== "undefined") { kind = thisEntry.kind; } if (typeof thisEntry.funcIndex !== "undefined") { oldIndex = thisEntry.funcIndex; } } } let newIndex = oldIndex; if (oldIndex instanceof WailVariable) { newIndex = oldIndex.value; } else { // Fix up export table based on any additions to the import table if (kind == KIND_FUNC) { newIndex = this._getAdjustedFunctionIndex(oldIndex); } else if (kind == KIND_GLOBAL) { newIndex = this._getAdjustedGlobalIndex(oldIndex); } } reader.copyBuffer(VarUint32ToArray(fieldLen)); reader.copyBuffer(fieldStr); reader.copyBuffer([kind]); reader.copyBuffer(VarUint32ToArray(newIndex)); // TODO Should return value of entry, not just name and kind // TODO Should allow modification if (typeof this._globalExportCallback === "function") { const parameters = {}; parameters.field = fieldStr; parameters.kind = kind; this._globalExportCallback(parameters); } } for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; const fieldStr = stringToByteArray(optionsEntry.fieldStr); const fieldLen = VarUint32ToArray(fieldStr.length); const kind = Uint8ToArray(optionsEntry.kind); let index; if (optionsEntry.index instanceof TypedWailVariable) { index = optionsEntry.index.value; } else if (optionsEntry.index instanceof WailVariable) { throw new Error("Untyped WailVariable in _parseExportSection()"); } else { index = VarUint32ToArray(optionsEntry.index); } reader.copyBuffer(fieldLen); reader.copyBuffer(fieldStr); reader.copyBuffer(kind); reader.copyBuffer(index); } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseStartSection() { this.commitBytes(); // We effectively ignore elementCount since (AFAIK) there is no circumstance // where there is more than one start entry in a binary const payloadSize = this.readVarUint32(); const oldStart = this.readVarUint32(); const existingEntries = this._sectionOptions[SECTION_START].existingEntries; let newStart; if (existingEntries.length > 0) { // As far as I can tell there's no purpose to calling editStartEntry multiple // times, but this is consistent with how other edit functions work for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; if (typeof thisEntry === "number") { newStart = thisEntry; } else if (thisEntry instanceof WailVariable) { newStart = thisEntry.value; } else { throw new Error("Invalid function index in _parseStartSection()"); } } } else { newStart = this._getAdjustedFunctionIndex(oldStart); } const newStartArray = VarUint32ToArray(newStart); const newPayloadLen = VarUint32ToArray(newStartArray.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newStartArray); } _addElementSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_ELEMENT].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); for (let i = 0; i < newEntries.length; i++, newCount++) { const optionsEntry = newEntries[i]; const index = optionsEntry.index; if (index != 0) { throw new Error("Unsupported element index " + index); } const offset = optionsEntry.offset; const elems = this._expandArrayVariables(optionsEntry.elems); const elemCount = elems.length; reader.copyBuffer(VarUint32ToArray(index)); reader.copyBuffer(offset); reader.copyBuffer(VarUint32ToArray(elemCount)); reader.copyBuffer(elems); } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_ELEMENT]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseElementSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); const start = this.inPos; const oldCount = this.readVarUint32(); const end = this.inPos; const oldCountLength = end - start; const oldPayload = this.readBytes(oldPayloadLen - oldCountLength); const reader = new BufferReader(oldPayload); const newEntries = this._sectionOptions[SECTION_ELEMENT].newEntries; const existingEntries = this._sectionOptions[SECTION_ELEMENT].existingEntries; let populateThisTimeOnly = false; if (!window.UnityWebModkit.Runtime || !window.UnityWebModkit.Runtime.internalMappings) { populateThisTimeOnly = true; window.UnityWebModkit.Runtime.internalMappings = []; } for (let elemIndex = 0; elemIndex < oldCount; elemIndex++) { let memIndex = reader.readVarUint32(); let current; // At time of writing, init expressions can only be simple expressions. // Therefore, it is safe to just parse until we find OP_END. However, // this may become unreliable in the future do { current = this._readInstruction(reader); } while (current[0] != OP_END); reader.commitBytes(); let numElements = reader.readVarUint32(); let elements = []; for (let i = 0; i < numElements; i++) { const oldIndex = reader.readVarUint32(); const newIndex = this._getAdjustedFunctionIndex(oldIndex); elements.push(newIndex); } for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; const thisIndex = thisEntry.index; if (elemIndex == thisIndex) { // TODO Support WailVariables if (typeof thisEntry.elems !== "undefined") { elements = thisEntry.elems; numElements = elements.length; } } } reader.copyBuffer(VarUint32ToArray(numElements)); for (let i = 0; i < numElements; i++) { reader.copyBuffer(VarUint32ToArray(elements[i])); } if (!populateThisTimeOnly) continue; window.UnityWebModkit.Runtime.internalMappings.push({ index: memIndex, elements, }); } let newCount = oldCount; for (let i = 0; i < newEntries.length; i++, newCount++) { const optionsEntry = newEntries[i]; const index = optionsEntry.index; if (index != 0) { throw new Error("Unsupported element index " + index); } const offset = optionsEntry.offset; const elems = this._expandArrayVariables(optionsEntry.elems); const elemCount = elems.length; reader.copyBuffer(VarUint32ToArray(index)); reader.copyBuffer(offset); reader.copyBuffer(VarUint32ToArray(elemCount)); reader.copyBuffer(elems); } const newPayload = reader.write(); const newCountArray = VarUint32ToArray(newCount); const newPayloadLen = VarUint32ToArray(newCountArray.length + newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newCountArray); this.copyBuffer(newPayload); } _addCodeSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_CODE].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); // const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_CODE]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseCodeSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); const oldPayload = this.readBytes(oldPayloadLen); const reader = new BufferReader(oldPayload); const oldCount = reader.readVarUint32(); const newEntries = this._sectionOptions[SECTION_CODE].newEntries; const newCount = oldCount + newEntries.length; // Write new count once reader.copyBuffer(VarUint32ToArray(newCount)); // Check if we need to populate internalWasmCode - optimize property access let populateThisTimeOnly = false; const unityRuntime = window.UnityWebModkit?.Runtime; if (unityRuntime && !unityRuntime.internalWasmCode) { populateThisTimeOnly = true; unityRuntime.internalWasmCode = []; } const internalWasmCode = unityRuntime?.internalWasmCode; const importFuncCount = this._importFuncCount; const adjustedImportFuncCount = importFuncCount; // Create lookup map for new entries to avoid O(n^2) search const newEntriesMap = new Map(); if (newEntries.length > 0) { for (let i = 0; i < newEntries.length; i++) { const entry = newEntries[i]; let index = entry.index; if (index instanceof WailVariable) { index = index.value; } newEntriesMap.set(index, entry); } } // Batch processing with reduced console logging const batchSize = 1000; let lastLog = 0; for (let batchStart = 0; batchStart < oldCount; batchStart += batchSize) { const batchEnd = Math.min(batchStart + batchSize, oldCount); for (let i = batchStart; i < batchEnd; i++) { // Skip logging for most iterations if (i - lastLog >= 5000 && i !== 0) { lastLog = i; console.log("Parsing function " + i + " of " + oldCount); } const funcIndex = this._getAdjustedFunctionIndex(adjustedImportFuncCount + i); const readFunction = this._readFunction(reader, funcIndex); readFunction.preservedIndex = i; // Store only functions with 74 instructions if populating if (populateThisTimeOnly && readFunction.instructions.length === 74) { internalWasmCode.push(readFunction); } } } // Process new entries using the pre-built map if (newEntries.length > 0) { for (let currentIndex = oldCount; currentIndex < newCount; currentIndex++) { const realIndex = this._funcSectionIndexToFuncTableIndex(currentIndex); const optionsEntry = newEntriesMap.get(realIndex); if (!optionsEntry) { throw new Error(`No CODE entry found for index ${realIndex}`); } // Build body directly without creating multiple BufferReader objects const bodyParts = []; const locals = optionsEntry.locals; // Write locals count bodyParts.push(...VarUint32ToArray(locals.length)); // Write each local for (let i = 0; i < locals.length; i++) { bodyParts.push(...VarUint32ToArray(1)); bodyParts.push(locals[i]); } // Write code const expandedCode = this._expandArrayVariables(optionsEntry.code); bodyParts.push(...expandedCode); // Convert to Uint8Array once const bodyPayload = new Uint8Array(bodyParts); const bodySize = VarUint32ToArray(bodyPayload.length); // Write to main reader reader.copyBuffer(bodySize); reader.copyBuffer(bodyPayload); } } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _addDataSection() { const reader = new BufferReader(); const newEntries = this._sectionOptions[SECTION_DATA].newEntries; const entryCountArray = VarUint32ToArray(newEntries.length); reader.copyBuffer(entryCountArray); for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; let index; if (typeof optionsEntry.index !== "undefined") { index = VarUint32ToArray(optionsEntry.index); } else { index = VarUint32ToArray(0); } const offset = optionsEntry.offset; // Initialization expressions must always end with an "end" instruction if (offset[offset.length - 1] != OP_END) { offset.push(OP_END); } const data = optionsEntry.data; const size = VarUint32ToArray(data.length); reader.copyBuffer(index); reader.copyBuffer(offset); reader.copyBuffer(size); reader.copyBuffer(data); } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer([SECTION_DATA]); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } _parseDataSection() { this.commitBytes(); const oldPayloadLen = this.readVarUint32(); // This is gross but VarUInt32s can be variable lengths (obviously). // So this is an easy way to account for any possible length. const start = this.inPos; const oldCount = this.readVarUint32(); const end = this.inPos; const oldCountLength = end - start; const oldPayload = this.readBytes(oldPayloadLen - oldCountLength); const reader = new BufferReader(oldPayload); const newEntries = this._sectionOptions[SECTION_DATA].newEntries; const existingEntries = this._sectionOptions[SECTION_DATA].existingEntries; const newCount = oldCount + newEntries.length; reader.copyBuffer(VarUint32ToArray(newCount)); for (let dataIndex = 0; dataIndex < oldCount; dataIndex++) { let current; do { current = this._readInstruction(reader); } while (current[0] !== OP_END); reader.commitBytes(); let size = reader.readVarUint32(); let data = reader.readBytes(size); for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; const thisIndex = thisEntry.index; if (dataIndex == thisIndex) { if (typeof thisEntry.data !== "undefined") { data = thisEntry.data; size = data.length; } } } reader.copyBuffer(VarUint32ToArray(size)); reader.copyBuffer(data); } for (let i = 0; i < newEntries.length; i++) { const optionsEntry = newEntries[i]; let index; if (typeof optionsEntry.index !== "undefined") { index = VarUint32ToArray(optionsEntry.index); } else { index = VarUint32ToArray(0); } const offset = optionsEntry.offset; // Initialization expressions must always end with an "end" instruction if (offset[offset.length - 1] != OP_END) { offset.push(OP_END); } const data = optionsEntry.data; const size = VarUint32ToArray(data.length); reader.copyBuffer(index); reader.copyBuffer(offset); reader.copyBuffer(size); reader.copyBuffer(data); } const newPayload = reader.write(); const newPayloadLen = VarUint32ToArray(newPayload.length); this.copyBuffer(newPayloadLen); this.copyBuffer(newPayload); } // TODO Modify locals/params _readFunction(reader, funcIndex) { const existingEntries = this._sectionOptions[SECTION_CODE].existingEntries; const bodySize = reader.readVarUint32(); const bodyPayload = reader.readBytes(bodySize); const headerReader = new BufferReader(bodyPayload); const localCount = headerReader.readVarUint32(); const locals = []; const instructions = []; for (let i = 0; i < localCount; i++) { locals.push(...Array(headerReader.readVarUint32()).fill(valueTypeStr[headerReader.readUint8()])); } headerReader.commitBytes(); const bodyReader = new BufferReader(bodyPayload.subarray(headerReader.inPos)); while (bodyReader.inPos < bodyReader.inBuffer.length) { const instr = this._readInstruction(bodyReader); if (locals.length === 1 && locals[0] === "i32") { instructions.push(instr); } bodyReader.commitBytes(); } let newHeader = headerReader.write(); let newBody = bodyReader.write(); // TODO Should probably prioritize non-global callbacks over global callbacks if (typeof this._globalFunctionCallback === "function") { const parameters = {}; parameters.bytes = bodyReader.write(); parameters.index = funcIndex; const callbackResult = this._globalFunctionCallback(parameters); if (callbackResult !== false) { newBody = callbackResult; } } else { // TODO Callback should send and receive local info to/from callback for (let i = 0; i < this._functionCallbacks.length; i++) { const thisCallback = this._functionCallbacks[i]; let thisIndex = thisCallback.index; if (thisIndex instanceof WailVariable) { thisIndex = thisIndex.value; } if (thisIndex === funcIndex) { const parameters = {}; parameters.bytes = bodyReader.write(); parameters.index = funcIndex; // TODO Handle locals as well const callbackResult = thisCallback.callback(parameters); if (callbackResult !== false) { newBody = callbackResult; } } } } for (let i = 0; i < existingEntries.length; i++) { const thisEntry = existingEntries[i]; let thisIndex = thisEntry.index; if (thisIndex instanceof WailVariable) { thisIndex = thisIndex.value; } if (funcIndex == thisIndex) { const newHeaderReader = new BufferReader(); const newBodyReader = new BufferReader(); const locals = thisEntry.locals; newHeaderReader.copyBuffer(VarUint32ToArray(locals.length)); for (let i = 0; i < locals.length; i++) { const thisLocal = locals[i]; newHeaderReader.copyBuffer(VarUint32ToArray(1)); newHeaderReader.copyBuffer(Uint8ToArray(thisLocal)); } const code = this._expandArrayVariables(thisEntry.code); newBodyReader.copyBuffer(code); newHeader = newHeaderReader.write(); newBody = newBodyReader.write(); } } let newPayloadLen = VarUint32ToArray(newHeader.length + newBody.length); reader.copyBuffer(newPayloadLen); reader.copyBuffer(newHeader); reader.copyBuffer(newBody); return { locals, instructions, }; } _readInstruction(reader) { reader.commitBytes(); reader.setAnchor(); const opcode = reader.readUint8(); let oldTarget; let newTarget; let arg; switch (opcode) { case OP_UNREACHABLE: case OP_NOP: case OP_ELSE: case OP_END: case OP_RETURN: case OP_DROP: case OP_SELECT: case OP_I32_EQZ: case OP_I32_EQ: case OP_I32_NE: case OP_I32_LT_S: case OP_I32_LT_U: case OP_I32_GT_S: case OP_I32_GT_U: case OP_I32_LE_S: case OP_I32_LE_U: case OP_I32_GE_S: case OP_I32_GE_U: case OP_I64_EQZ: case OP_I64_EQ: case OP_I64_NE: case OP_I64_LT_S: case OP_I64_LT_U: case OP_I64_GT_S: case OP_I64_GT_U: case OP_I64_LE_S: case OP_I64_LE_U: case OP_I64_GE_S: case OP_I64_GE_U: case OP_F32_EQ: case OP_F32_NE: case OP_F32_LT: case OP_F32_GT: case OP_F32_LE: case OP_F32_GE: case OP_F64_EQ: case OP_F64_NE: case OP_F64_LT: case OP_F64_GT: case OP_F64_LE: case OP_F64_GE: case OP_I32_CLZ: case OP_I32_CTZ: case OP_I32_POPCNT: case OP_I32_ADD: case OP_I32_SUB: case OP_I32_MUL: case OP_I32_DIV_S: case OP_I32_DIV_U: case OP_I32_REM_S: case OP_I32_REM_U: case OP_I32_AND: case OP_I32_OR: case OP_I32_XOR: case OP_I32_SHL: case OP_I32_SHR_S: case OP_I32_SHR_U: case OP_I32_ROTL: case OP_I32_ROTR: case OP_I64_CLZ: case OP_I64_CTZ: case OP_I64_POPCNT: case OP_I64_ADD: case OP_I64_SUB: case OP_I64_MUL: case OP_I64_DIV_S: case OP_I64_DIV_U: case OP_I64_REM_S: case OP_I64_REM_U: case OP_I64_AND: case OP_I64_OR: case OP_I64_XOR: case OP_I64_SHL: case OP_I64_SHR_S: case OP_I64_SHR_U: case OP_I64_ROTL: case OP_I64_ROTR: case OP_F32_ABS: case OP_F32_NEG: case OP_F32_CEIL: case OP_F32_FLOOR: case OP_F32_TRUNC: case OP_F32_NEAREST: case OP_F32_SQRT: case OP_F32_ADD: case OP_F32_SUB: case OP_F32_MUL: case OP_F32_DIV: case OP_F32_MIN: case OP_F32_MAX: case OP_F32_COPYSIGN: case OP_F64_ABS: case OP_F64_NEG: case OP_F64_CEIL: case OP_F64_FLOOR: case OP_F64_TRUNC: case OP_F64_NEAREST: case OP_F64_SQRT: case OP_F64_ADD: case OP_F64_SUB: case OP_F64_MUL: case OP_F64_DIV: case OP_F64_MIN: case OP_F64_MAX: case OP_F64_COPYSIGN: case OP_I32_WRAP_I64: case OP_I32_TRUNC_S_F32: case OP_I32_TRUNC_U_F32: case OP_I32_TRUNC_S_F64: case OP_I32_TRUNC_U_F64: case OP_I64_EXTEND_S_I32: case OP_I64_EXTEND_U_I32: case OP_I64_TRUNC_S_F32: case OP_I64_TRUNC_U_F32: case OP_I64_TRUNC_S_F64: case OP_I64_TRUNC_U_F64: case OP_F32_CONVERT_S_I32: case OP_F32_CONVERT_U_I32: case OP_F32_CONVERT_S_I64: case OP_F32_CONVERT_U_I64: case OP_F32_DEMOTE_F64: case OP_F64_CONVERT_S_I32: case OP_F64_CONVERT_U_I32: case OP_F64_CONVERT_S_I64: case OP_F64_CONVERT_U_I64: case OP_F64_PROMOTE_F32: case OP_I32_REINTERPRET_F32: case OP_I64_REINTERPRET_F64: case OP_F32_REINTERPRET_I32: case OP_F64_REINTERPRET_I64: break; case OP_BLOCK: case OP_LOOP: case OP_IF: case OP_MEMORY_SIZE: case OP_MEMORY_GROW: reader.readUint8(); break; case OP_BR: case OP_BR_IF: case OP_GET_LOCAL: case OP_SET_LOCAL: case OP_TEE_LOCAL: case OP_I32_CONST: case OP_I64_CONST: reader.readVarUint32(); break; case OP_GET_GLOBAL: case OP_SET_GLOBAL: reader.commitBytes(); oldTarget = reader.readVarUint32(); newTarget = this._getAdjustedGlobalIndex(oldTarget); reader.copyBuffer(VarUint32ToArray(newTarget)); break; case OP_F32_CONST: reader.readBytes(4); break; case OP_F64_CONST: reader.readBytes(8); break; case OP_I32_LOAD: case OP_I64_LOAD: case OP_F32_LOAD: case OP_F64_LOAD: case OP_I32_LOAD8_S: case OP_I32_LOAD8_U: case OP_I32_LOAD16_S: case OP_I32_LOAD16_U: case OP_I64_LOAD8_S: case OP_I64_LOAD8_U: case OP_I64_LOAD16_S: case OP_I64_LOAD16_U: case OP_I64_LOAD32_S: case OP_I64_LOAD32_U: case OP_I32_STORE: case OP_I64_STORE: case OP_F32_STORE: case OP_F64_STORE: case OP_I32_STORE8: case OP_I32_STORE16: case OP_I64_STORE8: case OP_I64_STORE16: case OP_I64_STORE32: reader.readVarUint32(); reader.readVarUint32(); break; case OP_BR_TABLE: const count = reader.readVarUint32(); for (let i = 0; i < count; i++) { reader.readVarUint32(); } reader.readVarUint32(); break; case OP_CALL: reader.commitBytes(); oldTarget = reader.readVarUint32(); newTarget = this._getAdjustedFunctionIndex(oldTarget); reader.copyBuffer(VarUint32ToArray(newTarget)); break; case OP_CALL_INDIRECT: reader.readVarUint32(); reader.readUint8(); break; case OP_I32_EXTEND8_S: case OP_I32_EXTEND16_S: case OP_I64_EXTEND8_S: case OP_I64_EXTEND16_S: case OP_I64_EXTEND32_S: break; case OP_BULK_MEMORY: arg = reader.readUint8(); switch (arg) { case ARG_MEMORY_INIT: case ARG_TABLE_INIT: reader.readVarUint32(); reader.readUint8(); break; case ARG_DATA_DROP: case ARG_ELEM_DROP: reader.readVarUint32(); break; case ARG_MEMORY_COPY: case ARG_TABLE_COPY: reader.readUint8(); reader.readUint8(); break; case ARG_MEMORY_FILL: reader.readUint8(); break; default: throw new Error("Unknown argument '" + arg + "' for OP_BULK_MEMORY"); } break; case OP_SIMD: arg = reader.readUint8(); switch (arg) { case SIMD_I8X16_SWIZZLE: case SIMD_I8X16_SPLAT: case SIMD_I16X8_SPLAT: case SIMD_I32X4_SPLAT: case SIMD_I64X2_SPLAT: case SIMD_F32X4_SPLAT: case SIMD_F64X2_SPLAT: case SIMD_I8X16_EQ: case SIMD_I8X16_NE: case SIMD_I8X16_LT_S: case SIMD_I8X16_LT_U: case SIMD_I8X16_GT_S: case SIMD_I8X16_GT_U: case SIMD_I8X16_LE_S: case SIMD_I8X16_LE_U: case SIMD_I8X16_GE_S: case SIMD_I8X16_GE_U: case SIMD_I16X8_EQ: case SIMD_I16X8_NE: case SIMD_I16X8_LT_S: case SIMD_I16X8_LT_U: case SIMD_I16X8_GT_S: case SIMD_I16X8_GT_U: case SIMD_I16X8_LE_S: case SIMD_I16X8_LE_U: case SIMD_I16X8_GE_S: case SIMD_I16X8_GE_U: case SIMD_I32X4_EQ: case SIMD_I32X4_NE: case SIMD_I32X4_LT_S: case SIMD_I32X4_LT_U: case SIMD_I32X4_GT_S: case SIMD_I32X4_GT_U: case SIMD_I32X4_LE_S: case SIMD_I32X4_LE_U: case SIMD_I32X4_GE_S: case SIMD_I32X4_GE_U: case SIMD_F32X4_EQ: case SIMD_F32X4_NE: case SIMD_F32X4_LT: case SIMD_F32X4_GT: case SIMD_F32X4_LE: case SIMD_F32X4_GE: case SIMD_F64X2_EQ: case SIMD_F64X2_NE: case SIMD_F64X2_LT: case SIMD_F64X2_GT: case SIMD_F64X2_LE: case SIMD_F64X2_GE: case SIMD_V128_NOT: case SIMD_V128_AND: case SIMD_V128_ANDNOT: case SIMD_V128_OR: case SIMD_V128_XOR: case SIMD_V128_BITSELECT: case SIMD_I8X16_ABS: case SIMD_I8X16_NEG: case SIMD_I8X16_ALL_TRUE: case SIMD_I8X16_BITMASK: case SIMD_I8X16_NARROW_I16X8_S: case SIMD_I8X16_NARROW_I16X8_U: case SIMD_I8X16_SHL: case SIMD_I8X16_SHR_S: case SIMD_I8X16_SHR_U: case SIMD_I8X16_ADD: case SIMD_I8X16_ADD_SAT_S: case SIMD_I8X16_ADD_SAT_U: case SIMD_I8X16_SUB: case SIMD_I8X16_SUB_SAT_S: case SIMD_I8X16_SUB_SAT_U: case SIMD_I8X16_MIN_S: case SIMD_I8X16_MIN_U: case SIMD_I8X16_MAX_S: case SIMD_I8X16_MAX_U: case SIMD_I8X16_AVGR_U: case SIMD_I16X8_ABS: case SIMD_I16X8_NEG: case SIMD_I16X8_ALL_TRUE: case SIMD_I16X8_BITMASK: case SIMD_I16X8_NARROW_I32X4_S: case SIMD_I16X8_NARROW_I32X4_U: case SIMD_I16X8_EXTEND_LOW_I8X16_S: case SIMD_I16X8_EXTEND_HIGH_I8X16_S: case SIMD_I16X8_EXTEND_LOW_I8X16_U: case SIMD_I16X8_EXTEND_HIGH_I8X16_U: case SIMD_I16X8_SHL: case SIMD_I16X8_SHR_S: case SIMD_I16X8_SHR_U: case SIMD_I16X8_ADD: case SIMD_I16X8_ADD_SAT_S: case SIMD_I16X8_ADD_SAT_U: case SIMD_I16X8_SUB: case SIMD_I16X8_SUB_SAT_S: case SIMD_I16X8_SUB_SAT_U: case SIMD_I16X8_MUL: case SIMD_I16X8_MIN_S: case SIMD_I16X8_MIN_U: case SIMD_I16X8_MAX_S: case SIMD_I16X8_MAX_U: case SIMD_I16X8_AVGR_U: case SIMD_I32X4_ABS: case SIMD_I32X4_NEG: case SIMD_I32X4_ALL_TRUE: case SIMD_I32X4_BITMASK: case SIMD_I32X4_EXTEND_LOW_I16X8_S: case SIMD_I32X4_EXTEND_HIGH_I16X8_S: case SIMD_I32X4_EXTEND_LOW_I16X8_U: case SIMD_I32X4_EXTEND_HIGH_I16X8_U: case SIMD_I32X4_SHL: case SIMD_I32X4_SHR_S: case SIMD_I32X4_SHR_U: case SIMD_I32X4_ADD: case SIMD_I32X4_SUB: case SIMD_I32X4_MUL: case SIMD_I32X4_MIN_S: case SIMD_I32X4_MIN_U: case SIMD_I32X4_MAX_S: case SIMD_I32X4_MAX_U: case SIMD_I32X4_DOT_I16X8_S: case SIMD_I64X2_ABS: case SIMD_I64X2_NEG: case SIMD_I64X2_BITMASK: case SIMD_I64X2_EXTEND_LOW_I32X4_S: case SIMD_I64X2_EXTEND_HIGH_I32X4_S: case SIMD_I64X2_EXTEND_LOW_I32X4_U: case SIMD_I64X2_EXTEND_HIGH_I32X4_U: case SIMD_I64X2_SHL: case SIMD_I64X2_SHR_S: case SIMD_I64X2_SHR_U: case SIMD_I64X2_ADD: case SIMD_I64X2_SUB: case SIMD_I64X2_MUL: case SIMD_F32X4_CEIL: case SIMD_F32X4_FLOOR: case SIMD_F32X4_TRUNC: case SIMD_F32X4_NEAREST: case SIMD_F64X2_CEIL: case SIMD_F64X2_FLOOR: case SIMD_F64X2_TRUNC: case SIMD_F64X2_NEAREST: case SIMD_F32X4_ABS: case SIMD_F32X4_NEG: case SIMD_F32X4_SQRT: case SIMD_F32X4_ADD: case SIMD_F32X4_SUB: case SIMD_F32X4_MUL: case SIMD_F32X4_DIV: case SIMD_F32X4_MIN: case SIMD_F32X4_MAX: case SIMD_F32X4_PMIN: case SIMD_F32X4_PMAX: case SIMD_F64X2_ABS: case SIMD_F64X2_NEG: case SIMD_F64X2_SQRT: case SIMD_F64X2_ADD: case SIMD_F64X2_SUB: case SIMD_F64X2_MUL: case SIMD_F64X2_DIV: case SIMD_F64X2_MIN: case SIMD_F64X2_MAX: case SIMD_F64X2_PMIN: case SIMD_F64X2_PMAX: case SIMD_I32X4_TRUNC_SAT_F32X4_S: case SIMD_I32X4_TRUNC_SAT_F32X4_U: case SIMD_F32X4_CONVERT_I32X4_S: case SIMD_F32X4_CONVERT_I32X4_U: case SIMD_I16X8_EXTMUL_LOW_I8X16_S: case SIMD_I16X8_EXTMUL_HIGH_I8X16_S: case SIMD_I16X8_EXTMUL_LOW_I8X16_U: case SIMD_I16X8_EXTMUL_HIGH_I8X16_U: case SIMD_I32X4_EXTMUL_LOW_I16X8_S: case SIMD_I32X4_EXTMUL_HIGH_I16X8_S: case SIMD_I32X4_EXTMUL_LOW_I16X8_U: case SIMD_I32X4_EXTMUL_HIGH_I16X8_U: case SIMD_I64X2_EXTMUL_LOW_I32X4_S: case SIMD_I64X2_EXTMUL_HIGH_I32X4_S: case SIMD_I64X2_EXTMUL_LOW_I32X4_U: case SIMD_I64X2_EXTMUL_HIGH_I32X4_U: case SIMD_I16X8_Q15MULR_SAT_S: case SIMD_V128_ANY_TRUE: case SIMD_I64X2_EQ: case SIMD_I64X2_NE: case SIMD_I64X2_LT_S: case SIMD_I64X2_GT_S: case SIMD_I64X2_LE_S: case SIMD_I64X2_GE_S: case SIMD_I64X2_ALL_TRUE: case SIMD_F64X2_CONVERT_LOW_I32X4_S: case SIMD_F64X2_CONVERT_LOW_I32X4_U: case SIMD_I32X4_TRUNC_SAT_F64X2_S_ZERO: case SIMD_I32X4_TRUNC_SAT_F64X2_U_ZERO: case SIMD_F32X4_DEMOTE_F64X2_ZERO: case SIMD_F64X2_PROMOTE_LOW_F32X4: case SIMD_I8X16_POPCNT: case SIMD_I16X8_EXTADD_PAIRWISE_I8X16_S: case SIMD_I16X8_EXTADD_PAIRWISE_I8X16_U: case SIMD_I32X4_EXTADD_PAIRWISE_I16X8_S: case SIMD_I32X4_EXTADD_PAIRWISE_I16X8_U: break; case SIMD_V128_LOAD: case SIMD_V128_LOAD8X8_S: case SIMD_V128_LOAD8X8_U: case SIMD_V128_LOAD16X4_S: case SIMD_V128_LOAD16X4_U: case SIMD_V128_LOAD32X2_S: case SIMD_V128_LOAD32X2_U: case SIMD_V128_LOAD8_SPLAT: case SIMD_V128_LOAD16_SPLAT: case SIMD_V128_LOAD32_SPLAT: case SIMD_V128_LOAD64_SPLAT: case SIMD_V128_STORE: case SIMD_V128_LOAD32_ZERO: case SIMD_V128_LOAD64_ZERO: reader.readVarUint32(); reader.readVarUint32(); break; case SIMD_I8X16_SHUFFLE: case SIMD_V128_CONST: reader.readUint128(); break; case SIMD_I8X16_EXTRACT_LANE_S: case SIMD_I8X16_EXTRACT_LANE_U: case SIMD_I8X16_REPLACE_LANE: case SIMD_I16X8_EXTRACT_LANE_S: case SIMD_I16X8_EXTRACT_LANE_U: case SIMD_I16X8_REPLACE_LANE: case SIMD_I32X4_EXTRACT_LANE: case SIMD_I32X4_REPLACE_LANE: case SIMD_I64X2_EXTRACT_LANE: case SIMD_I64X2_REPLACE_LANE: case SIMD_F32X4_EXTRACT_LANE: case SIMD_F32X4_REPLACE_LANE: case SIMD_F64X2_EXTRACT_LANE: case SIMD_F64X2_REPLACE_LANE: reader.readUint8(); break; case SIMD_V128_LOAD8_LANE: case SIMD_V128_LOAD16_LANE: case SIMD_V128_LOAD32_LANE: case SIMD_V128_LOAD64_LANE: case SIMD_V128_STORE8_LANE: case SIMD_V128_STORE16_LANE: case SIMD_V128_STORE32_LANE: case SIMD_V128_STORE64_LANE: reader.readVarUint32(); reader.readVarUint32(); reader.readUint8(); break; default: throw new Error("Unknown argument '" + arg + "' for OP_SIMD"); } break; case OP_ATOMIC: arg = reader.readUint8(); if (arg > ARG_I64_ATOMIC_RMW_CMPXCHG_32U || (arg > 0x2 && arg < 0x10)) { throw new Error("Unknown argument '" + arg + "' for OP_ATOMIC. Probably parsing incorrectly"); } //reader.readUint8(); reader.readVarUint32(); reader.readVarUint32(); break; default: throw new Error("Unknown opcode '" + opcode + "'. Probably parsing incorrectly"); } reader.commitBytes(); if (typeof this._instructionCallbacks[opcode] !== "undefined") { const instrBytes = reader.readFromAnchor(); const callbackResults = this._instructionCallbacks[opcode](instrBytes); reader.writeAtAnchor(callbackResults); } else if (typeof this._globalInstructionCallback === "function") { const instrBytes = reader.readFromAnchor(); const callbackResults = this._globalInstructionCallback[opcode](instrBytes); reader.writeAtAnchor(callbackResults); } const fullInstruction = reader.readFromAnchor(); return fullInstruction; } // Converts an index into the FUNCTION section into an adjusted index into the program's // function table _funcSectionIndexToFuncTableIndex(index) { return index + this._importFuncCount + this._importFuncNewCount; } // Helper function used to "fix up" an index into the function table when the table // may have been modified _getAdjustedFunctionIndex(index) { if (index >= this._importFuncCount) { return index + this._importFuncNewCount; } return index; } // Helper function used to "fix up" an index into the global table when the table // may have been modified _getAdjustedGlobalIndex(index) { if (index >= this._importGlobalCount) { return index + this._importGlobalNewCount; } return index; } } /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/getFullHash */ /******/ (() => { /******/ __webpack_require__.h = () => ("9d69bd5e69d212be6030") /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /******/ /* webpack/runtime/make namespace object */ /******/ (() => { /******/ // define __esModule on exports /******/ __webpack_require__.r = (exports) => { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ })(); /******/ /************************************************************************/ /******/ /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module is referenced by other modules so it can't be inlined /******/ var __webpack_exports__ = __webpack_require__("./src/mod.ts"); /******/ window.UnityWebModkit = __webpack_exports__; /******/ /******/ })() ;