From 4c8cd3261dc3c4aed40a80f24361eba9c4d89182 Mon Sep 17 00:00:00 2001 From: zurdi Date: Sat, 20 Dec 2025 00:56:40 +0000 Subject: [PATCH 01/31] feat: add ROM patcher functionality with UI integration - Implemented z-worker.js and zip.min.js for handling zip operations. - Added PatcherBtn component to the main navigation for easy access to the patcher. - Created Patcher.vue view for applying patches to ROM files. - Updated router to include a new route for the patcher. - Enhanced navigation store with a method to navigate to the patcher. - Integrated file input handling for ROM and patch files in the Patcher component. - Added error handling and loading states during patch application. --- frontend/assets/patcherjs/RomPatcher.js | 465 ++++++ .../assets/powered_by_rom_patcher_js.png | Bin 0 -> 799 bytes frontend/assets/patcherjs/modules/BinFile.js | 495 ++++++ .../patcherjs/modules/HashCalculator.js | 275 ++++ .../modules/RomPatcher.format.aps_gba.js | 123 ++ .../modules/RomPatcher.format.aps_n64.js | 207 +++ .../modules/RomPatcher.format.bdf.js | 323 ++++ .../modules/RomPatcher.format.bps.js | 501 ++++++ .../modules/RomPatcher.format.ips.js | 289 ++++ .../modules/RomPatcher.format.pmsr.js | 93 ++ .../modules/RomPatcher.format.ppf.js | 245 +++ .../modules/RomPatcher.format.rup.js | 387 +++++ .../modules/RomPatcher.format.ups.js | 219 +++ .../modules/RomPatcher.format.vcdiff.js | 394 +++++ frontend/assets/patcherjs/modules/bz2/LICENSE | 19 + frontend/assets/patcherjs/modules/bz2/bz2.js | 1 + .../assets/patcherjs/modules/zip.js/LICENSE | 28 + .../patcherjs/modules/zip.js/inflate.js | 1418 +++++++++++++++++ .../patcherjs/modules/zip.js/z-worker.js | 116 ++ .../patcherjs/modules/zip.js/zip.min.js | 973 +++++++++++ .../common/Navigation/MainAppBar.vue | 2 + .../common/Navigation/PatcherBtn.vue | 48 + frontend/src/plugins/router.ts | 9 + frontend/src/stores/navigation.ts | 4 + frontend/src/views/Patcher.vue | 207 +++ 25 files changed, 6841 insertions(+) create mode 100644 frontend/assets/patcherjs/RomPatcher.js create mode 100644 frontend/assets/patcherjs/assets/powered_by_rom_patcher_js.png create mode 100644 frontend/assets/patcherjs/modules/BinFile.js create mode 100644 frontend/assets/patcherjs/modules/HashCalculator.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.aps_gba.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.aps_n64.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.bdf.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.bps.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.ips.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.pmsr.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.ppf.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.rup.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.ups.js create mode 100644 frontend/assets/patcherjs/modules/RomPatcher.format.vcdiff.js create mode 100644 frontend/assets/patcherjs/modules/bz2/LICENSE create mode 100644 frontend/assets/patcherjs/modules/bz2/bz2.js create mode 100644 frontend/assets/patcherjs/modules/zip.js/LICENSE create mode 100644 frontend/assets/patcherjs/modules/zip.js/inflate.js create mode 100644 frontend/assets/patcherjs/modules/zip.js/z-worker.js create mode 100644 frontend/assets/patcherjs/modules/zip.js/zip.min.js create mode 100644 frontend/src/components/common/Navigation/PatcherBtn.vue create mode 100644 frontend/src/views/Patcher.vue diff --git a/frontend/assets/patcherjs/RomPatcher.js b/frontend/assets/patcherjs/RomPatcher.js new file mode 100644 index 000000000..66f9b840d --- /dev/null +++ b/frontend/assets/patcherjs/RomPatcher.js @@ -0,0 +1,465 @@ +/* + * Rom Patcher JS core + * A ROM patcher/builder made in JavaScript, can be implemented as a webapp or a Node.JS CLI tool + * By Marc Robledo https://www.marcrobledo.com + * Sourcecode: https://github.com/marcrobledo/RomPatcher.js + * License: + * + * MIT License + * + * Copyright (c) 2016-2025 Marc Robledo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const RomPatcher = (function () { + const TOO_BIG_ROM_SIZE = 67108863; + + const HEADERS_INFO = [ + { + extensions: ["nes"], + size: 16, + romSizeMultiple: 1024, + name: "iNES", + } /* https://www.nesdev.org/wiki/INES */, + { + extensions: ["fds"], + size: 16, + romSizeMultiple: 65500, + name: "fwNES", + } /* https://www.nesdev.org/wiki/FDS_file_format */, + { extensions: ["lnx"], size: 64, romSizeMultiple: 1024, name: "LNX" }, + { + extensions: ["sfc", "smc", "swc", "fig"], + size: 512, + romSizeMultiple: 262144, + name: "SNES copier", + }, + ]; + + const GAME_BOY_NINTENDO_LOGO = [ + 0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b, 0x03, 0x73, 0x00, 0x83, + 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e, + 0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99, + ]; + + const _getRomSystem = function (binFile) { + /* to-do: add more systems */ + const extension = binFile.getExtension().trim(); + if (binFile.fileSize > 0x0200 && binFile.fileSize % 4 === 0) { + if ( + (extension === "gb" || extension === "gbc") && + binFile.fileSize % 0x4000 === 0 + ) { + binFile.seek(0x0104); + var valid = true; + for (var i = 0; i < GAME_BOY_NINTENDO_LOGO.length && valid; i++) { + if (GAME_BOY_NINTENDO_LOGO[i] !== binFile.readU8()) valid = false; + } + if (valid) return "gb"; + } else if (extension === "md" || extension === "bin") { + binFile.seek(0x0100); + if (/SEGA (GENESIS|MEGA DR)/.test(binFile.readString(12))) return "smd"; + } else if (extension === "z64" && binFile.fileSize >= 0x400000) { + return "n64"; + } + } else if (extension === "fds" && binFile.fileSize % 65500 === 0) { + return "fds"; + } + return null; + }; + const _getRomAdditionalChecksum = function (binFile) { + /* to-do: add more systems */ + const romSystem = _getRomSystem(binFile); + if (romSystem === "n64") { + binFile.seek(0x3c); + const cartId = binFile.readString(3); + + binFile.seek(0x10); + const crc = binFile.readBytes(8).reduce(function (hex, b) { + if (b < 16) return hex + "0" + b.toString(16); + else return hex + b.toString(16); + }, ""); + return cartId + " (" + crc + ")"; + } + return null; + }; + + return { + parsePatchFile: function (patchFile) { + if (!(patchFile instanceof BinFile)) + throw new Error("Patch file is not an instance of BinFile"); + + patchFile.littleEndian = false; + patchFile.seek(0); + + var header = patchFile.readString(8); + var patch = null; + if (header.startsWith(IPS.MAGIC)) { + patch = IPS.fromFile(patchFile); + } else if (header.startsWith(UPS.MAGIC)) { + patch = UPS.fromFile(patchFile); + } else if (header.startsWith(APS.MAGIC)) { + patch = APS.fromFile(patchFile); + } else if (header.startsWith(APSGBA.MAGIC)) { + patch = APSGBA.fromFile(patchFile); + } else if (header.startsWith(BPS.MAGIC)) { + patch = BPS.fromFile(patchFile); + } else if (header.startsWith(RUP.MAGIC)) { + patch = RUP.fromFile(patchFile); + } else if (header.startsWith(PPF.MAGIC)) { + patch = PPF.fromFile(patchFile); + } else if (header.startsWith(BDF.MAGIC)) { + patch = BDF.fromFile(patchFile); + } else if (header.startsWith(PMSR.MAGIC)) { + patch = PMSR.fromFile(patchFile); + } else if (header.startsWith(VCDIFF.MAGIC)) { + patch = VCDIFF.fromFile(patchFile); + } + + if (patch) patch._originalPatchFile = patchFile; + + return patch; + }, + + validateRom: function (romFile, patch, skipHeaderSize) { + if (!(romFile instanceof BinFile)) + throw new Error("ROM file is not an instance of BinFile"); + else if (typeof patch !== "object") + throw new Error("Unknown patch format"); + + if (typeof skipHeaderSize !== "number" || skipHeaderSize < 0) + skipHeaderSize = 0; + + if ( + typeof patch.validateSource === "function" && + !patch.validateSource(romFile, skipHeaderSize) + ) { + return false; + } + return true; + }, + + applyPatch: function (romFile, patch, optionsParam) { + if (!(romFile instanceof BinFile)) + throw new Error("ROM file is not an instance of BinFile"); + else if (typeof patch !== "object") + throw new Error("Unknown patch format"); + + const options = { + requireValidation: false, + removeHeader: false, + addHeader: false, + fixChecksum: false, + outputSuffix: true, + }; + if (typeof optionsParam === "object") { + if (typeof optionsParam.requireValidation !== "undefined") + options.requireValidation = !!optionsParam.requireValidation; + if (typeof optionsParam.removeHeader !== "undefined") + options.removeHeader = !!optionsParam.removeHeader; + if (typeof optionsParam.addHeader !== "undefined") + options.addHeader = !!optionsParam.addHeader; + if (typeof optionsParam.fixChecksum !== "undefined") + options.fixChecksum = !!optionsParam.fixChecksum; + if (typeof optionsParam.outputSuffix !== "undefined") + options.outputSuffix = !!optionsParam.outputSuffix; + } + + var extractedHeader = false; + var fakeHeaderSize = 0; + if (options.removeHeader) { + const headerInfo = RomPatcher.isRomHeadered(romFile); + if (headerInfo) { + const splitData = RomPatcher.removeHeader(romFile); + extractedHeader = splitData.header; + romFile = splitData.rom; + } + } else if (options.addHeader) { + const headerInfo = RomPatcher.canRomGetHeader(romFile); + if (headerInfo) { + fakeHeaderSize = headerInfo.fileSize; + romFile = RomPatcher.addFakeHeader(romFile); + } + } + + if ( + options.requireValidation && + !RomPatcher.validateRom(romFile, patch) + ) { + throw new Error("Invalid input ROM checksum"); + } + + var patchedRom = patch.apply(romFile); + if (extractedHeader) { + /* reinsert header */ + if (options.fixChecksum) RomPatcher.fixRomHeaderChecksum(patchedRom); + + const patchedRomWithHeader = new BinFile( + extractedHeader.fileSize + patchedRom.fileSize, + ); + patchedRomWithHeader.fileName = patchedRom.fileName; + patchedRomWithHeader.fileType = patchedRom.fileType; + extractedHeader.copyTo( + patchedRomWithHeader, + 0, + extractedHeader.fileSize, + ); + patchedRom.copyTo( + patchedRomWithHeader, + 0, + patchedRom.fileSize, + extractedHeader.fileSize, + ); + + patchedRom = patchedRomWithHeader; + } else if (fakeHeaderSize) { + /* remove fake header */ + const patchedRomWithoutFakeHeader = patchedRom.slice(fakeHeaderSize); + + if (options.fixChecksum) + RomPatcher.fixRomHeaderChecksum(patchedRomWithoutFakeHeader); + + patchedRom = patchedRomWithoutFakeHeader; + } else if (options.fixChecksum) { + RomPatcher.fixRomHeaderChecksum(patchedRom); + } + + if (options.outputSuffix) { + patchedRom.fileName = romFile.fileName.replace( + /\.([^\.]*?)$/, + " (patched).$1", + ); + if (patchedRom.unpatched) + patchedRom.fileName = patchedRom.fileName.replace( + " (patched)", + " (unpatched)", + ); + } else if (patch._originalPatchFile) { + patchedRom.fileName = patch._originalPatchFile.fileName.replace( + /\.\w+$/i, + /\.\w+$/i.test(romFile.fileName) + ? romFile.fileName.match(/\.\w+$/i)[0] + : "", + ); + } else { + patchedRom.fileName = romFile.fileName; + } + + return patchedRom; + }, + + createPatch: function (originalFile, modifiedFile, format, metadata) { + if (!(originalFile instanceof BinFile)) + throw new Error("Original ROM file is not an instance of BinFile"); + else if (!(modifiedFile instanceof BinFile)) + throw new Error("Modified ROM file is not an instance of BinFile"); + + if (typeof format === "string") format = format.trim().toLowerCase(); + else if (typeof format === "undefined") format = "ips"; + + var patch; + if (format === "ips") { + patch = IPS.buildFromRoms(originalFile, modifiedFile); + } else if (format === "bps") { + patch = BPS.buildFromRoms( + originalFile, + modifiedFile, + originalFile.fileSize <= 4194304, + ); + } else if (format === "ppf") { + patch = PPF.buildFromRoms(originalFile, modifiedFile); + } else if (format === "ups") { + patch = UPS.buildFromRoms(originalFile, modifiedFile); + } else if (format === "aps") { + patch = APS.buildFromRoms(originalFile, modifiedFile); + } else if (format === "rup") { + patch = RUP.buildFromRoms( + originalFile, + modifiedFile, + metadata && metadata.Description ? metadata.Description : null, + ); + } else if (format === "ebp") { + patch = IPS.buildFromRoms(originalFile, modifiedFile, metadata); + } else { + throw new Error("Invalid patch format"); + } + + if ( + !(format === "ppf" && originalFile.fileSize > modifiedFile.fileSize) && //skip verification if PPF and PPF+modified size>original size + modifiedFile.hashCRC32() !== patch.apply(originalFile).hashCRC32() + ) { + //throw new Error('Unexpected error: verification failed. Patched file and modified file mismatch. Please report this bug.'); + } + return patch; + }, + + /* check if ROM can inject a fake header (for patches that require a headered ROM) */ + canRomGetHeader: function (romFile) { + if (romFile.fileSize <= 0x600000) { + const compatibleHeader = HEADERS_INFO.find( + (headerInfo) => + headerInfo.extensions.indexOf(romFile.getExtension()) !== -1 && + romFile.fileSize % headerInfo.romSizeMultiple === 0, + ); + if (compatibleHeader) { + return { + name: compatibleHeader.name, + size: compatibleHeader.size, + }; + } + } + return null; + }, + + /* check if ROM has a known header */ + isRomHeadered: function (romFile) { + if (romFile.fileSize <= 0x600200 && romFile.fileSize % 1024 !== 0) { + const compatibleHeader = HEADERS_INFO.find( + (headerInfo) => + headerInfo.extensions.indexOf(romFile.getExtension()) !== -1 && + (romFile.fileSize - headerInfo.size) % + headerInfo.romSizeMultiple === + 0, + ); + if (compatibleHeader) { + return { + name: compatibleHeader.name, + size: compatibleHeader.size, + }; + } + } + return null; + }, + + /* remove ROM header */ + removeHeader: function (romFile) { + const headerInfo = RomPatcher.isRomHeadered(romFile); + if (headerInfo) { + return { + header: romFile.slice(0, headerInfo.size), + rom: romFile.slice(headerInfo.size), + }; + } + return null; + }, + + /* add fake ROM header */ + addFakeHeader: function (romFile) { + const headerInfo = RomPatcher.canRomGetHeader(romFile); + if (headerInfo) { + const romWithFakeHeader = new BinFile( + headerInfo.size + romFile.fileSize, + ); + romWithFakeHeader.fileName = romFile.fileName; + romWithFakeHeader.fileType = romFile.fileType; + romFile.copyTo(romWithFakeHeader, 0, romFile.fileSize, headerInfo.size); + + //add a correct FDS header + if (_getRomSystem(romWithFakeHeader) === "fds") { + romWithFakeHeader.seek(0); + romWithFakeHeader.writeBytes([ + 0x46, + 0x44, + 0x53, + 0x1a, + romFile.fileSize / 65500, + ]); + } + + romWithFakeHeader.fakeHeader = true; + + return romWithFakeHeader; + } + return null; + }, + + /* get ROM internal checksum, if possible */ + fixRomHeaderChecksum: function (romFile) { + const romSystem = _getRomSystem(romFile); + + if (romSystem === "gb") { + /* get current checksum */ + romFile.seek(0x014d); + const currentChecksum = romFile.readU8(); + + /* calculate checksum */ + var newChecksum = 0x00; + romFile.seek(0x0134); + for (var i = 0; i <= 0x18; i++) { + newChecksum = ((newChecksum - romFile.readU8() - 1) >>> 0) & 0xff; + } + + /* fix checksum */ + if (currentChecksum !== newChecksum) { + console.log("fixed Game Boy checksum"); + romFile.seek(0x014d); + romFile.writeU8(newChecksum); + return true; + } + } else if (romSystem === "smd") { + /* get current checksum */ + romFile.seek(0x018e); + const currentChecksum = romFile.readU16(); + + /* calculate checksum */ + var newChecksum = 0x0000; + romFile.seek(0x0200); + while (!romFile.isEOF()) { + newChecksum = ((newChecksum + romFile.readU16()) >>> 0) & 0xffff; + } + + /* fix checksum */ + if (currentChecksum !== newChecksum) { + console.log("fixed Megadrive/Genesis checksum"); + romFile.seek(0x018e); + romFile.writeU16(newChecksum); + return true; + } + } + + return false; + }, + + /* get ROM additional checksum info, if possible */ + getRomAdditionalChecksum: function (romFile) { + return _getRomAdditionalChecksum(romFile); + }, + + /* check if ROM is too big */ + isRomTooBig: function (romFile) { + return romFile && romFile.fileSize > TOO_BIG_ROM_SIZE; + }, + }; +})(); + +if (typeof module !== "undefined" && module.exports) { + module.exports = RomPatcher; + + IPS = require("./modules/RomPatcher.format.ips"); + UPS = require("./modules/RomPatcher.format.ups"); + APS = require("./modules/RomPatcher.format.aps_n64"); + APSGBA = require("./modules/RomPatcher.format.aps_gba"); + BPS = require("./modules/RomPatcher.format.bps"); + RUP = require("./modules/RomPatcher.format.rup"); + PPF = require("./modules/RomPatcher.format.ppf"); + BDF = require("./modules/RomPatcher.format.bdf"); + PMSR = require("./modules/RomPatcher.format.pmsr"); + VCDIFF = require("./modules/RomPatcher.format.vcdiff"); +} diff --git a/frontend/assets/patcherjs/assets/powered_by_rom_patcher_js.png b/frontend/assets/patcherjs/assets/powered_by_rom_patcher_js.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7033a147467ddebbaa697060bb3ece06160e0b GIT binary patch literal 799 zcmV+)1K|9LP)glISr82rMrKi(re&7+p2O>V;CJEhxaxBV7a!Csod56q&v36N#pm}7LI~EOF6n+% zb7FbrA&iG-paxSs24(OY1n@JEQG=!bGe9`f(Wc_Ru!8`pDsDn_Ar}$D86Z^c{-DCG z$U*`x6QGoVmSH>U6yzrk1hXvt6_3M174AWao}GlxT7n+j#vGi1S3zpA8leLXFc#TP z5q!ofBwL*b+ylt>@Pe8B73EcpGX}pc$E1f>OJ1F996ykbk#=Pv(mu>+X(zZ6iQ z2(toCL@)u&u`EIij#}$)l%W{iPJl-!$7t+yDbkKpSfGHN{RDV`&*S`4Z z(d-Hpz`15SkfROh6l=mBEV53E9pF1QVujY+dG6EktGAxb)6H3E0vyI;wc4wJ&Du{u zwM$1#$6}FY0u!K_WGXbJ-7Y)cjw+duhMPJ~G^|x2c zkw}1r40HrPtfEhF%li8c!q9|cx}sm3P6XbBVOn!@2NdWSR_jf;#-$rx0<^oV^J$FI z&mI+fsS00j)R0aZF?#ewVGX8^z7 dKcYrFz(4L5(3SMW{JH=D002ovPDHLkV1hl;XzKs~ literal 0 HcmV?d00001 diff --git a/frontend/assets/patcherjs/modules/BinFile.js b/frontend/assets/patcherjs/modules/BinFile.js new file mode 100644 index 000000000..62013b64a --- /dev/null +++ b/frontend/assets/patcherjs/modules/BinFile.js @@ -0,0 +1,495 @@ +/* + * BinFile.js (last update: 2024-08-21) + * by Marc Robledo, https://www.marcrobledo.com + * + * a JS class for reading/writing sequentially binary data from/to a file + * that allows much more manipulation than simple DataView + * compatible with both browsers and Node.js + * + * MIT License + * + * Copyright (c) 2014-2024 Marc Robledo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +function BinFile(source, onLoad) { + this.littleEndian = false; + this.offset = 0; + this._lastRead = null; + this._offsetsStack = []; + + if ( + BinFile.RUNTIME_ENVIROMENT === "browser" && + (source instanceof File || + source instanceof FileList || + (source instanceof HTMLElement && + source.tagName === "INPUT" && + source.type === "file")) + ) { + if (source instanceof HTMLElement) source = source.files; + if (source instanceof FileList) source = source[0]; + + this.fileName = source.name; + this.fileType = source.type; + this.fileSize = source.size; + + if (typeof window.FileReader !== "function") + throw new Error("Incompatible browser"); + + this._fileReader = new FileReader(); + this._fileReader.addEventListener( + "load", + function () { + this.binFile._u8array = new Uint8Array(this.result); + + if (typeof onLoad === "function") onLoad(this.binFile); + }, + false, + ); + + this._fileReader.binFile = this; + + this._fileReader.readAsArrayBuffer(source); + } else if ( + BinFile.RUNTIME_ENVIROMENT === "node" && + typeof source === "string" + ) { + if (!nodeFs.existsSync(source)) throw new Error(source + " does not exist"); + + const arrayBuffer = nodeFs.readFileSync(source); + + this.fileName = nodePath.basename(source); + this.fileType = nodeFs.statSync(source).type; + this.fileSize = arrayBuffer.byteLength; + + this._u8array = new Uint8Array(arrayBuffer); + + if (typeof onLoad === "function") onLoad(this); + } else if (source instanceof BinFile) { + /* if source is another BinFile, clone it */ + this.fileName = source.fileName; + this.fileType = source.fileType; + this.fileSize = source.fileSize; + + this._u8array = new Uint8Array(source._u8array.buffer.slice()); + + if (typeof onLoad === "function") onLoad(this); + } else if (source instanceof ArrayBuffer) { + this.fileName = "file.bin"; + this.fileType = "application/octet-stream"; + this.fileSize = source.byteLength; + + this._u8array = new Uint8Array(source); + + if (typeof onLoad === "function") onLoad(this); + } else if (ArrayBuffer.isView(source)) { + /* source is TypedArray */ + this.fileName = "file.bin"; + this.fileType = "application/octet-stream"; + this.fileSize = source.buffer.byteLength; + + this._u8array = new Uint8Array(source.buffer); + + if (typeof onLoad === "function") onLoad(this); + } else if (typeof source === "number") { + /* source is integer, create new empty file */ + this.fileName = "file.bin"; + this.fileType = "application/octet-stream"; + this.fileSize = source; + + this._u8array = new Uint8Array(new ArrayBuffer(source)); + + if (typeof onLoad === "function") onLoad(this); + } else { + throw new Error("invalid BinFile source"); + } +} +BinFile.RUNTIME_ENVIROMENT = (function () { + if (typeof window === "object" && typeof window.document === "object") + return "browser"; + else if ( + typeof WorkerGlobalScope === "function" && + self instanceof WorkerGlobalScope + ) + return "webworker"; + else if ( + typeof require === "function" && + typeof process === "object" && + typeof process.versions === "object" && + typeof process.versions.node === "string" + ) + return "node"; + else return null; +})(); +BinFile.DEVICE_LITTLE_ENDIAN = (function () { + /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness */ + var buffer = new ArrayBuffer(2); + new DataView(buffer).setInt16(0, 256, true /* littleEndian */); + // Int16Array uses the platform's endianness. + return new Int16Array(buffer)[0] === 256; +})(); + +BinFile.prototype.push = function () { + this._offsetsStack.push(this.offset); +}; +BinFile.prototype.pop = function () { + this.seek(this._offsetsStack.pop()); +}; +BinFile.prototype.seek = function (offset) { + this.offset = offset; +}; +BinFile.prototype.skip = function (nBytes) { + this.offset += nBytes; +}; +BinFile.prototype.isEOF = function () { + return !(this.offset < this.fileSize); +}; +BinFile.prototype.slice = function (offset, len, doNotClone) { + if (typeof offset !== "number" || offset < 0) offset = 0; + else if (offset >= this.fileSize) throw new Error("out of bounds slicing"); + else offset = Math.floor(offset); + + if ( + typeof len !== "number" || + offset < 0 || + offset + len >= this.fileSize.length + ) + len = this.fileSize - offset; + else if (len === 0) throw new Error("zero length provided for slicing"); + else len = Math.floor(len); + + if (offset === 0 && len === this.fileSize && doNotClone) return this; + + var newFile = new BinFile(this._u8array.buffer.slice(offset, offset + len)); + newFile.fileName = this.fileName; + newFile.fileType = this.fileType; + newFile.littleEndian = this.littleEndian; + return newFile; +}; +BinFile.prototype.prependBytes = function (bytes) { + var newFile = new BinFile(this.fileSize + bytes.length); + newFile.seek(0); + newFile.writeBytes(bytes); + this.copyTo(newFile, 0, this.fileSize, bytes.length); + + this.fileSize = newFile.fileSize; + this._u8array = newFile._u8array; + return this; +}; +BinFile.prototype.removeLeadingBytes = function (nBytes) { + this.seek(0); + var oldData = this.readBytes(nBytes); + var newFile = this.slice(nBytes.length); + + this.fileSize = newFile.fileSize; + this._u8array = newFile._u8array; + return oldData; +}; + +BinFile.prototype.copyTo = function (target, offsetSource, len, offsetTarget) { + if (!(target instanceof BinFile)) + throw new Error("target is not a BinFile object"); + + if (typeof offsetTarget !== "number") offsetTarget = offsetSource; + + len = len || this.fileSize - offsetSource; + + for (var i = 0; i < len; i++) { + target._u8array[offsetTarget + i] = this._u8array[offsetSource + i]; + } +}; + +BinFile.prototype.save = function () { + if (BinFile.RUNTIME_ENVIROMENT === "browser") { + var fileBlob = new Blob([this._u8array], { type: this.fileType }); + var blobUrl = URL.createObjectURL(fileBlob); + var a = document.createElement("a"); + a.href = blobUrl; + a.download = this.fileName; + document.body.appendChild(a); + a.dispatchEvent(new MouseEvent("click")); + URL.revokeObjectURL(blobUrl); + document.body.removeChild(a); + } else if (BinFile.RUNTIME_ENVIROMENT === "node") { + nodeFs.writeFileSync(this.fileName, Buffer.from(this._u8array.buffer)); + } else { + throw new Error("invalid runtime environment, can't save file"); + } +}; + +BinFile.prototype.getExtension = function () { + var ext = this.fileName ? this.fileName.toLowerCase().match(/\.(\w+)$/) : ""; + + return ext ? ext[1] : ""; +}; +BinFile.prototype.getName = function () { + return this.fileName.replace( + new RegExp("\\." + this.getExtension() + "$", "i"), + "", + ); +}; +BinFile.prototype.setExtension = function (newExtension) { + return (this.fileName = this.getName() + "." + newExtension); +}; +BinFile.prototype.setName = function (newName) { + return (this.fileName = newName + "." + this.getExtension()); +}; + +BinFile.prototype.readU8 = function () { + this._lastRead = this._u8array[this.offset++]; + + return this._lastRead; +}; +BinFile.prototype.readU16 = function () { + if (this.littleEndian) + this._lastRead = + this._u8array[this.offset] + (this._u8array[this.offset + 1] << 8); + else + this._lastRead = + (this._u8array[this.offset] << 8) + this._u8array[this.offset + 1]; + + this.offset += 2; + return this._lastRead >>> 0; +}; +BinFile.prototype.readU24 = function () { + if (this.littleEndian) + this._lastRead = + this._u8array[this.offset] + + (this._u8array[this.offset + 1] << 8) + + (this._u8array[this.offset + 2] << 16); + else + this._lastRead = + (this._u8array[this.offset] << 16) + + (this._u8array[this.offset + 1] << 8) + + this._u8array[this.offset + 2]; + + this.offset += 3; + return this._lastRead >>> 0; +}; +BinFile.prototype.readU32 = function () { + if (this.littleEndian) + this._lastRead = + this._u8array[this.offset] + + (this._u8array[this.offset + 1] << 8) + + (this._u8array[this.offset + 2] << 16) + + (this._u8array[this.offset + 3] << 24); + else + this._lastRead = + (this._u8array[this.offset] << 24) + + (this._u8array[this.offset + 1] << 16) + + (this._u8array[this.offset + 2] << 8) + + this._u8array[this.offset + 3]; + + this.offset += 4; + return this._lastRead >>> 0; +}; +BinFile.prototype.readU64 = function () { + if (this.littleEndian) + this._lastRead = + this._u8array[this.offset] + + (this._u8array[this.offset + 1] << 8) + + (this._u8array[this.offset + 2] << 16) + + (this._u8array[this.offset + 3] << 24) + + (this._u8array[this.offset + 4] << 32) + + (this._u8array[this.offset + 5] << 40) + + (this._u8array[this.offset + 6] << 48) + + (this._u8array[this.offset + 7] << 56); + else + this._lastRead = + (this._u8array[this.offset] << 56) + + (this._u8array[this.offset + 1] << 48) + + (this._u8array[this.offset + 2] << 40) + + (this._u8array[this.offset + 3] << 32) + + (this._u8array[this.offset + 4] << 24) + + (this._u8array[this.offset + 5] << 16) + + (this._u8array[this.offset + 6] << 8) + + this._u8array[this.offset + 7]; + this.offset += 8; + return this._lastRead >>> 0; +}; + +BinFile.prototype.readBytes = function (len) { + this._lastRead = new Array(len); + for (var i = 0; i < len; i++) { + this._lastRead[i] = this._u8array[this.offset + i]; + } + + this.offset += len; + return this._lastRead; +}; + +BinFile.prototype.readString = function (len) { + this._lastRead = ""; + for ( + var i = 0; + i < len && + this.offset + i < this.fileSize && + this._u8array[this.offset + i] > 0; + i++ + ) + this._lastRead = + this._lastRead + String.fromCharCode(this._u8array[this.offset + i]); + + this.offset += len; + return this._lastRead; +}; + +BinFile.prototype.writeU8 = function (u8) { + this._u8array[this.offset++] = u8; +}; +BinFile.prototype.writeU16 = function (u16) { + if (this.littleEndian) { + this._u8array[this.offset] = u16 & 0xff; + this._u8array[this.offset + 1] = u16 >> 8; + } else { + this._u8array[this.offset] = u16 >> 8; + this._u8array[this.offset + 1] = u16 & 0xff; + } + + this.offset += 2; +}; +BinFile.prototype.writeU24 = function (u24) { + if (this.littleEndian) { + this._u8array[this.offset] = u24 & 0x0000ff; + this._u8array[this.offset + 1] = (u24 & 0x00ff00) >> 8; + this._u8array[this.offset + 2] = (u24 & 0xff0000) >> 16; + } else { + this._u8array[this.offset] = (u24 & 0xff0000) >> 16; + this._u8array[this.offset + 1] = (u24 & 0x00ff00) >> 8; + this._u8array[this.offset + 2] = u24 & 0x0000ff; + } + + this.offset += 3; +}; +BinFile.prototype.writeU32 = function (u32) { + if (this.littleEndian) { + this._u8array[this.offset] = u32 & 0x000000ff; + this._u8array[this.offset + 1] = (u32 & 0x0000ff00) >> 8; + this._u8array[this.offset + 2] = (u32 & 0x00ff0000) >> 16; + this._u8array[this.offset + 3] = (u32 & 0xff000000) >> 24; + } else { + this._u8array[this.offset] = (u32 & 0xff000000) >> 24; + this._u8array[this.offset + 1] = (u32 & 0x00ff0000) >> 16; + this._u8array[this.offset + 2] = (u32 & 0x0000ff00) >> 8; + this._u8array[this.offset + 3] = u32 & 0x000000ff; + } + + this.offset += 4; +}; + +BinFile.prototype.writeBytes = function (a) { + for (var i = 0; i < a.length; i++) this._u8array[this.offset + i] = a[i]; + + this.offset += a.length; +}; + +BinFile.prototype.writeString = function (str, len) { + len = len || str.length; + for (var i = 0; i < str.length && i < len; i++) + this._u8array[this.offset + i] = str.charCodeAt(i); + + for (; i < len; i++) this._u8array[this.offset + i] = 0x00; + + this.offset += len; +}; + +BinFile.prototype.swapBytes = function (swapSize, newFile) { + if (typeof swapSize !== "number") { + swapSize = 4; + } + + if (this.fileSize % swapSize !== 0) { + throw new Error("file size is not divisible by " + swapSize); + } + + var swappedFile = new BinFile(this.fileSize); + this.seek(0); + while (!this.isEOF()) { + swappedFile.writeBytes(this.readBytes(swapSize).reverse()); + } + + if (newFile) { + swappedFile.fileName = this.fileName; + swappedFile.fileType = this.fileType; + + return swappedFile; + } else { + this._u8array = swappedFile._u8array; + + return this; + } +}; + +BinFile.prototype.hashSHA1 = async function (start, len) { + if ( + typeof HashCalculator !== "object" || + typeof HashCalculator.sha1 !== "function" + ) + throw new Error("no Hash object found or missing sha1 function"); + + return HashCalculator.sha1(this.slice(start, len, true)._u8array.buffer); +}; +BinFile.prototype.hashMD5 = function (start, len) { + if ( + typeof HashCalculator !== "object" || + typeof HashCalculator.md5 !== "function" + ) + throw new Error("no Hash object found or missing md5 function"); + + return HashCalculator.md5(this.slice(start, len, true)._u8array.buffer); +}; +BinFile.prototype.hashCRC32 = function (start, len) { + if ( + typeof HashCalculator !== "object" || + typeof HashCalculator.crc32 !== "function" + ) + throw new Error("no Hash object found or missing crc32 function"); + + return HashCalculator.crc32(this.slice(start, len, true)._u8array.buffer); +}; +BinFile.prototype.hashAdler32 = function (start, len) { + if ( + typeof HashCalculator !== "object" || + typeof HashCalculator.adler32 !== "function" + ) + throw new Error("no Hash object found or missing adler32 function"); + + return HashCalculator.adler32(this.slice(start, len, true)._u8array.buffer); +}; +BinFile.prototype.hashCRC16 = function (start, len) { + if ( + typeof HashCalculator !== "object" || + typeof HashCalculator.crc16 !== "function" + ) + throw new Error("no Hash object found or missing crc16 function"); + + return HashCalculator.crc16(this.slice(start, len, true)._u8array.buffer); +}; + +if ( + BinFile.RUNTIME_ENVIROMENT === "node" && + typeof module !== "undefined" && + module.exports +) { + module.exports = BinFile; + HashCalculator = require("./HashCalculator"); + nodePath = require("path"); + nodeFs = require("fs"); +} diff --git a/frontend/assets/patcherjs/modules/HashCalculator.js b/frontend/assets/patcherjs/modules/HashCalculator.js new file mode 100644 index 000000000..5ae8488cb --- /dev/null +++ b/frontend/assets/patcherjs/modules/HashCalculator.js @@ -0,0 +1,275 @@ +/* + * HashCalculator.js (last update: 2021-08-15) + * by Marc Robledo, https://www.marcrobledo.com + * + * data hash calculator (CRC32, MD5, SHA1, ADLER-32, CRC16) + * + * MIT License + * + * Copyright (c) 2016-2021 Marc Robledo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const HashCalculator = (function () { + const HEX_CHR = "0123456789abcdef".split(""); + + /* MD5 helpers */ + const _add32 = function (a, b) { + return (a + b) & 0xffffffff; + }; + const _md5cycle = function (x, k) { + var a = x[0], + b = x[1], + c = x[2], + d = x[3]; + a = ff(a, b, c, d, k[0], 7, -680876936); + d = ff(d, a, b, c, k[1], 12, -389564586); + c = ff(c, d, a, b, k[2], 17, 606105819); + b = ff(b, c, d, a, k[3], 22, -1044525330); + a = ff(a, b, c, d, k[4], 7, -176418897); + d = ff(d, a, b, c, k[5], 12, 1200080426); + c = ff(c, d, a, b, k[6], 17, -1473231341); + b = ff(b, c, d, a, k[7], 22, -45705983); + a = ff(a, b, c, d, k[8], 7, 1770035416); + d = ff(d, a, b, c, k[9], 12, -1958414417); + c = ff(c, d, a, b, k[10], 17, -42063); + b = ff(b, c, d, a, k[11], 22, -1990404162); + a = ff(a, b, c, d, k[12], 7, 1804603682); + d = ff(d, a, b, c, k[13], 12, -40341101); + c = ff(c, d, a, b, k[14], 17, -1502002290); + b = ff(b, c, d, a, k[15], 22, 1236535329); + a = gg(a, b, c, d, k[1], 5, -165796510); + d = gg(d, a, b, c, k[6], 9, -1069501632); + c = gg(c, d, a, b, k[11], 14, 643717713); + b = gg(b, c, d, a, k[0], 20, -373897302); + a = gg(a, b, c, d, k[5], 5, -701558691); + d = gg(d, a, b, c, k[10], 9, 38016083); + c = gg(c, d, a, b, k[15], 14, -660478335); + b = gg(b, c, d, a, k[4], 20, -405537848); + a = gg(a, b, c, d, k[9], 5, 568446438); + d = gg(d, a, b, c, k[14], 9, -1019803690); + c = gg(c, d, a, b, k[3], 14, -187363961); + b = gg(b, c, d, a, k[8], 20, 1163531501); + a = gg(a, b, c, d, k[13], 5, -1444681467); + d = gg(d, a, b, c, k[2], 9, -51403784); + c = gg(c, d, a, b, k[7], 14, 1735328473); + b = gg(b, c, d, a, k[12], 20, -1926607734); + a = hh(a, b, c, d, k[5], 4, -378558); + d = hh(d, a, b, c, k[8], 11, -2022574463); + c = hh(c, d, a, b, k[11], 16, 1839030562); + b = hh(b, c, d, a, k[14], 23, -35309556); + a = hh(a, b, c, d, k[1], 4, -1530992060); + d = hh(d, a, b, c, k[4], 11, 1272893353); + c = hh(c, d, a, b, k[7], 16, -155497632); + b = hh(b, c, d, a, k[10], 23, -1094730640); + a = hh(a, b, c, d, k[13], 4, 681279174); + d = hh(d, a, b, c, k[0], 11, -358537222); + c = hh(c, d, a, b, k[3], 16, -722521979); + b = hh(b, c, d, a, k[6], 23, 76029189); + a = hh(a, b, c, d, k[9], 4, -640364487); + d = hh(d, a, b, c, k[12], 11, -421815835); + c = hh(c, d, a, b, k[15], 16, 530742520); + b = hh(b, c, d, a, k[2], 23, -995338651); + a = ii(a, b, c, d, k[0], 6, -198630844); + d = ii(d, a, b, c, k[7], 10, 1126891415); + c = ii(c, d, a, b, k[14], 15, -1416354905); + b = ii(b, c, d, a, k[5], 21, -57434055); + a = ii(a, b, c, d, k[12], 6, 1700485571); + d = ii(d, a, b, c, k[3], 10, -1894986606); + c = ii(c, d, a, b, k[10], 15, -1051523); + b = ii(b, c, d, a, k[1], 21, -2054922799); + a = ii(a, b, c, d, k[8], 6, 1873313359); + d = ii(d, a, b, c, k[15], 10, -30611744); + c = ii(c, d, a, b, k[6], 15, -1560198380); + b = ii(b, c, d, a, k[13], 21, 1309151649); + a = ii(a, b, c, d, k[4], 6, -145523070); + d = ii(d, a, b, c, k[11], 10, -1120210379); + c = ii(c, d, a, b, k[2], 15, 718787259); + b = ii(b, c, d, a, k[9], 21, -343485551); + x[0] = _add32(a, x[0]); + x[1] = _add32(b, x[1]); + x[2] = _add32(c, x[2]); + x[3] = _add32(d, x[3]); + }; + const _md5blk = function (d) { + var md5blks = [], + i; + for (i = 0; i < 64; i += 4) + md5blks[i >> 2] = + d[i] + (d[i + 1] << 8) + (d[i + 2] << 16) + (d[i + 3] << 24); + return md5blks; + }; + const _cmn = function (q, a, b, x, s, t) { + a = _add32(_add32(a, q), _add32(x, t)); + return _add32((a << s) | (a >>> (32 - s)), b); + }; + const ff = function (a, b, c, d, x, s, t) { + return _cmn((b & c) | (~b & d), a, b, x, s, t); + }; + const gg = function (a, b, c, d, x, s, t) { + return _cmn((b & d) | (c & ~d), a, b, x, s, t); + }; + const hh = function (a, b, c, d, x, s, t) { + return _cmn(b ^ c ^ d, a, b, x, s, t); + }; + const ii = function (a, b, c, d, x, s, t) { + return _cmn(c ^ (b | ~d), a, b, x, s, t); + }; + + /* CRC32 helpers */ + const CRC32_TABLE = (function () { + var c, + crcTable = []; + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; + crcTable[n] = c; + } + return crcTable; + })(); + + /* Adler-32 helpers */ + const ADLER32_MOD = 0xfff1; + + const generateUint8Array = function (arrayBuffer, offset, len) { + if (typeof offset !== "number" || offset < 0) offset = 0; + else if (offset < arrayBuffer.byteLength) offset = Math.floor(offset); + else throw new Error("out of bounds slicing"); + + if ( + typeof len !== "number" || + len < 0 || + offset + len >= arrayBuffer.byteLength.length + ) + len = arrayBuffer.byteLength - offset; + else if (len > 0) len = Math.floor(len); + else throw new Error("zero length provided for slicing"); + + return new Uint8Array(arrayBuffer, offset, len); + }; + + return { + /* SHA-1 using WebCryptoAPI */ + sha1: async function sha1(arrayBuffer, offset, len) { + if (typeof window === "undefined" || typeof window.crypto === "undefined") + throw new Error("Web Crypto API is not available"); + + const u8array = generateUint8Array(arrayBuffer, offset, len); + if (u8array.byteLength !== arrayBuffer.byteLength) { + arrayBuffer = arrayBuffer.slice( + u8array.byteOffset, + u8array.byteOffset + u8array.byteLength, + ); + } + + const hash = await window.crypto.subtle.digest("SHA-1", arrayBuffer); + + const bytes = new Uint8Array(hash); + let hexString = ""; + for (let i = 0; i < bytes.length; i++) + hexString += + bytes[i] < 16 ? "0" + bytes[i].toString(16) : bytes[i].toString(16); + return hexString; + }, + + /* MD5 - from Joseph's Myers - http://www.myersdaily.org/joseph/javascript/md5.js */ + md5: function (arrayBuffer, offset, len) { + let u8array = generateUint8Array(arrayBuffer, offset, len); + + var n = u8array.byteLength, + state = [1732584193, -271733879, -1732584194, 271733878], + i; + for (i = 64; i <= u8array.byteLength; i += 64) + _md5cycle(state, _md5blk(u8array.slice(i - 64, i))); + u8array = u8array.slice(i - 64); + var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for (i = 0; i < u8array.byteLength; i++) + tail[i >> 2] |= u8array[i] << ((i % 4) << 3); + tail[i >> 2] |= 0x80 << ((i % 4) << 3); + if (i > 55) { + _md5cycle(state, tail); + for (i = 0; i < 16; i++) tail[i] = 0; + } + tail[14] = n * 8; + tail[15] = Math.floor(n / 536870912) >>> 0; //if file is bigger than 512Mb*8, value is bigger than 32 bits, so it needs two words to store its length + _md5cycle(state, tail); + + for (var i = 0; i < state.length; i++) { + var s = "", + j = 0; + for (; j < 4; j++) + s += + HEX_CHR[(state[i] >> (j * 8 + 4)) & 0x0f] + + HEX_CHR[(state[i] >> (j * 8)) & 0x0f]; + state[i] = s; + } + return state.join(""); + }, + + /* CRC32 - from Alex - https://stackoverflow.com/a/18639999 */ + crc32: function (arrayBuffer, offset, len) { + const u8array = generateUint8Array(arrayBuffer, offset, len); + + var crc = 0 ^ -1; + + for (var i = 0; i < u8array.byteLength; i++) + crc = (crc >>> 8) ^ CRC32_TABLE[(crc ^ u8array[i]) & 0xff]; + + return (crc ^ -1) >>> 0; + }, + + /* Adler-32 - https://en.wikipedia.org/wiki/Adler-32#Example_implementation */ + adler32: function (arrayBuffer, offset, len) { + const u8array = generateUint8Array(arrayBuffer, offset, len); + + var a = 1, + b = 0; + + for (var i = 0; i < u8array.byteLength; i++) { + a = (a + u8array[i]) % ADLER32_MOD; + b = (b + a) % ADLER32_MOD; + } + + return ((b << 16) | a) >>> 0; + }, + + /* CRC16/CCITT-FALSE */ + crc16: function (arrayBuffer, offset, len) { + const u8array = generateUint8Array(arrayBuffer, offset, len); + + var crc = 0xffff; + + var offset = 0; + + for (var i = 0; i < u8array.byteLength; i++) { + crc ^= u8array[offset++] << 8; + for (j = 0; j < 8; ++j) { + crc = (crc & 0x8000) >>> 0 ? (crc << 1) ^ 0x1021 : crc << 1; + } + } + + return crc & 0xffff; + }, + }; +})(); + +if (typeof module !== "undefined" && module.exports) { + module.exports = HashCalculator; +} diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.aps_gba.js b/frontend/assets/patcherjs/modules/RomPatcher.format.aps_gba.js new file mode 100644 index 000000000..4d987fdf9 --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.aps_gba.js @@ -0,0 +1,123 @@ +/* APS (GBA) module for Rom Patcher JS v20230331 - Marc Robledo 2017-2023 - http://www.marcrobledo.com/license */ +/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(GBA) */ + +const APS_GBA_MAGIC = "APS1"; +const APS_GBA_BLOCK_SIZE = 0x010000; //64Kb +const APS_GBA_RECORD_SIZE = 4 + 2 + 2 + APS_GBA_BLOCK_SIZE; +if (typeof module !== "undefined" && module.exports) { + module.exports = APSGBA; +} +function APSGBA() { + this.sourceSize = 0; + this.targetSize = 0; + this.records = []; +} +APSGBA.prototype.addRecord = function ( + offset, + sourceCrc16, + targetCrc16, + xorBytes, +) { + this.records.push({ + offset: offset, + sourceCrc16: sourceCrc16, + targetCrc16: targetCrc16, + xorBytes: xorBytes, + }); +}; +APSGBA.prototype.toString = function () { + var s = "Total records: " + this.records.length; + s += "\nInput file size: " + this.sourceSize; + s += "\nOutput file size: " + this.targetSize; + return s; +}; +APSGBA.prototype.validateSource = function (sourceFile) { + if (sourceFile.fileSize !== this.sourceSize) return false; + + for (var i = 0; i < this.records.length; i++) { + sourceFile.seek(this.records[i].offset); + var bytes = sourceFile.readBytes(APS_GBA_BLOCK_SIZE); + if ( + sourceFile.hashCRC16(this.records[i].offset, APS_GBA_BLOCK_SIZE) !== + this.records[i].sourceCrc16 + ) + return false; + } + + return true; +}; +APSGBA.prototype.export = function (fileName) { + var patchFileSize = 12 + this.records.length * APS_GBA_RECORD_SIZE; + + tempFile = new BinFile(patchFileSize); + tempFile.littleEndian = true; + tempFile.fileName = fileName + ".aps"; + tempFile.writeString(APS_GBA_MAGIC, APS_GBA_MAGIC.length); + tempFile.writeU32(this.sourceSize); + tempFile.writeU32(this.targetSize); + + for (var i = 0; i < this.records.length; i++) { + tempFile.writeU32(this.records[i].offset); + tempFile.writeU16(this.records[i].sourceCrc16); + tempFile.writeU16(this.records[i].targetCrc16); + tempFile.writeBytes(this.records[i].xorBytes); + } + + return tempFile; +}; + +APSGBA.prototype.apply = function (romFile, validate) { + if (validate && !this.validateSource(romFile)) { + throw new Error("Source ROM checksum mismatch"); + } + + tempFile = new BinFile(this.targetSize); + romFile.copyTo(tempFile, 0, romFile.fileSize); + + for (var i = 0; i < this.records.length; i++) { + romFile.seek(this.records[i].offset); + tempFile.seek(this.records[i].offset); + for (var j = 0; j < APS_GBA_BLOCK_SIZE; j++) { + tempFile.writeU8(romFile.readU8() ^ this.records[i].xorBytes[j]); + } + + if ( + validate && + tempFile.hashCRC16(this.records[i].offset, APS_GBA_BLOCK_SIZE) !== + this.records[i].targetCrc16 + ) { + throw new Error("Target ROM checksum mismatch"); + } + } + + return tempFile; +}; + +APSGBA.MAGIC = APS_GBA_MAGIC; + +APSGBA.fromFile = function (patchFile) { + patchFile.seek(0); + patchFile.littleEndian = true; + + if ( + patchFile.readString(APS_GBA_MAGIC.length) !== APS_GBA_MAGIC || + patchFile.fileSize < 12 + APS_GBA_RECORD_SIZE || + (patchFile.fileSize - 12) % APS_GBA_RECORD_SIZE !== 0 + ) + return null; + + var patch = new APSGBA(); + + patch.sourceSize = patchFile.readU32(); + patch.targetSize = patchFile.readU32(); + + while (!patchFile.isEOF()) { + var offset = patchFile.readU32(); + var sourceCrc16 = patchFile.readU16(); + var targetCrc16 = patchFile.readU16(); + var xorBytes = patchFile.readBytes(APS_GBA_BLOCK_SIZE); + + patch.addRecord(offset, sourceCrc16, targetCrc16, xorBytes); + } + return patch; +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.aps_n64.js b/frontend/assets/patcherjs/modules/RomPatcher.format.aps_n64.js new file mode 100644 index 000000000..67b08f1fe --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.aps_n64.js @@ -0,0 +1,207 @@ +/* APS (N64) module for Rom Patcher JS v20180930 - Marc Robledo 2017-2018 - http://www.marcrobledo.com/license */ +/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(N64) */ + +const APS_N64_MAGIC = "APS10"; +const APS_RECORD_RLE = 0x0000; +const APS_RECORD_SIMPLE = 0x01; +const APS_N64_MODE = 0x01; +if (typeof module !== "undefined" && module.exports) { + module.exports = APS; +} +function APS() { + this.records = []; + this.headerType = 0; + this.encodingMethod = 0; + this.description = "no description"; + + this.header = {}; +} +APS.prototype.addRecord = function (o, d) { + this.records.push({ offset: o, type: APS_RECORD_SIMPLE, data: d }); +}; +APS.prototype.addRLERecord = function (o, b, l) { + this.records.push({ offset: o, type: APS_RECORD_RLE, length: l, byte: b }); +}; +APS.prototype.toString = function () { + var s = "Total records: " + this.records.length; + s += "\nHeader type: " + this.headerType; + if (this.headerType === APS_N64_MODE) { + s += " (N64)"; + } + s += "\nEncoding method: " + this.encodingMethod; + s += "\nDescription: " + this.description; + s += "\nHeader: " + JSON.stringify(this.header); + return s; +}; +APS.prototype.validateSource = function (sourceFile) { + if (this.headerType === APS_N64_MODE) { + sourceFile.seek(0x3c); + if (sourceFile.readString(3) !== this.header.cartId) return false; + + sourceFile.seek(0x10); + var crc = sourceFile.readBytes(8); + for (var i = 0; i < 8; i++) { + if (crc[i] !== this.header.crc[i]) return false; + } + } + return true; +}; +APS.prototype.getValidationInfo = function () { + if (this.headerType === APS_N64_MODE) { + return ( + this.header.cartId + + " (" + + this.header.crc.reduce(function (hex, b) { + if (b < 16) return hex + "0" + b.toString(16); + else return hex + b.toString(16); + }, "") + + ")" + ); + } + return null; +}; +APS.prototype.export = function (fileName) { + var patchFileSize = 61; + if (this.headerType === APS_N64_MODE) patchFileSize += 17; + + for (var i = 0; i < this.records.length; i++) { + if (this.records[i].type === APS_RECORD_RLE) patchFileSize += 7; + else patchFileSize += 5 + this.records[i].data.length; //offset+length+data + } + + tempFile = new BinFile(patchFileSize); + tempFile.littleEndian = true; + tempFile.fileName = fileName + ".aps"; + tempFile.writeString(APS_N64_MAGIC, APS_N64_MAGIC.length); + tempFile.writeU8(this.headerType); + tempFile.writeU8(this.encodingMethod); + tempFile.writeString(this.description, 50); + + if (this.headerType === APS_N64_MODE) { + tempFile.writeU8(this.header.originalN64Format); + tempFile.writeString(this.header.cartId, 3); + tempFile.writeBytes(this.header.crc); + tempFile.writeBytes(this.header.pad); + } + tempFile.writeU32(this.header.sizeOutput); + + for (var i = 0; i < this.records.length; i++) { + var rec = this.records[i]; + tempFile.writeU32(rec.offset); + if (rec.type === APS_RECORD_RLE) { + tempFile.writeU8(0x00); + tempFile.writeU8(rec.byte); + tempFile.writeU8(rec.length); + } else { + tempFile.writeU8(rec.data.length); + tempFile.writeBytes(rec.data); + } + } + + return tempFile; +}; + +APS.prototype.apply = function (romFile, validate) { + if (validate && !this.validateSource(romFile)) { + throw new Error("Source ROM checksum mismatch"); + } + + tempFile = new BinFile(this.header.sizeOutput); + romFile.copyTo(tempFile, 0, tempFile.fileSize); + + for (var i = 0; i < this.records.length; i++) { + tempFile.seek(this.records[i].offset); + if (this.records[i].type === APS_RECORD_RLE) { + for (var j = 0; j < this.records[i].length; j++) + tempFile.writeU8(this.records[i].byte); + } else { + tempFile.writeBytes(this.records[i].data); + } + } + + return tempFile; +}; + +APS.MAGIC = APS_N64_MAGIC; + +APS.fromFile = function (patchFile) { + var patch = new APS(); + patchFile.littleEndian = true; + + patchFile.seek(5); + patch.headerType = patchFile.readU8(); + patch.encodingMethod = patchFile.readU8(); + patch.description = patchFile.readString(50); + + var seek; + if (patch.headerType === APS_N64_MODE) { + patch.header.originalN64Format = patchFile.readU8(); + patch.header.cartId = patchFile.readString(3); + patch.header.crc = patchFile.readBytes(8); + patch.header.pad = patchFile.readBytes(5); + } + patch.header.sizeOutput = patchFile.readU32(); + + while (!patchFile.isEOF()) { + var offset = patchFile.readU32(); + var length = patchFile.readU8(); + + if (length === APS_RECORD_RLE) + patch.addRLERecord( + offset, + patchFile.readU8(seek), + patchFile.readU8(seek + 1), + ); + else patch.addRecord(offset, patchFile.readBytes(length)); + } + return patch; +}; + +APS.buildFromRoms = function (original, modified) { + var patch = new APS(); + + if (original.readU32() === 0x80371240) { + //is N64 ROM + patch.headerType = APS_N64_MODE; + + patch.header.originalN64Format = /\.v64$/i.test(original.fileName) ? 0 : 1; + original.seek(0x3c); + patch.header.cartId = original.readString(3); + original.seek(0x10); + patch.header.crc = original.readBytes(8); + patch.header.pad = [0, 0, 0, 0, 0]; + } + patch.header.sizeOutput = modified.fileSize; + + original.seek(0); + modified.seek(0); + + while (!modified.isEOF()) { + var b1 = original.isEOF() ? 0x00 : original.readU8(); + var b2 = modified.readU8(); + + if (b1 !== b2) { + var RLERecord = true; + var differentBytes = []; + var offset = modified.offset - 1; + + while (b1 !== b2 && differentBytes.length < 0xff) { + differentBytes.push(b2); + if (b2 !== differentBytes[0]) RLERecord = false; + + if (modified.isEOF() || differentBytes.length === 0xff) break; + + b1 = original.isEOF() ? 0x00 : original.readU8(); + b2 = modified.readU8(); + } + + if (RLERecord && differentBytes.length > 2) { + patch.addRLERecord(offset, differentBytes[0], differentBytes.length); + } else { + patch.addRecord(offset, differentBytes); + } + } + } + + return patch; +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.bdf.js b/frontend/assets/patcherjs/modules/RomPatcher.format.bdf.js new file mode 100644 index 000000000..c4602051e --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.bdf.js @@ -0,0 +1,323 @@ +/* BDF module for Rom Patcher JS v20250922 - Marc Robledo 2025 - http://www.marcrobledo.com/license */ +/* File format specification: https://www.daemonology.net/bsdiff/ */ + +const BDF_MAGIC = "BSDIFF40"; + +if (typeof module !== "undefined" && module.exports) { + module.exports = BDF; +} + +function BDF() { + this.records = []; + this.patchedSize = 0; +} + +BDF.prototype.apply = function (file) { + var tempFile = new BinFile(this.patchedSize); + + for (const record of this.records) { + for (const b of record.diff) { + tempFile.writeU8(file.readU8() + b); + } + tempFile.writeBytes(record.extra); + file.seek(file.offset + record.skip); + } + return tempFile; +}; + +BDF.MAGIC = BDF_MAGIC; + +BDF.fromFile = function (file) { + var patch = new BDF(); + + file.seek(8); + file.littleEndian = true; + var controlSize = file.readU64(); + var diffSize = file.readU64(); + patch.patchedSize = file.readU64(); + + var controlCompressed = file.readBytes(controlSize); + var diffCompressed = file.readBytes(diffSize); + var extraCompressed = file.readBytes(file.fileSize - file.offset); + + var controlFile = new BinFile(bz2.decompress(controlCompressed)); + controlFile.littleEndian = true; + var diffFile = new BinFile(bz2.decompress(diffCompressed)); + var extraFile = new BinFile(bz2.decompress(extraCompressed)); + + while (!controlFile.isEOF()) { + var diffLen = controlFile.readU64(); + var extraLen = controlFile.readU64(); + var skip = controlFile.readU64(); + if (skip & (1 << 63)) skip = -(skip & ~(1 << 63)); + var diff = diffFile.readBytes(diffLen); + var extra = extraFile.readBytes(extraLen); + patch.records.push({ diff, extra, skip }); + } + + return patch; +}; + +/* +bz2 (C) 2019-present SheetJS LLC + +Copyright 2019 SheetJS LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ +("use strict"); +const bz2 = (function $() { + let x = [ + 0, 79764919, 159529838, 222504665, 319059676, 398814059, 445009330, + 507990021, 638119352, 583659535, 797628118, 726387553, 890018660, + 835552979, 1015980042, 944750013, 1276238704, 1221641927, 1167319070, + 1095957929, 1595256236, 1540665371, 1452775106, 1381403509, 1780037320, + 1859660671, 1671105958, 1733955601, 2031960084, 2111593891, 1889500026, + 1952343757, 2552477408, 2632100695, 2443283854, 2506133561, 2334638140, + 2414271883, 2191915858, 2254759653, 3190512472, 3135915759, 3081330742, + 3009969537, 2905550212, 2850959411, 2762807018, 2691435357, 3560074640, + 3505614887, 3719321342, 3648080713, 3342211916, 3287746299, 3467911202, + 3396681109, 4063920168, 4143685023, 4223187782, 4286162673, 3779000052, + 3858754371, 3904687514, 3967668269, 881225847, 809987520, 1023691545, + 969234094, 662832811, 591600412, 771767749, 717299826, 311336399, + 374308984, 453813921, 533576470, 25881363, 88864420, 134795389, 214552010, + 2023205639, 2086057648, 1897238633, 1976864222, 1804852699, 1867694188, + 1645340341, 1724971778, 1587496639, 1516133128, 1461550545, 1406951526, + 1302016099, 1230646740, 1142491917, 1087903418, 2896545431, 2825181984, + 2770861561, 2716262478, 3215044683, 3143675388, 3055782693, 3001194130, + 2326604591, 2389456536, 2200899649, 2280525302, 2578013683, 2640855108, + 2418763421, 2498394922, 3769900519, 3832873040, 3912640137, 3992402750, + 4088425275, 4151408268, 4197601365, 4277358050, 3334271071, 3263032808, + 3476998961, 3422541446, 3585640067, 3514407732, 3694837229, 3640369242, + 1762451694, 1842216281, 1619975040, 1682949687, 2047383090, 2127137669, + 1938468188, 2001449195, 1325665622, 1271206113, 1183200824, 1111960463, + 1543535498, 1489069629, 1434599652, 1363369299, 622672798, 568075817, + 748617968, 677256519, 907627842, 853037301, 1067152940, 995781531, + 51762726, 131386257, 177728840, 240578815, 269590778, 349224269, + 429104020, 491947555, 4046411278, 4126034873, 4172115296, 4234965207, + 3794477266, 3874110821, 3953728444, 4016571915, 3609705398, 3555108353, + 3735388376, 3664026991, 3290680682, 3236090077, 3449943556, 3378572211, + 3174993278, 3120533705, 3032266256, 2961025959, 2923101090, 2868635157, + 2813903052, 2742672763, 2604032198, 2683796849, 2461293480, 2524268063, + 2284983834, 2364738477, 2175806836, 2238787779, 1569362073, 1498123566, + 1409854455, 1355396672, 1317987909, 1246755826, 1192025387, 1137557660, + 2072149281, 2135122070, 1912620623, 1992383480, 1753615357, 1816598090, + 1627664531, 1707420964, 295390185, 358241886, 404320391, 483945776, + 43990325, 106832002, 186451547, 266083308, 932423249, 861060070, + 1041341759, 986742920, 613929101, 542559546, 756411363, 701822548, + 3316196985, 3244833742, 3425377559, 3370778784, 3601682597, 3530312978, + 3744426955, 3689838204, 3819031489, 3881883254, 3928223919, 4007849240, + 4037393693, 4100235434, 4180117107, 4259748804, 2310601993, 2373574846, + 2151335527, 2231098320, 2596047829, 2659030626, 2470359227, 2550115596, + 2947551409, 2876312838, 2788305887, 2733848168, 3165939309, 3094707162, + 3040238851, 2985771188, + ], + f = [ + 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, + 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, + 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, + -2147483648, + ]; + function e($) { + let x = []; + for (let f = 0; f < $.length; f += 1) x.push([f, $[f]]); + x.push([$.length, -1]); + let e = [], + d = x[0][0], + b = x[0][1]; + for (let _ = 0; _ < x.length; _ += 1) { + let a = x[_][0], + c = x[_][1]; + if (b) + for (let t = d; t < a; t += 1) + e.push({ code: t, bits: b, symbol: void 0 }); + if (((d = a), (b = c), -1 === c)) break; + } + e.sort(($, x) => $.bits - x.bits || $.code - x.code); + let l = 0, + o = -1, + r = [], + n; + for (let s = 0; s < e.length; s += 1) { + let i = e[s]; + ((o += 1), + i.bits !== l && ((o <<= i.bits - l), (n = r[(l = i.bits)] = {})), + (i.symbol = o), + (n[o] = i)); + } + return { table: e, fastAccess: r }; + } + function d($, x) { + if (x < 0 || x >= $.length) throw RangeError("Out of bound"); + let f = $.slice(); + $.sort(($, x) => $ - x); + let e = {}; + for (let d = $.length - 1; d >= 0; d -= 1) e[$[d]] = d; + let b = []; + for (let _ = 0; _ < $.length; _ += 1) b.push(e[f[_]]++); + let a, + c = $[(a = x)], + t = []; + for (let l = 1; l < $.length; l += 1) { + let o = $[(a = b[a])]; + void 0 === o ? t.push(255) : t.push(o); + } + return (t.push(c), t.reverse(), t); + } + let b = { + decompress: function $(b, _ = !1) { + let a = 0, + c = 0, + t = 0, + l = ($) => { + if ($ >= 32) { + let x = $ >> 1; + return l(x) * (1 << x) + l($ - x); + } + for (; t < $; ) ((c = (c << 8) + b[a]), (a += 1), (t += 8)); + let e = f[$], + d = (c >> (t - $)) & e; + return ((t -= $), (c &= ~(e << t)), d); + }, + o = l(16); + if (16986 !== o) throw Error("Invalid magic"); + let r = l(8); + if (104 !== r) throw Error("Invalid method"); + let n = l(8); + if (n >= 49 && n <= 57) n -= 48; + else throw Error("Invalid blocksize"); + let s = new Uint8Array(1.5 * b.length), + i = 0, + h = -1; + for (;;) { + let u = l(48), + p = 0 | l(32); + if (54156738319193 === u) { + if (l(1)) throw Error("do not support randomised"); + let g = l(24), + w = [], + m = l(16); + for (let v = 32768; v > 0; v >>= 1) { + if (!(m & v)) { + for (let y = 0; y < 16; y += 1) w.push(!1); + continue; + } + let k = l(16); + for (let I = 32768; I > 0; I >>= 1) w.push(!!(k & I)); + } + let A = l(3); + if (A < 2 || A > 6) throw Error("Invalid number of huffman groups"); + let z = l(15), + C = [], + O = Array.from({ length: A }, ($, x) => x); + for (let F = 0; F < z; F += 1) { + let H = 0; + for (; l(1); ) + if ((H += 1) >= A) throw Error("MTF table out of range"); + let M = O[H]; + for (let P = H; P > 0; O[P] = O[--P]); + (C.push(M), (O[0] = M)); + } + let R = w.reduce(($, x) => $ + x, 0) + 2, + T = []; + for (let j = 0; j < A; j += 1) { + let q = l(5), + B = []; + for (let D = 0; D < R; D += 1) { + if (q < 0 || q > 20) + throw Error("Huffman group length outside range"); + for (; l(1); ) q -= 2 * l(1) - 1; + B.push(q); + } + T.push(e(B)); + } + let E = []; + for (let G = 0; G < w.length - 1; G += 1) w[G] && E.push(G); + let J = 0, + K = 0, + L, + N, + Q = 0, + S = 0, + U = []; + for (;;) { + for (let V in ((J -= 1) <= 0 && + ((J = 50), K <= C.length && ((L = T[C[K]]), (K += 1))), + L.fastAccess)) + if ( + Object.prototype.hasOwnProperty.call(L.fastAccess, V) && + (t < V && ((c = (c << 8) + b[a]), (a += 1), (t += 8)), + (N = L.fastAccess[V][c >> (t - V)])) + ) { + ((c &= f[(t -= V)]), (N = N.code)); + break; + } + if (N >= 0 && N <= 1) { + (0 === Q && (S = 1), (Q += S << N), (S <<= 1)); + continue; + } + { + let W = E[0]; + for (; Q > 0; Q -= 1) U.push(W); + } + if (N === R - 1) break; + { + let X = E[N - 1]; + for (let Y = N - 1; Y > 0; E[Y] = E[--Y]); + ((E[0] = X), U.push(X)); + } + } + let Z = d(U, g), + $$ = 0; + for (; $$ < Z.length; ) { + let $x = Z[$$], + $f = 1; + if ( + ($$ < Z.length - 4 && + Z[$$ + 1] === $x && + Z[$$ + 2] === $x && + Z[$$ + 3] === $x + ? (($f = Z[$$ + 4] + 4), ($$ += 5)) + : ($$ += 1), + i + $f >= s.length) + ) { + let $e = s; + (s = new Uint8Array(2 * $e.length)).set($e); + } + for (let $d = 0; $d < $f; $d += 1) + (_ && (h = (h << 8) ^ x[((h >> 24) ^ $x) & 255]), + (s[i] = $x), + (i += 1)); + } + if (_) { + let $b = -1 ^ h; + if ($b !== p) throw Error(`CRC mismatch: ${$b} !== ${p}`); + h = -1; + } + } else if (25779555029136 === u) { + l(7 & t); + break; + } else throw Error("Invalid bz2 blocktype"); + } + return s.subarray(0, i); + }, + }; + return b; +})(); diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.bps.js b/frontend/assets/patcherjs/modules/RomPatcher.format.bps.js new file mode 100644 index 000000000..4404851a0 --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.bps.js @@ -0,0 +1,501 @@ +/* BPS module for Rom Patcher JS v20240821 - Marc Robledo 2016-2024 - http://www.marcrobledo.com/license */ +/* File format specification: https://www.romhacking.net/documents/746/ */ + +const BPS_MAGIC = "BPS1"; +const BPS_ACTION_SOURCE_READ = 0; +const BPS_ACTION_TARGET_READ = 1; +const BPS_ACTION_SOURCE_COPY = 2; +const BPS_ACTION_TARGET_COPY = 3; +if (typeof module !== "undefined" && module.exports) { + module.exports = BPS; +} + +function BPS() { + this.sourceSize = 0; + this.targetSize = 0; + this.metaData = ""; + this.actions = []; + this.sourceChecksum = 0; + this.targetChecksum = 0; + this.patchChecksum = 0; +} +BPS.prototype.toString = function () { + var s = "Source size: " + this.sourceSize; + s += "\nTarget size: " + this.targetSize; + s += "\nMetadata: " + this.metaData; + s += "\n#Actions: " + this.actions.length; + return s; +}; +BPS.prototype.calculateFileChecksum = function () { + var patchFile = this.export(); + return patchFile.hashCRC32(0, patchFile.fileSize - 4); +}; +BPS.prototype.validateSource = function (romFile, headerSize) { + return this.sourceChecksum === romFile.hashCRC32(headerSize); +}; +BPS.prototype.getValidationInfo = function () { + return { + type: "CRC32", + value: this.sourceChecksum, + }; +}; +BPS.prototype.apply = function (romFile, validate) { + if (validate && !this.validateSource(romFile)) { + throw new Error("Source ROM checksum mismatch"); + } + + tempFile = new BinFile(this.targetSize); + + //patch + var sourceRelativeOffset = 0; + var targetRelativeOffset = 0; + for (var i = 0; i < this.actions.length; i++) { + var action = this.actions[i]; + + if (action.type === BPS_ACTION_SOURCE_READ) { + romFile.copyTo(tempFile, tempFile.offset, action.length); + tempFile.skip(action.length); + } else if (action.type === BPS_ACTION_TARGET_READ) { + tempFile.writeBytes(action.bytes); + } else if (action.type === BPS_ACTION_SOURCE_COPY) { + sourceRelativeOffset += action.relativeOffset; + var actionLength = action.length; + while (actionLength--) { + tempFile.writeU8(romFile._u8array[sourceRelativeOffset]); + sourceRelativeOffset++; + } + } else if (action.type === BPS_ACTION_TARGET_COPY) { + targetRelativeOffset += action.relativeOffset; + var actionLength = action.length; + while (actionLength--) { + tempFile.writeU8(tempFile._u8array[targetRelativeOffset]); + targetRelativeOffset++; + } + } + } + + if (validate && this.targetChecksum !== tempFile.hashCRC32()) { + throw new Error("Target ROM checksum mismatch"); + } + + return tempFile; +}; + +BPS.MAGIC = BPS_MAGIC; + +BPS.fromFile = function (file) { + file.readVLV = BPS_readVLV; + + file.littleEndian = true; + var patch = new BPS(); + + file.seek(4); //skip BPS1 + + patch.sourceSize = file.readVLV(); + patch.targetSize = file.readVLV(); + + var metaDataLength = file.readVLV(); + if (metaDataLength) { + patch.metaData = file.readString(metaDataLength); + } + + var endActionsOffset = file.fileSize - 12; + while (file.offset < endActionsOffset) { + var data = file.readVLV(); + var action = { type: data & 3, length: (data >> 2) + 1 }; + + if (action.type === BPS_ACTION_TARGET_READ) { + action.bytes = file.readBytes(action.length); + } else if ( + action.type === BPS_ACTION_SOURCE_COPY || + action.type === BPS_ACTION_TARGET_COPY + ) { + var relativeOffset = file.readVLV(); + action.relativeOffset = + (relativeOffset & 1 ? -1 : +1) * (relativeOffset >> 1); + } + + patch.actions.push(action); + } + + //file.seek(endActionsOffset); + patch.sourceChecksum = file.readU32(); + patch.targetChecksum = file.readU32(); + patch.patchChecksum = file.readU32(); + + if (patch.patchChecksum !== patch.calculateFileChecksum()) { + throw new Error("Patch checksum mismatch"); + } + + return patch; +}; + +function BPS_readVLV() { + var data = 0, + shift = 1; + while (true) { + var x = this.readU8(); + data += (x & 0x7f) * shift; + if (x & 0x80) break; + shift <<= 7; + data += shift; + } + + this._lastRead = data; + return data; +} +function BPS_writeVLV(data) { + while (true) { + var x = data & 0x7f; + data >>= 7; + if (data === 0) { + this.writeU8(0x80 | x); + break; + } + this.writeU8(x); + data--; + } +} +function BPS_getVLVLen(data) { + var len = 0; + while (true) { + var x = data & 0x7f; + data >>= 7; + if (data === 0) { + len++; + break; + } + len++; + data--; + } + return len; +} + +BPS.prototype.export = function (fileName) { + var patchFileSize = BPS_MAGIC.length; + patchFileSize += BPS_getVLVLen(this.sourceSize); + patchFileSize += BPS_getVLVLen(this.targetSize); + patchFileSize += BPS_getVLVLen(this.metaData.length); + patchFileSize += this.metaData.length; + for (var i = 0; i < this.actions.length; i++) { + var action = this.actions[i]; + patchFileSize += BPS_getVLVLen(((action.length - 1) << 2) + action.type); + + if (action.type === BPS_ACTION_TARGET_READ) { + patchFileSize += action.length; + } else if ( + action.type === BPS_ACTION_SOURCE_COPY || + action.type === BPS_ACTION_TARGET_COPY + ) { + patchFileSize += BPS_getVLVLen( + (Math.abs(action.relativeOffset) << 1) + + (action.relativeOffset < 0 ? 1 : 0), + ); + } + } + patchFileSize += 12; + + var patchFile = new BinFile(patchFileSize); + patchFile.fileName = fileName + ".bps"; + patchFile.littleEndian = true; + patchFile.writeVLV = BPS_writeVLV; + + patchFile.writeString(BPS_MAGIC); + patchFile.writeVLV(this.sourceSize); + patchFile.writeVLV(this.targetSize); + patchFile.writeVLV(this.metaData.length); + patchFile.writeString(this.metaData, this.metaData.length); + + for (var i = 0; i < this.actions.length; i++) { + var action = this.actions[i]; + patchFile.writeVLV(((action.length - 1) << 2) + action.type); + + if (action.type === BPS_ACTION_TARGET_READ) { + patchFile.writeBytes(action.bytes); + } else if ( + action.type === BPS_ACTION_SOURCE_COPY || + action.type === BPS_ACTION_TARGET_COPY + ) { + patchFile.writeVLV( + (Math.abs(action.relativeOffset) << 1) + + (action.relativeOffset < 0 ? 1 : 0), + ); + } + } + patchFile.writeU32(this.sourceChecksum); + patchFile.writeU32(this.targetChecksum); + patchFile.writeU32(this.patchChecksum); + + return patchFile; +}; + +function BPS_Node() { + this.offset = 0; + this.next = null; +} +BPS_Node.prototype.delete = function () { + if (this.next) delete this.next; +}; +BPS.buildFromRoms = function (original, modified, deltaMode) { + var patch = new BPS(); + patch.sourceSize = original.fileSize; + patch.targetSize = modified.fileSize; + + if (deltaMode) { + patch.actions = createBPSFromFilesDelta(original, modified); + } else { + patch.actions = createBPSFromFilesLinear(original, modified); + } + + patch.sourceChecksum = original.hashCRC32(); + patch.targetChecksum = modified.hashCRC32(); + patch.patchChecksum = patch.calculateFileChecksum(); + return patch; +}; + +/* delta implementation from https://github.com/chiya/beat/blob/master/nall/beat/linear.hpp */ +function createBPSFromFilesLinear(original, modified) { + var patchActions = []; + + /* references to match original beat code */ + var sourceData = original._u8array; + var targetData = modified._u8array; + var sourceSize = original.fileSize; + var targetSize = modified.fileSize; + var Granularity = 1; + + var targetRelativeOffset = 0; + var outputOffset = 0; + var targetReadLength = 0; + + function targetReadFlush() { + if (targetReadLength) { + //encode(TargetRead | ((targetReadLength - 1) << 2)); + var action = { + type: BPS_ACTION_TARGET_READ, + length: targetReadLength, + bytes: [], + }; + patchActions.push(action); + var offset = outputOffset - targetReadLength; + while (targetReadLength) { + //write(targetData[offset++]); + action.bytes.push(targetData[offset++]); + targetReadLength--; + } + } + } + + while (outputOffset < targetSize) { + var sourceLength = 0; + for (var n = 0; outputOffset + n < Math.min(sourceSize, targetSize); n++) { + if (sourceData[outputOffset + n] != targetData[outputOffset + n]) break; + sourceLength++; + } + + var rleLength = 0; + for (var n = 1; outputOffset + n < targetSize; n++) { + if (targetData[outputOffset] != targetData[outputOffset + n]) break; + rleLength++; + } + + if (rleLength >= 4) { + //write byte to repeat + targetReadLength++; + outputOffset++; + targetReadFlush(); + + //copy starting from repetition byte + //encode(TargetCopy | ((rleLength - 1) << 2)); + var relativeOffset = outputOffset - 1 - targetRelativeOffset; + //encode(relativeOffset << 1); + patchActions.push({ + type: BPS_ACTION_TARGET_COPY, + length: rleLength, + relativeOffset: relativeOffset, + }); + outputOffset += rleLength; + targetRelativeOffset = outputOffset - 1; + } else if (sourceLength >= 4) { + targetReadFlush(); + //encode(SourceRead | ((sourceLength - 1) << 2)); + patchActions.push({ type: BPS_ACTION_SOURCE_READ, length: sourceLength }); + outputOffset += sourceLength; + } else { + targetReadLength += Granularity; + outputOffset += Granularity; + } + } + + targetReadFlush(); + + return patchActions; +} + +/* delta implementation from https://github.com/chiya/beat/blob/master/nall/beat/delta.hpp */ +function createBPSFromFilesDelta(original, modified) { + var patchActions = []; + + /* references to match original beat code */ + var sourceData = original._u8array; + var targetData = modified._u8array; + var sourceSize = original.fileSize; + var targetSize = modified.fileSize; + var Granularity = 1; + + var sourceRelativeOffset = 0; + var targetRelativeOffset = 0; + var outputOffset = 0; + + var sourceTree = new Array(65536); + var targetTree = new Array(65536); + for (var n = 0; n < 65536; n++) { + sourceTree[n] = null; + targetTree[n] = null; + } + + //source tree creation + for (var offset = 0; offset < sourceSize; offset++) { + var symbol = sourceData[offset + 0]; + //sourceChecksum = crc32_adjust(sourceChecksum, symbol); + if (offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8; + var node = new BPS_Node(); + node.offset = offset; + node.next = sourceTree[symbol]; + sourceTree[symbol] = node; + } + + var targetReadLength = 0; + + function targetReadFlush() { + if (targetReadLength) { + //encode(TargetRead | ((targetReadLength - 1) << 2)); + var action = { + type: BPS_ACTION_TARGET_READ, + length: targetReadLength, + bytes: [], + }; + patchActions.push(action); + var offset = outputOffset - targetReadLength; + while (targetReadLength) { + //write(targetData[offset++]); + action.bytes.push(targetData[offset++]); + targetReadLength--; + } + } + } + + while (outputOffset < modified.fileSize) { + var maxLength = 0, + maxOffset = 0, + mode = BPS_ACTION_TARGET_READ; + + var symbol = targetData[outputOffset + 0]; + if (outputOffset < targetSize - 1) + symbol |= targetData[outputOffset + 1] << 8; + + { + //source read + var length = 0, + offset = outputOffset; + while ( + offset < sourceSize && + offset < targetSize && + sourceData[offset] == targetData[offset] + ) { + length++; + offset++; + } + if (length > maxLength) + ((maxLength = length), (mode = BPS_ACTION_SOURCE_READ)); + } + + { + //source copy + var node = sourceTree[symbol]; + while (node) { + var length = 0, + x = node.offset, + y = outputOffset; + while ( + x < sourceSize && + y < targetSize && + sourceData[x++] == targetData[y++] + ) + length++; + if (length > maxLength) + ((maxLength = length), + (maxOffset = node.offset), + (mode = BPS_ACTION_SOURCE_COPY)); + node = node.next; + } + } + + { + //target copy + var node = targetTree[symbol]; + while (node) { + var length = 0, + x = node.offset, + y = outputOffset; + while (y < targetSize && targetData[x++] == targetData[y++]) length++; + if (length > maxLength) + ((maxLength = length), + (maxOffset = node.offset), + (mode = BPS_ACTION_TARGET_COPY)); + node = node.next; + } + + //target tree append + node = new BPS_Node(); + node.offset = outputOffset; + node.next = targetTree[symbol]; + targetTree[symbol] = node; + } + + { + //target read + if (maxLength < 4) { + maxLength = Math.min(Granularity, targetSize - outputOffset); + mode = BPS_ACTION_TARGET_READ; + } + } + + if (mode != BPS_ACTION_TARGET_READ) targetReadFlush(); + + switch (mode) { + case BPS_ACTION_SOURCE_READ: + //encode(BPS_ACTION_SOURCE_READ | ((maxLength - 1) << 2)); + patchActions.push({ type: BPS_ACTION_SOURCE_READ, length: maxLength }); + break; + case BPS_ACTION_TARGET_READ: + //delay write to group sequential TargetRead commands into one + targetReadLength += maxLength; + break; + case BPS_ACTION_SOURCE_COPY: + case BPS_ACTION_TARGET_COPY: + //encode(mode | ((maxLength - 1) << 2)); + var relativeOffset; + if (mode == BPS_ACTION_SOURCE_COPY) { + relativeOffset = maxOffset - sourceRelativeOffset; + sourceRelativeOffset = maxOffset + maxLength; + } else { + relativeOffset = maxOffset - targetRelativeOffset; + targetRelativeOffset = maxOffset + maxLength; + } + //encode((relativeOffset < 0) | (abs(relativeOffset) << 1)); + patchActions.push({ + type: mode, + length: maxLength, + relativeOffset: relativeOffset, + }); + break; + } + + outputOffset += maxLength; + } + + targetReadFlush(); + + return patchActions; +} diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.ips.js b/frontend/assets/patcherjs/modules/RomPatcher.format.ips.js new file mode 100644 index 000000000..0ab64c221 --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.ips.js @@ -0,0 +1,289 @@ +/* IPS module for Rom Patcher JS v20250430 - Marc Robledo 2016-2025 - http://www.marcrobledo.com/license */ +/* File format specification: http://www.smwiki.net/wiki/IPS_file_format */ + +/* This file also acts as EBP (EarthBound Patch) module */ +/* EBP is actually just IPS with some JSON metadata stuck on the end (implementation: https://github.com/Lyrositor/EBPatcher) */ + +const IPS_MAGIC = "PATCH"; +const IPS_MAX_ROM_SIZE = 0x1000000; //16 megabytes +const IPS_RECORD_RLE = 0x0000; +const IPS_RECORD_SIMPLE = 0x01; + +if (typeof module !== "undefined" && module.exports) { + module.exports = IPS; +} + +function IPS() { + this.records = []; + this.truncate = false; + this.EBPmetadata = null; +} +IPS.prototype.addSimpleRecord = function (o, d) { + this.records.push({ + offset: o, + type: IPS_RECORD_SIMPLE, + length: d.length, + data: d, + }); +}; +IPS.prototype.addRLERecord = function (o, l, b) { + this.records.push({ offset: o, type: IPS_RECORD_RLE, length: l, byte: b }); +}; +IPS.prototype.setEBPMetadata = function (metadataObject) { + if (typeof metadataObject !== "object") + throw new TypeError("metadataObject must be an object"); + for (var key in metadataObject) { + if (typeof metadataObject[key] !== "string") + throw new TypeError("metadataObject values must be strings"); + } + + /* EBPatcher (linked above) expects the "patcher" field to be EBPatcher to read the metadata */ + /* CoilSnake (EB modding tool) inserts this manually too */ + /* So we also add it here for compatibility purposes */ + this.EBPmetadata = { patcher: "EBPatcher", ...metadataObject }; +}; +IPS.prototype.getDescription = function () { + if (this.EBPmetadata) { + var description = ""; + for (var key in this.EBPmetadata) { + if (key === "patcher") continue; + + const keyPretty = key.charAt(0).toUpperCase() + key.slice(1); + description += keyPretty + ": " + this.EBPmetadata[key] + "\n"; + } + return description.trim(); + } + return null; +}; +IPS.prototype.toString = function () { + nSimpleRecords = 0; + nRLERecords = 0; + for (var i = 0; i < this.records.length; i++) { + if (this.records[i].type === IPS_RECORD_RLE) nRLERecords++; + else nSimpleRecords++; + } + var s = "Simple records: " + nSimpleRecords; + s += "\nRLE records: " + nRLERecords; + s += "\nTotal records: " + this.records.length; + if (this.truncate && !this.EBPmetadata) + s += "\nTruncate at: 0x" + this.truncate.toString(16); + else if (this.EBPmetadata) + s += "\nEBP Metadata: " + JSON.stringify(this.EBPmetadata); + return s; +}; +IPS.prototype.export = function (fileName) { + var patchFileSize = 5; //PATCH string + for (var i = 0; i < this.records.length; i++) { + if (this.records[i].type === IPS_RECORD_RLE) + patchFileSize += 3 + 2 + 2 + 1; //offset+0x0000+length+RLE byte to be written + else patchFileSize += 3 + 2 + this.records[i].data.length; //offset+length+data + } + patchFileSize += 3; //EOF string + if (this.truncate && !this.EBPmetadata) + patchFileSize += 3; //truncate + else if (this.EBPmetadata) + patchFileSize += JSON.stringify(this.EBPmetadata).length; + + tempFile = new BinFile(patchFileSize); + tempFile.fileName = fileName + (this.EBPmetadata ? ".ebp" : ".ips"); + tempFile.writeString(IPS_MAGIC); + for (var i = 0; i < this.records.length; i++) { + var rec = this.records[i]; + tempFile.writeU24(rec.offset); + if (rec.type === IPS_RECORD_RLE) { + tempFile.writeU16(0x0000); + tempFile.writeU16(rec.length); + tempFile.writeU8(rec.byte); + } else { + tempFile.writeU16(rec.data.length); + tempFile.writeBytes(rec.data); + } + } + + tempFile.writeString("EOF"); + if (this.truncate && !this.EBPmetadata) tempFile.writeU24(this.truncate); + else if (this.EBPmetadata) + tempFile.writeString(JSON.stringify(this.EBPmetadata)); + + return tempFile; +}; +IPS.prototype.apply = function (romFile) { + if (this.truncate && !this.EBPmetadata) { + if (this.truncate > romFile.fileSize) { + //expand (discussed here: https://github.com/marcrobledo/RomPatcher.js/pull/46) + tempFile = new BinFile(this.truncate); + romFile.copyTo(tempFile, 0, romFile.fileSize, 0); + } else { + //truncate + tempFile = romFile.slice(0, this.truncate); + } + } else { + //calculate target ROM size, expanding it if any record offset is beyond target ROM size + var newFileSize = romFile.fileSize; + for (var i = 0; i < this.records.length; i++) { + var rec = this.records[i]; + if (rec.type === IPS_RECORD_RLE) { + if (rec.offset + rec.length > newFileSize) { + newFileSize = rec.offset + rec.length; + } + } else { + if (rec.offset + rec.data.length > newFileSize) { + newFileSize = rec.offset + rec.data.length; + } + } + } + + if (newFileSize === romFile.fileSize) { + tempFile = romFile.slice(0, romFile.fileSize); + } else { + tempFile = new BinFile(newFileSize); + romFile.copyTo(tempFile, 0); + } + } + + romFile.seek(0); + + for (var i = 0; i < this.records.length; i++) { + tempFile.seek(this.records[i].offset); + if (this.records[i].type === IPS_RECORD_RLE) { + for (var j = 0; j < this.records[i].length; j++) + tempFile.writeU8(this.records[i].byte); + } else { + tempFile.writeBytes(this.records[i].data); + } + } + + return tempFile; +}; + +IPS.MAGIC = IPS_MAGIC; + +IPS.fromFile = function (file) { + var patchFile = new IPS(); + file.seek(5); + + while (!file.isEOF()) { + var offset = file.readU24(); + + if (offset === 0x454f46) { + /* EOF */ + if (file.isEOF()) { + break; + } else if (file.offset + 3 === file.fileSize) { + patchFile.truncate = file.readU24(); + break; + } else if (file.readU8() === "{".charCodeAt(0)) { + file.skip(-1); + patchFile.setEBPMetadata( + JSON.parse(file.readString(file.fileSize - file.offset)), + ); + break; + } + } + + var length = file.readU16(); + + if (length === IPS_RECORD_RLE) { + patchFile.addRLERecord(offset, file.readU16(), file.readU8()); + } else { + patchFile.addSimpleRecord(offset, file.readBytes(length)); + } + } + return patchFile; +}; + +IPS.buildFromRoms = function (original, modified, asEBP = false) { + var patch = new IPS(); + + if (!asEBP && modified.fileSize < original.fileSize) { + patch.truncate = modified.fileSize; + } else if (asEBP) { + patch.setEBPMetadata( + typeof asEBP === "object" + ? asEBP + : { + Author: "Unknown", + Title: "Untitled", + Description: "No description", + }, + ); + } + + //solucion: guardar startOffset y endOffset (ir mirando de 6 en 6 hacia atrás) + var previousRecord = { type: 0xdeadbeef, startOffset: 0, length: 0 }; + while (!modified.isEOF()) { + var b1 = original.isEOF() ? 0x00 : original.readU8(); + var b2 = modified.readU8(); + + if (b1 !== b2) { + var RLEmode = true; + var differentData = []; + var startOffset = modified.offset - 1; + + while (b1 !== b2 && differentData.length < 0xffff) { + differentData.push(b2); + if (b2 !== differentData[0]) RLEmode = false; + + if (modified.isEOF() || differentData.length === 0xffff) break; + + b1 = original.isEOF() ? 0x00 : original.readU8(); + b2 = modified.readU8(); + } + + //check if this record is near the previous one + var distance = + startOffset - (previousRecord.offset + previousRecord.length); + if ( + previousRecord.type === IPS_RECORD_SIMPLE && + distance < 6 && + previousRecord.length + distance + differentData.length < 0xffff + ) { + if (RLEmode && differentData.length > 6) { + // separate a potential RLE record + original.seek(startOffset); + modified.seek(startOffset); + previousRecord = { type: 0xdeadbeef, startOffset: 0, length: 0 }; + } else { + // merge both records + while (distance--) { + previousRecord.data.push( + modified._u8array[previousRecord.offset + previousRecord.length], + ); + previousRecord.length++; + } + previousRecord.data = previousRecord.data.concat(differentData); + previousRecord.length = previousRecord.data.length; + } + } else { + if (startOffset >= IPS_MAX_ROM_SIZE) { + throw new Error( + `Files are too big for ${patch.EBPmetadata ? "EBP" : "IPS"} format`, + ); + return null; + } + + if (RLEmode && differentData.length > 2) { + patch.addRLERecord( + startOffset, + differentData.length, + differentData[0], + ); + } else { + patch.addSimpleRecord(startOffset, differentData); + } + previousRecord = patch.records[patch.records.length - 1]; + } + } + } + + if (modified.fileSize > original.fileSize) { + var lastRecord = patch.records[patch.records.length - 1]; + var lastOffset = lastRecord.offset + lastRecord.length; + + if (lastOffset < modified.fileSize) { + patch.addSimpleRecord(modified.fileSize - 1, [0x00]); + } + } + + return patch; +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.pmsr.js b/frontend/assets/patcherjs/modules/RomPatcher.format.pmsr.js new file mode 100644 index 000000000..b58d23cff --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.pmsr.js @@ -0,0 +1,93 @@ +/* PMSR (Paper Mario Star Rod) module for Rom Patcher JS v20240721 - Marc Robledo 2020-2024 - http://www.marcrobledo.com/license */ +/* File format specification: http://origami64.net/attachment.php?aid=790 (dead link) */ + +const PMSR_MAGIC = "PMSR"; +const YAY0_MAGIC = "Yay0"; +const PAPER_MARIO_USA10_CRC32 = 0xa7f5cd7e; +const PAPER_MARIO_USA10_FILE_SIZE = 41943040; +if (typeof module !== "undefined" && module.exports) { + module.exports = PMSR; +} + +function PMSR() { + this.targetSize = 0; + this.records = []; +} +PMSR.prototype.addRecord = function (offset, data) { + this.records.push({ offset: offset, data: data }); +}; +PMSR.prototype.toString = function () { + var s = "Star Rod patch"; + s += "\nTarget file size: " + this.targetSize; + s += "\n#Records: " + this.records.length; + return s; +}; + +PMSR.prototype.validateSource = function (romFile) { + return ( + romFile.fileSize === PAPER_MARIO_USA10_FILE_SIZE && + romFile.hashCRC32() === PAPER_MARIO_USA10_CRC32 + ); +}; +PMSR.prototype.getValidationInfo = function () { + return { + type: "CRC32", + value: PAPER_MARIO_USA10_CRC32, + }; +}; +PMSR.prototype.apply = function (romFile, validate) { + if (validate && !this.validateSource(romFile)) { + throw new Error("Source ROM checksum mismatch"); + } + + console.log("a"); + if (this.targetSize === romFile.fileSize) { + tempFile = romFile.slice(0, romFile.fileSize); + } else { + tempFile = new BinFile(this.targetSize); + romFile.copyTo(tempFile, 0); + } + + console.log("b"); + for (var i = 0; i < this.records.length; i++) { + tempFile.seek(this.records[i].offset); + tempFile.writeBytes(this.records[i].data); + } + + return tempFile; +}; + +PMSR.MAGIC = PMSR_MAGIC; + +PMSR.fromFile = function (file) { + var patch = new PMSR(); + + /*file.seek(0); + if(file.readString(YAY0_MAGIC.length)===YAY0_MAGIC){ + file=PMSR.YAY0_decode(file); + }*/ + + patch.targetSize = PAPER_MARIO_USA10_FILE_SIZE; + + file.seek(4); + var nRecords = file.readU32(); + + for (var i = 0; i < nRecords; i++) { + var offset = file.readU32(); + var length = file.readU32(); + patch.addRecord(offset, file.readBytes(length)); + + if (offset + length > patch.targetSize) patch.targetSize = offset + length; + } + + return patch; +}; + +/* to-do */ +//PMSR.prototype.export=function(fileName){return null} +//PMSR.buildFromRoms=function(original, modified){return null} + +/* https://github.com/pho/WindViewer/wiki/Yaz0-and-Yay0 */ +PMSR.YAY0_decode = function (file) { + /* to-do */ +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.ppf.js b/frontend/assets/patcherjs/modules/RomPatcher.format.ppf.js new file mode 100644 index 000000000..cdd0d4948 --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.ppf.js @@ -0,0 +1,245 @@ +/* PPF module for Rom Patcher JS v20200221 - Marc Robledo 2019-2020 - http://www.marcrobledo.com/license */ +/* File format specification: https://www.romhacking.net/utilities/353/ */ + +const PPF_MAGIC = "PPF"; +const PPF_IMAGETYPE_BIN = 0x00; +const PPF_IMAGETYPE_GI = 0x01; +const PPF_BEGIN_FILE_ID_DIZ_MAGIC = "@BEG"; //@BEGIN_FILE_ID.DIZ + +if (typeof module !== "undefined" && module.exports) { + module.exports = PPF; +} +function PPF() { + this.version = 3; + this.imageType = PPF_IMAGETYPE_BIN; + this.blockCheck = false; + this.undoData = false; + this.records = []; +} +PPF.prototype.addRecord = function (offset, data, undoData) { + if (this.undoData) { + this.records.push({ offset: offset, data: data, undoData: undoData }); + } else { + this.records.push({ offset: offset, data: data }); + } +}; +PPF.prototype.toString = function () { + var s = this.description; + s += "\nPPF version: " + this.version; + s += "\n#Records: " + this.records.length; + s += "\nImage type: " + this.imageType; + s += "\nBlock check: " + !!this.blockCheck; + s += "\nUndo data: " + this.undoData; + if (this.fileIdDiz) s += "\nFILE_ID.DIZ: " + this.fileIdDiz; + return s; +}; +PPF.prototype.export = function (fileName) { + var patchFileSize = 5 + 1 + 50; //PPFx0 + for (var i = 0; i < this.records.length; i++) { + patchFileSize += 4 + 1 + this.records[i].data.length; + if (this.version === 3) patchFileSize += 4; //offsets are u64 + } + + if (this.version === 3 || this.version === 2) { + patchFileSize += 4; + } + if (this.blockCheck) { + patchFileSize += 1024; + } + if (this.fileIdDiz) { + patchFileSize += 18 + this.fileIdDiz.length + 16 + 4; + } + + tempFile = new BinFile(patchFileSize); + tempFile.fileName = fileName + ".ppf"; + tempFile.writeString(PPF_MAGIC); + tempFile.writeString((this.version * 10).toString()); + tempFile.writeU8(parseInt(this.version) - 1); + tempFile.writeString(this.description, 50); + + if (this.version === 3) { + tempFile.writeU8(this.imageType); + tempFile.writeU8(this.blockCheck ? 0x01 : 0x00); + tempFile.writeU8(this.undoData ? 0x01 : 0x00); + tempFile.writeU8(0x00); //dummy + } else if (this.version === 2) { + tempFile.writeU32(this.inputFileSize); + } + + if (this.blockCheck) { + tempFile.writeBytes(this.blockCheck); + } + + tempFile.littleEndian = true; + for (var i = 0; i < this.records.length; i++) { + tempFile.writeU32(this.records[i].offset & 0xffffffff); + + if (this.version === 3) { + var offset2 = this.records[i].offset; + for (var j = 0; j < 32; j++) offset2 = parseInt((offset2 / 2) >>> 0); + tempFile.writeU32(offset2); + } + tempFile.writeU8(this.records[i].data.length); + tempFile.writeBytes(this.records[i].data); + if (this.undoData) tempFile.writeBytes(this.records[i].undoData); + } + + if (this.fileIdDiz) { + tempFile.writeString("@BEGIN_FILE_ID.DIZ"); + tempFile.writeString(this.fileIdDiz); + tempFile.writeString("@END_FILE_ID.DIZ"); + tempFile.writeU16(this.fileIdDiz.length); + tempFile.writeU16(0x00); + } + + return tempFile; +}; +PPF.prototype.apply = function (romFile) { + var newFileSize = romFile.fileSize; + for (var i = 0; i < this.records.length; i++) { + if (this.records[i].offset + this.records[i].data.length > newFileSize) + newFileSize = this.records[i].offset + this.records[i].data.length; + } + if (newFileSize === romFile.fileSize) { + tempFile = romFile.slice(0, romFile.fileSize); + } else { + tempFile = new BinFile(newFileSize); + romFile.copyTo(tempFile, 0); + } + + //check if undoing + var undoingData = false; + if (this.undoData) { + tempFile.seek(this.records[0].offset); + var originalBytes = tempFile.readBytes(this.records[0].data.length); + var foundDifferences = false; + for (var i = 0; i < originalBytes.length && !foundDifferences; i++) { + if (originalBytes[i] !== this.records[0].data[i]) { + foundDifferences = true; + } + } + if (!foundDifferences) { + undoingData = true; + } + } + + for (var i = 0; i < this.records.length; i++) { + tempFile.seek(this.records[i].offset); + + if (undoingData) { + tempFile.writeBytes(this.records[i].undoData); + } else { + tempFile.writeBytes(this.records[i].data); + } + } + + return tempFile; +}; + +PPF.MAGIC = PPF_MAGIC; + +PPF.fromFile = function (patchFile) { + var patch = new PPF(); + + patchFile.seek(3); + var version1 = parseInt(patchFile.readString(2)) / 10; + var version2 = patchFile.readU8() + 1; + if (version1 !== version2 || version1 > 3) { + throw new Error("invalid PPF version"); + } + + patch.version = version1; + patch.description = patchFile.readString(50).replace(/ +$/, ""); + + if (patch.version === 3) { + patch.imageType = patchFile.readU8(); + if (patchFile.readU8()) patch.blockCheck = true; + if (patchFile.readU8()) patch.undoData = true; + + patchFile.skip(1); + } else if (patch.version === 2) { + patch.blockCheck = true; + patch.inputFileSize = patchFile.readU32(); + } + + if (patch.blockCheck) { + patch.blockCheck = patchFile.readBytes(1024); + } + + patchFile.littleEndian = true; + while (!patchFile.isEOF()) { + if (patchFile.readString(4) === PPF_BEGIN_FILE_ID_DIZ_MAGIC) { + patchFile.skip(14); + //console.log('found file_id.diz begin'); + patch.fileIdDiz = patchFile.readString(3072); + patch.fileIdDiz = patch.fileIdDiz.substr( + 0, + patch.fileIdDiz.indexOf("@END_FILE_ID.DIZ"), + ); + break; + } + patchFile.skip(-4); + + var offset; + if (patch.version === 3) { + var u64_1 = patchFile.readU32(); + var u64_2 = patchFile.readU32(); + offset = u64_1 + u64_2 * 0x100000000; + } else offset = patchFile.readU32(); + + var len = patchFile.readU8(); + var data = patchFile.readBytes(len); + + var undoData = false; + if (patch.undoData) { + undoData = patchFile.readBytes(len); + } + + patch.addRecord(offset, data, undoData); + } + + return patch; +}; + +PPF.buildFromRoms = function (original, modified) { + var patch = new PPF(); + + patch.description = "Patch description"; + + if (original.fileSize > modified.fileSize) { + var expandedModified = new BinFile(original.fileSize); + modified.copyTo(expandedModified, 0); + modified = expandedModified; + } + + original.seek(0); + modified.seek(0); + while (!modified.isEOF()) { + var b1 = original.isEOF() ? 0x00 : original.readU8(); + var b2 = modified.readU8(); + + if (b1 !== b2) { + var differentData = []; + var offset = modified.offset - 1; + + while (b1 !== b2 && differentData.length < 0xff) { + differentData.push(b2); + + if (modified.isEOF() || differentData.length === 0xff) break; + + b1 = original.isEOF() ? 0x00 : original.readU8(); + b2 = modified.readU8(); + } + + patch.addRecord(offset, differentData); + } + } + + if (original.fileSize < modified.fileSize) { + modified.seek(modified.fileSize - 1); + if (modified.readU8() === 0x00) + patch.addRecord(modified.fileSize - 1, [0x00]); + } + + return patch; +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.rup.js b/frontend/assets/patcherjs/modules/RomPatcher.format.rup.js new file mode 100644 index 000000000..e18cf468e --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.rup.js @@ -0,0 +1,387 @@ +/* RUP module for Rom Patcher JS v20250430 - Marc Robledo 2018-2025 - http://www.marcrobledo.com/license */ +/* File format specification: http://www.romhacking.net/documents/288/ */ + +const RUP_MAGIC = "NINJA2"; +const RUP_COMMAND_END = 0x00; +const RUP_COMMAND_OPEN_NEW_FILE = 0x01; +const RUP_COMMAND_XOR_RECORD = 0x02; +const RUP_ROM_TYPES = [ + "raw", + "nes", + "fds", + "snes", + "n64", + "gb", + "sms", + "mega", + "pce", + "lynx", +]; +if (typeof module !== "undefined" && module.exports) { + module.exports = RUP; +} + +function RUP() { + this.author = ""; + this.version = ""; + this.title = ""; + this.genre = ""; + this.language = ""; + this.date = ""; + this.web = ""; + this.description = ""; + this.files = []; +} +RUP.prototype.toString = function () { + var s = "Author: " + this.author; + s += "\nVersion: " + this.version; + s += "\nTitle: " + this.title; + s += "\nGenre: " + this.genre; + s += "\nLanguage: " + this.language; + s += "\nDate: " + this.date; + s += "\nWeb: " + this.web; + s += "\nDescription: " + this.description; + for (var i = 0; i < this.files.length; i++) { + var file = this.files[i]; + s += "\n---------------"; + s += "\nFile " + i + ":"; + s += "\nFile name: " + file.fileName; + s += "\nRom type: " + RUP_ROM_TYPES[file.romType]; + s += "\nSource file size: " + file.sourceFileSize; + s += "\nTarget file size: " + file.targetFileSize; + s += "\nSource MD5: " + file.sourceMD5; + s += "\nTarget MD5: " + file.targetMD5; + if (file.overflowMode === "A") { + s += "\nOverflow mode: Append " + file.overflowData.length + " bytes"; + } else if (file.overflowMode === "M") { + s += "\nOverflow mode: Minify " + file.overflowData.length + " bytes"; + } + s += "\n#records: " + file.records.length; + } + return s; +}; + +RUP.prototype.validateSource = function (romFile, headerSize) { + var md5string = romFile.hashMD5(headerSize); + for (var i = 0; i < this.files.length; i++) { + if ( + this.files[i].sourceMD5 === md5string || + this.files[i].targetMD5 === md5string + ) { + return { + file: this.files[i], + undo: this.files[i].targetMD5 === md5string, + }; + } + } + return false; +}; +RUP.prototype.getValidationInfo = function () { + var values = []; + for (var i = 0; i < this.files.length; i++) { + values.push(this.files[i].sourceMD5); + } + return { + type: "MD5", + value: values, + }; +}; +RUP.prototype.getDescription = function () { + return this.description ? this.description : null; +}; +RUP.prototype.apply = function (romFile, validate) { + var validFile; + if (validate) { + validFile = this.validateSource(romFile); + + if (!validFile) throw new Error("Source ROM checksum mismatch"); + } else { + validFile = { + file: this.files[0], + undo: this.files[0].targetMD5 === romFile.hashMD5(), + }; + } + + var undo = validFile.undo; + var patch = validFile.file; + + tempFile = new BinFile(!undo ? patch.targetFileSize : patch.sourceFileSize); + /* copy original file */ + romFile.copyTo(tempFile, 0); + + for (var i = 0; i < patch.records.length; i++) { + var offset = patch.records[i].offset; + romFile.seek(offset); + tempFile.seek(offset); + for (var j = 0; j < patch.records[i].xor.length; j++) { + tempFile.writeU8( + (romFile.isEOF() ? 0x00 : romFile.readU8()) ^ patch.records[i].xor[j], + ); + } + } + + /* add overflow data if needed */ + if (patch.overflowMode === "A" && !undo) { + /* append */ + tempFile.seek(patch.sourceFileSize); + tempFile.writeBytes(patch.overflowData.map((byte) => byte ^ 0xff)); + } else if (patch.overflowMode === "M" && undo) { + /* minify */ + tempFile.seek(patch.targetFileSize); + tempFile.writeBytes(patch.overflowData.map((byte) => byte ^ 0xff)); + } + + if ( + validate && + ((!undo && tempFile.hashMD5() !== patch.targetMD5) || + (undo && tempFile.hashMD5() !== patch.sourceMD5)) + ) { + throw new Error("Target ROM checksum mismatch"); + } + + if (undo) tempFile.unpatched = true; + + return tempFile; +}; + +RUP.MAGIC = RUP_MAGIC; + +RUP.padZeroes = function (intVal, nBytes) { + var hexString = intVal.toString(16); + while (hexString.length < nBytes * 2) hexString = "0" + hexString; + return hexString; +}; +RUP.fromFile = function (file) { + var patch = new RUP(); + file.readVLV = RUP_readVLV; + + file.seek(RUP_MAGIC.length); + + patch.textEncoding = file.readU8(); + patch.author = file.readString(84); + patch.version = file.readString(11); + patch.title = file.readString(256); + patch.genre = file.readString(48); + patch.language = file.readString(48); + patch.date = file.readString(8); + patch.web = file.readString(512); + patch.description = file.readString(1074).replace(/\\n/g, "\n"); + + file.seek(0x800); + var nextFile; + while (!file.isEOF()) { + var command = file.readU8(); + + if (command === RUP_COMMAND_OPEN_NEW_FILE) { + if (nextFile) patch.files.push(nextFile); + + nextFile = { + records: [], + }; + + nextFile.fileName = file.readString(file.readVLV()); + nextFile.romType = file.readU8(); + nextFile.sourceFileSize = file.readVLV(); + nextFile.targetFileSize = file.readVLV(); + + nextFile.sourceMD5 = ""; + for (var i = 0; i < 16; i++) + nextFile.sourceMD5 += RUP.padZeroes(file.readU8(), 1); + + nextFile.targetMD5 = ""; + for (var i = 0; i < 16; i++) + nextFile.targetMD5 += RUP.padZeroes(file.readU8(), 1); + + if (nextFile.sourceFileSize !== nextFile.targetFileSize) { + nextFile.overflowMode = file.readString(1); // 'M' (source>target) or 'A' (source>= 8; + } +} +function RUP_getVLVLen(data) { + var ret = 1; + while (data) { + ret++; + data >>= 8; + } + return ret; +} + +RUP.prototype.export = function (fileName) { + var patchFileSize = 2048; + for (var i = 0; i < this.files.length; i++) { + var file = this.files[i]; + patchFileSize++; //command 0x01 + + patchFileSize += RUP_getVLVLen(file.fileName.length); + patchFileSize += file.fileName.length; + patchFileSize++; //rom type + patchFileSize += RUP_getVLVLen(file.sourceFileSize); + patchFileSize += RUP_getVLVLen(file.targetFileSize); + patchFileSize += 32; //MD5s + + if (file.sourceFileSize !== file.targetFileSize) { + patchFileSize++; // M or A + patchFileSize += RUP_getVLVLen(file.overflowData.length); + patchFileSize += file.overflowData.length; + } + for (var j = 0; j < file.records.length; j++) { + patchFileSize++; //command 0x01 + patchFileSize += RUP_getVLVLen(file.records[j].offset); + patchFileSize += RUP_getVLVLen(file.records[j].xor.length); + patchFileSize += file.records[j].xor.length; + } + } + patchFileSize++; //command 0x00 + + var patchFile = new BinFile(patchFileSize); + patchFile.fileName = fileName + ".rup"; + patchFile.writeVLV = RUP_writeVLV; + + patchFile.writeString(RUP_MAGIC); + patchFile.writeU8(this.textEncoding); + patchFile.writeString(this.author, 84); + patchFile.writeString(this.version, 11); + patchFile.writeString(this.title, 256); + patchFile.writeString(this.genre, 48); + patchFile.writeString(this.language, 48); + patchFile.writeString(this.date, 8); + patchFile.writeString(this.web, 512); + patchFile.writeString(this.description.replace(/\n/g, "\\n"), 1074); + + for (var i = 0; i < this.files.length; i++) { + var file = this.files[i]; + patchFile.writeU8(RUP_COMMAND_OPEN_NEW_FILE); + + patchFile.writeVLV(file.fileName); + patchFile.writeU8(file.romType); + patchFile.writeVLV(file.sourceFileSize); + patchFile.writeVLV(file.targetFileSize); + + for (var j = 0; j < 16; j++) + patchFile.writeU8(parseInt(file.sourceMD5.substr(j * 2, 2), 16)); + for (var j = 0; j < 16; j++) + patchFile.writeU8(parseInt(file.targetMD5.substr(j * 2, 2), 16)); + + if (file.sourceFileSize !== file.targetFileSize) { + patchFile.writeString( + file.sourceFileSize > file.targetFileSize ? "M" : "A", + ); + patchFile.writeVLV(file.overflowData.length); + patchFile.writeBytes(file.overflowData); + } + + for (var j = 0; j < file.records.length; j++) { + patchFile.writeU8(RUP_COMMAND_XOR_RECORD); + + patchFile.writeVLV(file.records[j].offset); + patchFile.writeVLV(file.records[j].xor.length); + patchFile.writeBytes(file.records[j].xor); + } + } + + patchFile.writeU8(RUP_COMMAND_END); + + return patchFile; +}; + +RUP.buildFromRoms = function (original, modified, description) { + var patch = new RUP(); + + var today = new Date(); + patch.date = + today.getYear() + + 1900 + + RUP.padZeroes(today.getMonth() + 1, 1) + + RUP.padZeroes(today.getDate(), 1); + if (description) patch.description = description; + + var file = { + fileName: "", + romType: 0, + sourceFileSize: original.fileSize, + targetFileSize: modified.fileSize, + sourceMD5: original.hashMD5(), + targetMD5: modified.hashMD5(), + overflowMode: null, + overflowData: [], + records: [], + }; + + if (file.sourceFileSize < file.targetFileSize) { + modified.seek(file.sourceFileSize); + file.overflowMode = "A"; + file.overflowData = modified + .readBytes(file.targetFileSize - file.sourceFileSize) + .map((byte) => byte ^ 0xff); + modified = modified.slice(0, file.sourceFileSize); + } else if (file.sourceFileSize > file.targetFileSize) { + original.seek(file.targetFileSize); + file.overflowMode = "M"; + file.overflowData = original + .readBytes(file.sourceFileSize - file.targetFileSize) + .map((byte) => byte ^ 0xff); + original = original.slice(0, file.targetFileSize); + } + + original.seek(0); + modified.seek(0); + + while (!modified.isEOF()) { + var b1 = original.isEOF() ? 0x00 : original.readU8(); + var b2 = modified.readU8(); + + if (b1 !== b2) { + var originalOffset = modified.offset - 1; + var xorDifferences = []; + + while (b1 !== b2) { + xorDifferences.push(b1 ^ b2); + + if (modified.isEOF()) break; + + b1 = original.isEOF() ? 0x00 : original.readU8(); + b2 = modified.readU8(); + } + + file.records.push({ offset: originalOffset, xor: xorDifferences }); + } + } + + patch.files.push(file); + + return patch; +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.ups.js b/frontend/assets/patcherjs/modules/RomPatcher.format.ups.js new file mode 100644 index 000000000..39dd695f0 --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.ups.js @@ -0,0 +1,219 @@ +/* UPS module for Rom Patcher JS v20240721 - Marc Robledo 2017-2024 - http://www.marcrobledo.com/license */ +/* File format specification: http://www.romhacking.net/documents/392/ */ + +const UPS_MAGIC = "UPS1"; +if (typeof module !== "undefined" && module.exports) { + module.exports = UPS; +} +function UPS() { + this.records = []; + this.sizeInput = 0; + this.sizeOutput = 0; + this.checksumInput = 0; + this.checksumOutput = 0; +} +UPS.prototype.addRecord = function (relativeOffset, d) { + this.records.push({ offset: relativeOffset, XORdata: d }); +}; +UPS.prototype.toString = function () { + var s = "Records: " + this.records.length; + s += "\nInput file size: " + this.sizeInput; + s += "\nOutput file size: " + this.sizeOutput; + s += "\nInput file checksum: " + this.checksumInput.toString(16); + s += "\nOutput file checksum: " + this.checksumOutput.toString(16); + return s; +}; +UPS.prototype.export = function (fileName) { + var patchFileSize = UPS_MAGIC.length; //UPS1 string + patchFileSize += UPS_getVLVLength(this.sizeInput); //input file size + patchFileSize += UPS_getVLVLength(this.sizeOutput); //output file size + for (var i = 0; i < this.records.length; i++) { + patchFileSize += UPS_getVLVLength(this.records[i].offset); + patchFileSize += this.records[i].XORdata.length + 1; + } + patchFileSize += 12; //input/output/patch checksums + + tempFile = new BinFile(patchFileSize); + tempFile.writeVLV = UPS_writeVLV; + tempFile.fileName = fileName + ".ups"; + tempFile.writeString(UPS_MAGIC); + + tempFile.writeVLV(this.sizeInput); + tempFile.writeVLV(this.sizeOutput); + + for (var i = 0; i < this.records.length; i++) { + tempFile.writeVLV(this.records[i].offset); + tempFile.writeBytes(this.records[i].XORdata); + tempFile.writeU8(0x00); + } + tempFile.littleEndian = true; + tempFile.writeU32(this.checksumInput); + tempFile.writeU32(this.checksumOutput); + tempFile.writeU32(tempFile.hashCRC32(0, tempFile.fileSize - 4)); + + return tempFile; +}; +UPS.prototype.validateSource = function (romFile, headerSize) { + return romFile.hashCRC32(headerSize) === this.checksumInput; +}; +UPS.prototype.getValidationInfo = function () { + return { + type: "CRC32", + value: this.checksumInput, + }; +}; +UPS.prototype.apply = function (romFile, validate) { + if (validate && !this.validateSource(romFile)) { + throw new Error("Source ROM checksum mismatch"); + } + + /* fix the glitch that cut the end of the file if it's larger than the changed file patch was originally created with */ + /* more info: https://github.com/marcrobledo/RomPatcher.js/pull/40#issuecomment-1069087423 */ + sizeOutput = this.sizeOutput; + sizeInput = this.sizeInput; + if (!validate && sizeInput < romFile.fileSize) { + sizeInput = romFile.fileSize; + if (sizeOutput < sizeInput) { + sizeOutput = sizeInput; + } + } + + /* copy original file */ + tempFile = new BinFile(sizeOutput); + romFile.copyTo(tempFile, 0, sizeInput); + + romFile.seek(0); + + var nextOffset = 0; + for (var i = 0; i < this.records.length; i++) { + var record = this.records[i]; + tempFile.skip(record.offset); + romFile.skip(record.offset); + + for (var j = 0; j < record.XORdata.length; j++) { + tempFile.writeU8( + (romFile.isEOF() ? 0x00 : romFile.readU8()) ^ record.XORdata[j], + ); + } + tempFile.skip(1); + romFile.skip(1); + } + + if (validate && tempFile.hashCRC32() !== this.checksumOutput) { + throw new Error("Target ROM checksum mismatch"); + } + + return tempFile; +}; + +UPS.MAGIC = UPS_MAGIC; + +/* encode/decode variable length values, used by UPS file structure */ +function UPS_writeVLV(data) { + while (1) { + var x = data & 0x7f; + data = data >> 7; + if (data === 0) { + this.writeU8(0x80 | x); + break; + } + this.writeU8(x); + data = data - 1; + } +} +function UPS_readVLV() { + var data = 0; + + var shift = 1; + while (1) { + var x = this.readU8(); + + if (x == -1) + throw new Error( + "Can't read UPS VLV at 0x" + (this.offset - 1).toString(16), + ); + + data += (x & 0x7f) * shift; + if ((x & 0x80) !== 0) break; + shift = shift << 7; + data += shift; + } + return data; +} +function UPS_getVLVLength(data) { + var len = 0; + while (1) { + var x = data & 0x7f; + data = data >> 7; + len++; + if (data === 0) { + break; + } + data = data - 1; + } + return len; +} + +UPS.fromFile = function (file) { + var patch = new UPS(); + file.readVLV = UPS_readVLV; + + file.seek(UPS_MAGIC.length); + + patch.sizeInput = file.readVLV(); + patch.sizeOutput = file.readVLV(); + + var nextOffset = 0; + while (file.offset < file.fileSize - 12) { + var relativeOffset = file.readVLV(); + + var XORdifferences = []; + while (file.readU8()) { + XORdifferences.push(file._lastRead); + } + patch.addRecord(relativeOffset, XORdifferences); + } + + file.littleEndian = true; + patch.checksumInput = file.readU32(); + patch.checksumOutput = file.readU32(); + + if (file.readU32() !== file.hashCRC32(0, file.fileSize - 4)) { + throw new Error("Patch checksum mismatch"); + } + + file.littleEndian = false; + return patch; +}; + +UPS.buildFromRoms = function (original, modified) { + var patch = new UPS(); + patch.sizeInput = original.fileSize; + patch.sizeOutput = modified.fileSize; + + var previousSeek = 1; + while (!modified.isEOF()) { + var b1 = original.isEOF() ? 0x00 : original.readU8(); + var b2 = modified.readU8(); + + if (b1 !== b2) { + var currentSeek = modified.offset; + var XORdata = []; + + while (b1 !== b2) { + XORdata.push(b1 ^ b2); + + if (modified.isEOF()) break; + b1 = original.isEOF() ? 0x00 : original.readU8(); + b2 = modified.readU8(); + } + + patch.addRecord(currentSeek - previousSeek, XORdata); + previousSeek = currentSeek + XORdata.length + 1; + } + } + + patch.checksumInput = original.hashCRC32(); + patch.checksumOutput = modified.hashCRC32(); + return patch; +}; diff --git a/frontend/assets/patcherjs/modules/RomPatcher.format.vcdiff.js b/frontend/assets/patcherjs/modules/RomPatcher.format.vcdiff.js new file mode 100644 index 000000000..b249724af --- /dev/null +++ b/frontend/assets/patcherjs/modules/RomPatcher.format.vcdiff.js @@ -0,0 +1,394 @@ +/* VCDIFF module for RomPatcher.js v20181021 - Marc Robledo 2018 - http://www.marcrobledo.com/license */ +/* File format specification: https://tools.ietf.org/html/rfc3284 */ +/* + Mostly based in: + https://github.com/vic-alexiev/TelerikAcademy/tree/master/C%23%20Fundamentals%20II/Homework%20Assignments/3.%20Methods/000.%20MiscUtil/Compression/Vcdiff + some code and ideas borrowed from: + https://hack64.net/jscripts/libpatch.js?6 +*/ +//const VCDIFF_MAGIC=0xd6c3c400; +const VCDIFF_MAGIC = "\xd6\xc3\xc4"; +/* +const XDELTA_014_MAGIC='%XDELTA'; +const XDELTA_018_MAGIC='%XDZ000'; +const XDELTA_020_MAGIC='%XDZ001'; +const XDELTA_100_MAGIC='%XDZ002'; +const XDELTA_104_MAGIC='%XDZ003'; +const XDELTA_110_MAGIC='%XDZ004'; +*/ +if (typeof module !== "undefined" && module.exports) { + module.exports = VCDIFF; + BinFile = require("./BinFile"); +} + +function VCDIFF(patchFile) { + this.file = patchFile; +} +VCDIFF.prototype.toString = function () { + return "VCDIFF patch"; +}; + +VCDIFF.prototype.apply = function (romFile, validate) { + //romFile._u8array=new Uint8Array(romFile._dataView.buffer); + + //var t0=performance.now(); + var parser = new VCDIFF_Parser(this.file); + + //read header + parser.seek(4); + var headerIndicator = parser.readU8(); + + if (headerIndicator & VCD_DECOMPRESS) { + //has secondary decompressor, read its id + var secondaryDecompressorId = parser.readU8(); + + if (secondaryDecompressorId !== 0) + throw new Error("not implemented: secondary decompressor"); + } + + if (headerIndicator & VCD_CODETABLE) { + var codeTableDataLength = parser.read7BitEncodedInt(); + + if (codeTableDataLength !== 0) + throw new Error("not implemented: custom code table"); // custom code table + } + + if (headerIndicator & VCD_APPHEADER) { + // ignore app header data + var appDataLength = parser.read7BitEncodedInt(); + parser.skip(appDataLength); + } + var headerEndOffset = parser.offset; + + //calculate target file size + var newFileSize = 0; + while (!parser.isEOF()) { + var winHeader = parser.decodeWindowHeader(); + newFileSize += winHeader.targetWindowLength; + parser.skip( + winHeader.addRunDataLength + + winHeader.addressesLength + + winHeader.instructionsLength, + ); + } + tempFile = new BinFile(newFileSize); + + parser.seek(headerEndOffset); + + var cache = new VCD_AdressCache(4, 3); + var codeTable = VCD_DEFAULT_CODE_TABLE; + + var targetWindowPosition = 0; //renombrar + + while (!parser.isEOF()) { + var winHeader = parser.decodeWindowHeader(); + + var addRunDataStream = new VCDIFF_Parser(this.file, parser.offset); + var instructionsStream = new VCDIFF_Parser( + this.file, + addRunDataStream.offset + winHeader.addRunDataLength, + ); + var addressesStream = new VCDIFF_Parser( + this.file, + instructionsStream.offset + winHeader.instructionsLength, + ); + + var addRunDataIndex = 0; + + cache.reset(addressesStream); + + var addressesStreamEndOffset = addressesStream.offset; + while (instructionsStream.offset < addressesStreamEndOffset) { + /* + var instructionIndex=instructionsStream.readS8(); + if(instructionIndex===-1){ + break; + } + */ + var instructionIndex = instructionsStream.readU8(); + + for (var i = 0; i < 2; i++) { + var instruction = codeTable[instructionIndex][i]; + var size = instruction.size; + + if (size === 0 && instruction.type !== VCD_NOOP) { + size = instructionsStream.read7BitEncodedInt(); + } + + if (instruction.type === VCD_NOOP) { + continue; + } else if (instruction.type === VCD_ADD) { + addRunDataStream.copyToFile2( + tempFile, + addRunDataIndex + targetWindowPosition, + size, + ); + addRunDataIndex += size; + } else if (instruction.type === VCD_COPY) { + var addr = cache.decodeAddress( + addRunDataIndex + winHeader.sourceLength, + instruction.mode, + ); + var absAddr = 0; + + // source segment and target segment are treated as if they're concatenated + var sourceData = null; + if (addr < winHeader.sourceLength) { + absAddr = winHeader.sourcePosition + addr; + if (winHeader.indicator & VCD_SOURCE) { + sourceData = romFile; + } else if (winHeader.indicator & VCD_TARGET) { + sourceData = tempFile; + } + } else { + absAddr = targetWindowPosition + (addr - winHeader.sourceLength); + sourceData = tempFile; + } + + while (size--) { + tempFile._u8array[targetWindowPosition + addRunDataIndex++] = + sourceData._u8array[absAddr++]; + //targetU8[targetWindowPosition + targetWindowOffs++] = copySourceU8[absAddr++]; + } + //to-do: test + //sourceData.copyToFile2(tempFile, absAddr, size, targetWindowPosition + addRunDataIndex); + //addRunDataIndex += size; + } else if (instruction.type === VCD_RUN) { + var runByte = addRunDataStream.readU8(); + var offset = targetWindowPosition + addRunDataIndex; + for (var j = 0; j < size; j++) { + tempFile._u8array[offset + j] = runByte; + } + + addRunDataIndex += size; + } else { + throw new Error("invalid instruction type found"); + } + } + } + + if ( + validate && + winHeader.adler32 && + winHeader.adler32 !== + adler32(tempFile, targetWindowPosition, winHeader.targetWindowLength) + ) { + throw new Error("Target ROM checksum mismatch"); + } + + parser.skip( + winHeader.addRunDataLength + + winHeader.addressesLength + + winHeader.instructionsLength, + ); + targetWindowPosition += winHeader.targetWindowLength; + } + + //console.log((performance.now()-t0)/1000); + return tempFile; +}; + +VCDIFF.MAGIC = VCDIFF_MAGIC; + +VCDIFF.fromFile = function (file) { + return new VCDIFF(file); +}; + +function VCDIFF_Parser(binFile, offset) { + this.fileSize = binFile.fileSize; + this._u8array = binFile._u8array; + this.offset = offset || 0; + + /* reimplement readU8, readU32 and skip from BinFile */ + /* in web implementation, there are no guarantees BinFile will be dynamically loaded before this one */ + /* so we cannot rely on cloning BinFile.prototype */ + this.readU8 = binFile.readU8; + this.readU32 = binFile.readU32; + this.skip = binFile.skip; + this.isEOF = binFile.isEOF; + this.seek = binFile.seek; +} +VCDIFF_Parser.prototype.read7BitEncodedInt = function () { + var num = 0, + bits = 0; + + do { + bits = this.readU8(); + num = (num << 7) + (bits & 0x7f); + } while (bits & 0x80); + + return num; +}; +VCDIFF_Parser.prototype.decodeWindowHeader = function () { + var windowHeader = { + indicator: this.readU8(), + sourceLength: 0, + sourcePosition: 0, + adler32: false, + }; + + if (windowHeader.indicator & (VCD_SOURCE | VCD_TARGET)) { + windowHeader.sourceLength = this.read7BitEncodedInt(); + windowHeader.sourcePosition = this.read7BitEncodedInt(); + } + + windowHeader.deltaLength = this.read7BitEncodedInt(); + windowHeader.targetWindowLength = this.read7BitEncodedInt(); + windowHeader.deltaIndicator = this.readU8(); // secondary compression: 1=VCD_DATACOMP,2=VCD_INSTCOMP,4=VCD_ADDRCOMP + if (windowHeader.deltaIndicator !== 0) { + throw new Error( + "unimplemented windowHeader.deltaIndicator:" + + windowHeader.deltaIndicator, + ); + } + + windowHeader.addRunDataLength = this.read7BitEncodedInt(); + windowHeader.instructionsLength = this.read7BitEncodedInt(); + windowHeader.addressesLength = this.read7BitEncodedInt(); + + if (windowHeader.indicator & VCD_ADLER32) { + windowHeader.adler32 = this.readU32(); + } + + return windowHeader; +}; + +VCDIFF_Parser.prototype.copyToFile2 = function (target, targetOffset, len) { + for (var i = 0; i < len; i++) { + target._u8array[targetOffset + i] = this._u8array[this.offset + i]; + } + //this.file.copyToFile(target, this.offset, len, targetOffset); + this.skip(len); +}; + +//------------------------------------------------------ + +// hdrIndicator +const VCD_DECOMPRESS = 0x01; +const VCD_CODETABLE = 0x02; +const VCD_APPHEADER = 0x04; // nonstandard? + +// winIndicator +const VCD_SOURCE = 0x01; +const VCD_TARGET = 0x02; +const VCD_ADLER32 = 0x04; + +function VCD_Instruction(instruction, size, mode) { + this.type = instruction; + this.size = size; + this.mode = mode; +} + +/* + build the default code table (used to encode/decode instructions) specified in RFC 3284 + heavily based on + https://github.com/vic-alexiev/TelerikAcademy/blob/master/C%23%20Fundamentals%20II/Homework%20Assignments/3.%20Methods/000.%20MiscUtil/Compression/Vcdiff/CodeTable.cs +*/ +const VCD_NOOP = 0; +const VCD_ADD = 1; +const VCD_RUN = 2; +const VCD_COPY = 3; +const VCD_DEFAULT_CODE_TABLE = (function () { + var entries = []; + + var empty = { type: VCD_NOOP, size: 0, mode: 0 }; + + // 0 + entries.push([{ type: VCD_RUN, size: 0, mode: 0 }, empty]); + + // 1,18 + for (var size = 0; size < 18; size++) { + entries.push([{ type: VCD_ADD, size: size, mode: 0 }, empty]); + } + + // 19,162 + for (var mode = 0; mode < 9; mode++) { + entries.push([{ type: VCD_COPY, size: 0, mode: mode }, empty]); + + for (var size = 4; size < 19; size++) { + entries.push([{ type: VCD_COPY, size: size, mode: mode }, empty]); + } + } + + // 163,234 + for (var mode = 0; mode < 6; mode++) { + for (var addSize = 1; addSize < 5; addSize++) { + for (var copySize = 4; copySize < 7; copySize++) { + entries.push([ + { type: VCD_ADD, size: addSize, mode: 0 }, + { type: VCD_COPY, size: copySize, mode: mode }, + ]); + } + } + } + + // 235,246 + for (var mode = 6; mode < 9; mode++) { + for (var addSize = 1; addSize < 5; addSize++) { + entries.push([ + { type: VCD_ADD, size: addSize, mode: 0 }, + { type: VCD_COPY, size: 4, mode: mode }, + ]); + } + } + + // 247,255 + for (var mode = 0; mode < 9; mode++) { + entries.push([ + { type: VCD_COPY, size: 4, mode: mode }, + { type: VCD_ADD, size: 1, mode: 0 }, + ]); + } + + return entries; +})(); + +/* + ported from https://github.com/vic-alexiev/TelerikAcademy/tree/master/C%23%20Fundamentals%20II/Homework%20Assignments/3.%20Methods/000.%20MiscUtil/Compression/Vcdiff + by Victor Alexiev (https://github.com/vic-alexiev) +*/ +const VCD_MODE_SELF = 0; +const VCD_MODE_HERE = 1; +function VCD_AdressCache(nearSize, sameSize) { + this.nearSize = nearSize; + this.sameSize = sameSize; + + this.near = new Array(nearSize); + this.same = new Array(sameSize * 256); +} +VCD_AdressCache.prototype.reset = function (addressStream) { + this.nextNearSlot = 0; + this.near.fill(0); + this.same.fill(0); + + this.addressStream = addressStream; +}; +VCD_AdressCache.prototype.decodeAddress = function (here, mode) { + var address = 0; + + if (mode === VCD_MODE_SELF) { + address = this.addressStream.read7BitEncodedInt(); + } else if (mode === VCD_MODE_HERE) { + address = here - this.addressStream.read7BitEncodedInt(); + } else if (mode - 2 < this.nearSize) { + //near cache + address = this.near[mode - 2] + this.addressStream.read7BitEncodedInt(); + } else { + //same cache + var m = mode - (2 + this.nearSize); + address = this.same[m * 256 + this.addressStream.readU8()]; + } + + this.update(address); + return address; +}; +VCD_AdressCache.prototype.update = function (address) { + if (this.nearSize > 0) { + this.near[this.nextNearSlot] = address; + this.nextNearSlot = (this.nextNearSlot + 1) % this.nearSize; + } + + if (this.sameSize > 0) { + this.same[address % (this.sameSize * 256)] = address; + } +}; diff --git a/frontend/assets/patcherjs/modules/bz2/LICENSE b/frontend/assets/patcherjs/modules/bz2/LICENSE new file mode 100644 index 000000000..874dc3fe6 --- /dev/null +++ b/frontend/assets/patcherjs/modules/bz2/LICENSE @@ -0,0 +1,19 @@ +Copyright 2019 SheetJS LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/assets/patcherjs/modules/bz2/bz2.js b/frontend/assets/patcherjs/modules/bz2/bz2.js new file mode 100644 index 000000000..2c40455fd --- /dev/null +++ b/frontend/assets/patcherjs/modules/bz2/bz2.js @@ -0,0 +1 @@ +/* code has been moved to RomPatcher.format.bdf.js because of incompatibility between webapp, web worker and CLI */ diff --git a/frontend/assets/patcherjs/modules/zip.js/LICENSE b/frontend/assets/patcherjs/modules/zip.js/LICENSE new file mode 100644 index 000000000..d4b12d83f --- /dev/null +++ b/frontend/assets/patcherjs/modules/zip.js/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2013, Gildas Lormeau + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/frontend/assets/patcherjs/modules/zip.js/inflate.js b/frontend/assets/patcherjs/modules/zip.js/inflate.js new file mode 100644 index 000000000..8a80d90ed --- /dev/null +++ b/frontend/assets/patcherjs/modules/zip.js/inflate.js @@ -0,0 +1,1418 @@ +/* + Copyright (c) 2013 Gildas Lormeau. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This program is based on JZlib 1.0.2 ymnk, JCraft,Inc. + * JZlib is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +!(function (i) { + "use strict"; + var P = 0, + q = 1, + B = -2, + C = -3, + x = -4, + F = -5, + G = [ + 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, + 32767, 65535, + ], + H = 1440, + a = [ + 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, + 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, + 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, + 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, + 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, + 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, + 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, + 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, + 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, + 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, + 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, + 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, + 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, + 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, + 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, + 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, + 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, + 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, + 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, + 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, + 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, + 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, + 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, + 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, + 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, + 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, + 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, 0, 8, + 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, + 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, + 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, + 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, + 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, + 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, + 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, + 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, + 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, + 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, + 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, + 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, + 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, + 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, + 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, + 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, + 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, + 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, + 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, + 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, + 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, + 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, + 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, + 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, + 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, + 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, + 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, + 110, 0, 8, 46, 0, 9, 189, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, + 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, + 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, + 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, + 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, + 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, + 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, + 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, + 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, + 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, + 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, + 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, + 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, + 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, + 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, + 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, + 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, + 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, + 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, + 79, 0, 9, 255, + ], + r = [ + 80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, + 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, + 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, + 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, + 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, + 192, 5, 24577, + ], + w = [ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0, + ], + c = [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0, 112, 112, + ], + v = [ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, + ], + h = [ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13, + ], + D = 15; + function J() { + var f, o, E, S, U, z; + function b(i, t, e, n, a, r, _, l, d, s, f) { + var o, b, u, x, w, c, v, h, k, m, y, g, p, I, A; + for (m = 0, w = e; E[i[t + m]]++, m++, 0 !== --w; ); + if (E[0] == e) return ((_[0] = -1), (l[0] = 0), P); + for (h = l[0], c = 1; c <= D && 0 === E[c]; c++); + for (h < (v = c) && (h = c), w = D; 0 !== w && 0 === E[w]; w--); + for ((u = w) < h && (h = w), l[0] = h, I = 1 << c; c < w; c++, I <<= 1) + if ((I -= E[c]) < 0) return C; + if ((I -= E[w]) < 0) return C; + for (E[w] += I, z[1] = c = 0, m = 1, p = 2; 0 != --w; ) + ((z[p] = c += E[m]), p++, m++); + for (m = w = 0; 0 !== (c = i[t + m]) && (f[z[c]++] = w), m++, ++w < e; ); + for ( + e = z[u], z[0] = w = 0, x = -1, g = -h, A = y = U[(m = 0)] = 0; + v <= u; + v++ + ) + for (o = E[v]; 0 != o--; ) { + for (; g + h < v; ) { + if ( + (x++, + (A = h < (A = u - (g += h)) ? h : A), + (b = 1 << (c = v - g)) > o + 1 && ((b -= o + 1), (p = v), c < A)) + ) + for (; ++c < A && !((b <<= 1) <= E[++p]); ) b -= E[p]; + if (((A = 1 << c), s[0] + A > H)) return C; + ((U[x] = y = s[0]), + (s[0] += A), + 0 !== x + ? ((z[x] = w), + (S[0] = c), + (c = w >>> (g - (S[1] = h))), + (S[2] = y - U[x - 1] - c), + d.set(S, 3 * (U[x - 1] + c))) + : (_[0] = y)); + } + for ( + S[1] = v - g, + e <= m + ? (S[0] = 192) + : f[m] < n + ? ((S[0] = f[m] < 256 ? 0 : 96), (S[2] = f[m++])) + : ((S[0] = r[f[m] - n] + 16 + 64), (S[2] = a[f[m++] - n])), + b = 1 << (v - g), + c = w >>> g; + c < A; + c += b + ) + d.set(S, 3 * (y + c)); + for (c = 1 << (v - 1); 0 != (w & c); c >>>= 1) w ^= c; + for (w ^= c, k = (1 << g) - 1; (w & k) != z[x]; ) + (x--, (k = (1 << (g -= h)) - 1)); + } + return 0 !== I && 1 != u ? F : P; + } + function u(i) { + var t; + for ( + f || + ((f = []), + (o = []), + (E = new Int32Array(D + 1)), + (S = []), + (U = new Int32Array(D)), + (z = new Int32Array(D + 1))), + o.length < i && (o = []), + t = 0; + t < i; + t++ + ) + o[t] = 0; + for (t = 0; t < D + 1; t++) E[t] = 0; + for (t = 0; t < 3; t++) S[t] = 0; + (U.set(E.subarray(0, D), 0), z.set(E.subarray(0, D + 1), 0)); + } + ((this.inflate_trees_bits = function (i, t, e, n, a) { + var r; + return ( + u(19), + (r = b(i, (f[0] = 0), 19, 19, null, null, e, t, n, f, o)) == C + ? (a.msg = "oversubscribed dynamic bit lengths tree") + : (r != F && 0 !== t[0]) || + ((a.msg = "incomplete dynamic bit lengths tree"), (r = C)), + r + ); + }), + (this.inflate_trees_dynamic = function (i, t, e, n, a, r, _, l, d) { + var s; + return ( + u(288), + (s = b(e, (f[0] = 0), i, 257, w, c, r, n, l, f, o)) != P || 0 === n[0] + ? (s == C + ? (d.msg = "oversubscribed literal/length tree") + : s != x && + ((d.msg = "incomplete literal/length tree"), (s = C)), + s) + : (u(288), + (s = b(e, i, t, 0, v, h, _, a, l, f, o)) != P || + (0 === a[0] && 257 < i) + ? (s == C + ? (d.msg = "oversubscribed distance tree") + : s == F + ? ((d.msg = "incomplete distance tree"), (s = C)) + : s != x && + ((d.msg = "empty distance tree with lengths"), (s = C)), + s) + : P) + ); + })); + } + J.inflate_trees_fixed = function (i, t, e, n) { + return ((i[0] = 9), (t[0] = 5), (e[0] = a), (n[0] = r), P); + }; + var U = 0, + z = 1, + j = 2, + K = 3, + L = 4, + M = 5, + N = 6, + O = 7, + Q = 8, + R = 9; + function e() { + var u, + x, + w, + c, + v = 0, + h = 0, + k = 0, + m = 0, + y = 0, + g = 0, + p = 0, + I = 0, + A = 0, + E = 0; + function S(i, t, e, n, a, r, _, l) { + var d, s, f, o, b, u, x, w, c, v, h, k, m, y, g, p; + ((x = l.next_in_index), + (w = l.avail_in), + (b = _.bitb), + (u = _.bitk), + (v = (c = _.write) < _.read ? _.read - c - 1 : _.end - c), + (h = G[i]), + (k = G[t])); + do { + for (; u < 20; ) (w--, (b |= (255 & l.read_byte(x++)) << u), (u += 8)); + if (0 !== (o = (s = e)[(p = 3 * ((f = n) + (d = b & h)))])) + for (;;) { + if (((b >>= s[p + 1]), (u -= s[p + 1]), 0 != (16 & o))) { + for ( + o &= 15, m = s[p + 2] + (b & G[o]), b >>= o, u -= o; + u < 15; + ) + (w--, (b |= (255 & l.read_byte(x++)) << u), (u += 8)); + for (o = (s = a)[(p = 3 * ((f = r) + (d = b & k)))]; ; ) { + if (((b >>= s[p + 1]), (u -= s[p + 1]), 0 != (16 & o))) { + for (o &= 15; u < o; ) + (w--, (b |= (255 & l.read_byte(x++)) << u), (u += 8)); + if ( + ((y = s[p + 2] + (b & G[o])), + (b >>= o), + (u -= o), + (v -= m), + y <= c) + ) + (0 < c - (g = c - y) && c - g < 2 + ? ((_.window[c++] = _.window[g++]), + (_.window[c++] = _.window[g++])) + : (_.window.set(_.window.subarray(g, g + 2), c), + (c += 2), + (g += 2)), + (m -= 2)); + else { + for (g = c - y; (g += _.end) < 0; ); + if ((o = _.end - g) < m) { + if (((m -= o), 0 < c - g && c - g < o)) + for (; (_.window[c++] = _.window[g++]), 0 != --o; ); + else + (_.window.set(_.window.subarray(g, g + o), c), + (c += o), + (g += o), + (o = 0)); + g = 0; + } + } + if (0 < c - g && c - g < m) + for (; (_.window[c++] = _.window[g++]), 0 != --m; ); + else + (_.window.set(_.window.subarray(g, g + m), c), + (c += m), + (g += m), + (m = 0)); + break; + } + if (0 != (64 & o)) + return ( + (l.msg = "invalid distance code"), + (w += m = u >> 3 < (m = l.avail_in - w) ? u >> 3 : m), + (x -= m), + (u -= m << 3), + (_.bitb = b), + (_.bitk = u), + (l.avail_in = w), + (l.total_in += x - l.next_in_index), + (l.next_in_index = x), + (_.write = c), + C + ); + ((d += s[p + 2]), (o = s[(p = 3 * (f + (d += b & G[o])))])); + } + break; + } + if (0 != (64 & o)) + return 0 != (32 & o) + ? ((w += m = u >> 3 < (m = l.avail_in - w) ? u >> 3 : m), + (x -= m), + (u -= m << 3), + (_.bitb = b), + (_.bitk = u), + (l.avail_in = w), + (l.total_in += x - l.next_in_index), + (l.next_in_index = x), + (_.write = c), + q) + : ((l.msg = "invalid literal/length code"), + (w += m = u >> 3 < (m = l.avail_in - w) ? u >> 3 : m), + (x -= m), + (u -= m << 3), + (_.bitb = b), + (_.bitk = u), + (l.avail_in = w), + (l.total_in += x - l.next_in_index), + (l.next_in_index = x), + (_.write = c), + C); + if ( + ((d += s[p + 2]), 0 === (o = s[(p = 3 * (f + (d += b & G[o])))])) + ) { + ((b >>= s[p + 1]), + (u -= s[p + 1]), + (_.window[c++] = s[p + 2]), + v--); + break; + } + } + else + ((b >>= s[p + 1]), (u -= s[p + 1]), (_.window[c++] = s[p + 2]), v--); + } while (258 <= v && 10 <= w); + return ( + (w += m = u >> 3 < (m = l.avail_in - w) ? u >> 3 : m), + (x -= m), + (u -= m << 3), + (_.bitb = b), + (_.bitk = u), + (l.avail_in = w), + (l.total_in += x - l.next_in_index), + (l.next_in_index = x), + (_.write = c), + P + ); + } + ((this.init = function (i, t, e, n, a, r) { + ((u = U), + (p = i), + (I = t), + (w = e), + (A = n), + (c = a), + (E = r), + (x = null)); + }), + (this.proc = function (i, t, e) { + var n, + a, + r, + _, + l, + d, + s, + f = 0, + o = 0, + b = 0; + for ( + b = t.next_in_index, + _ = t.avail_in, + f = i.bitb, + o = i.bitk, + d = (l = i.write) < i.read ? i.read - l - 1 : i.end - l; + ; + ) + switch (u) { + case U: + if ( + 258 <= d && + 10 <= _ && + ((i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + (e = S(p, I, w, A, c, E, i, t)), + (b = t.next_in_index), + (_ = t.avail_in), + (f = i.bitb), + (o = i.bitk), + (d = (l = i.write) < i.read ? i.read - l - 1 : i.end - l), + e != P) + ) { + u = e == q ? O : R; + break; + } + ((k = p), (x = w), (h = A), (u = z)); + case z: + for (n = k; o < n; ) { + if (0 === _) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((e = P), _--, (f |= (255 & t.read_byte(b++)) << o), (o += 8)); + } + if ( + ((a = 3 * (h + (f & G[n]))), + (f >>>= x[a + 1]), + (o -= x[a + 1]), + 0 === (r = x[a])) + ) { + ((m = x[a + 2]), (u = N)); + break; + } + if (0 != (16 & r)) { + ((y = 15 & r), (v = x[a + 2]), (u = j)); + break; + } + if (0 == (64 & r)) { + ((k = r), (h = a / 3 + x[a + 2])); + break; + } + if (0 == (32 & r)) + return ( + (u = R), + (t.msg = "invalid literal/length code"), + (e = C), + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + u = O; + break; + case j: + for (n = y; o < n; ) { + if (0 === _) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((e = P), _--, (f |= (255 & t.read_byte(b++)) << o), (o += 8)); + } + ((v += f & G[n]), + (f >>= n), + (o -= n), + (k = I), + (x = c), + (h = E), + (u = K)); + case K: + for (n = k; o < n; ) { + if (0 === _) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((e = P), _--, (f |= (255 & t.read_byte(b++)) << o), (o += 8)); + } + if ( + ((a = 3 * (h + (f & G[n]))), + (f >>= x[a + 1]), + (o -= x[a + 1]), + 0 != (16 & (r = x[a]))) + ) { + ((y = 15 & r), (g = x[a + 2]), (u = L)); + break; + } + if (0 != (64 & r)) + return ( + (u = R), + (t.msg = "invalid distance code"), + (e = C), + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((k = r), (h = a / 3 + x[a + 2])); + break; + case L: + for (n = y; o < n; ) { + if (0 === _) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((e = P), _--, (f |= (255 & t.read_byte(b++)) << o), (o += 8)); + } + ((g += f & G[n]), (f >>= n), (o -= n), (u = M)); + case M: + for (s = l - g; s < 0; ) s += i.end; + for (; 0 !== v; ) { + if ( + 0 === d && + (l == i.end && + 0 !== i.read && + (d = (l = 0) < i.read ? i.read - l - 1 : i.end - l), + 0 === d && + ((i.write = l), + (e = i.inflate_flush(t, e)), + (d = (l = i.write) < i.read ? i.read - l - 1 : i.end - l), + l == i.end && + 0 !== i.read && + (d = (l = 0) < i.read ? i.read - l - 1 : i.end - l), + 0 === d)) + ) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((i.window[l++] = i.window[s++]), + d--, + s == i.end && (s = 0), + v--); + } + u = U; + break; + case N: + if ( + 0 === d && + (l == i.end && + 0 !== i.read && + (d = (l = 0) < i.read ? i.read - l - 1 : i.end - l), + 0 === d && + ((i.write = l), + (e = i.inflate_flush(t, e)), + (d = (l = i.write) < i.read ? i.read - l - 1 : i.end - l), + l == i.end && + 0 !== i.read && + (d = (l = 0) < i.read ? i.read - l - 1 : i.end - l), + 0 === d)) + ) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + ((e = P), (i.window[l++] = m), d--, (u = U)); + break; + case O: + if ( + (7 < o && ((o -= 8), _++, b--), + (i.write = l), + (e = i.inflate_flush(t, e)), + (d = (l = i.write) < i.read ? i.read - l - 1 : i.end - l), + i.read != i.write) + ) + return ( + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + u = Q; + case Q: + return ( + (e = q), + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + case R: + return ( + (e = C), + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + default: + return ( + (e = B), + (i.bitb = f), + (i.bitk = o), + (t.avail_in = _), + (t.total_in += b - t.next_in_index), + (t.next_in_index = b), + (i.write = l), + i.inflate_flush(t, e) + ); + } + }), + (this.free = function () {})); + } + var T = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15], + V = 0, + W = 1, + X = 2, + Y = 3, + Z = 4, + $ = 5, + ii = 6, + ti = 7, + ei = 8, + ni = 9; + function n(i, t) { + var m, + y = this, + g = V, + p = 0, + I = 0, + A = 0, + E = [0], + S = [0], + U = new e(), + z = 0, + D = new Int32Array(3 * H), + j = new J(); + ((y.bitk = 0), + (y.bitb = 0), + (y.window = new Uint8Array(t)), + (y.end = t), + (y.read = 0), + (y.write = 0), + (y.reset = function (i, t) { + (t && (t[0] = 0), + g == ii && U.free(i), + (g = V), + (y.bitk = 0), + (y.bitb = 0), + (y.read = y.write = 0)); + }), + y.reset(i, null), + (y.inflate_flush = function (i, t) { + var e, n, a; + return ( + (n = i.next_out_index), + (e = ((a = y.read) <= y.write ? y.write : y.end) - a) > i.avail_out && + (e = i.avail_out), + 0 !== e && t == F && (t = P), + (i.avail_out -= e), + (i.total_out += e), + i.next_out.set(y.window.subarray(a, a + e), n), + (n += e), + (a += e) == y.end && + ((a = 0), + y.write == y.end && (y.write = 0), + (e = y.write - a) > i.avail_out && (e = i.avail_out), + 0 !== e && t == F && (t = P), + (i.avail_out -= e), + (i.total_out += e), + i.next_out.set(y.window.subarray(a, a + e), n), + (n += e), + (a += e)), + (i.next_out_index = n), + (y.read = a), + t + ); + }), + (y.proc = function (i, t) { + var e, n, a, r, _, l, d, s; + for ( + r = i.next_in_index, + _ = i.avail_in, + n = y.bitb, + a = y.bitk, + d = (l = y.write) < y.read ? y.read - l - 1 : y.end - l; + ; + ) + switch (g) { + case V: + for (; a < 3; ) { + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((t = P), _--, (n |= (255 & i.read_byte(r++)) << a), (a += 8)); + } + switch (((z = 1 & (e = 7 & n)), e >>> 1)) { + case 0: + ((n >>>= 3), (n >>>= e = 7 & (a -= 3)), (a -= e), (g = W)); + break; + case 1: + var f = [], + o = [], + b = [[]], + u = [[]]; + (J.inflate_trees_fixed(f, o, b, u), + U.init(f[0], o[0], b[0], 0, u[0], 0), + (n >>>= 3), + (a -= 3), + (g = ii)); + break; + case 2: + ((n >>>= 3), (a -= 3), (g = Y)); + break; + case 3: + return ( + (n >>>= 3), + (a -= 3), + (g = ni), + (i.msg = "invalid block type"), + (t = C), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + } + break; + case W: + for (; a < 32; ) { + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((t = P), _--, (n |= (255 & i.read_byte(r++)) << a), (a += 8)); + } + if (((~n >>> 16) & 65535) != (65535 & n)) + return ( + (g = ni), + (i.msg = "invalid stored block lengths"), + (t = C), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((p = 65535 & n), + (n = a = 0), + (g = 0 !== p ? X : 0 !== z ? ti : V)); + break; + case X: + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + if ( + 0 === d && + (l == y.end && + 0 !== y.read && + (d = (l = 0) < y.read ? y.read - l - 1 : y.end - l), + 0 === d && + ((y.write = l), + (t = y.inflate_flush(i, t)), + (d = (l = y.write) < y.read ? y.read - l - 1 : y.end - l), + l == y.end && + 0 !== y.read && + (d = (l = 0) < y.read ? y.read - l - 1 : y.end - l), + 0 === d)) + ) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + if ( + ((t = P), + _ < (e = p) && (e = _), + d < e && (e = d), + y.window.set(i.read_buf(r, e), l), + (r += e), + (_ -= e), + (l += e), + (d -= e), + 0 != (p -= e)) + ) + break; + g = 0 !== z ? ti : V; + break; + case Y: + for (; a < 14; ) { + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((t = P), _--, (n |= (255 & i.read_byte(r++)) << a), (a += 8)); + } + if (((I = e = 16383 & n), 29 < (31 & e) || 29 < ((e >> 5) & 31))) + return ( + (g = ni), + (i.msg = "too many length or distance symbols"), + (t = C), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + if (((e = 258 + (31 & e) + ((e >> 5) & 31)), !m || m.length < e)) + m = []; + else for (s = 0; s < e; s++) m[s] = 0; + ((n >>>= 14), (a -= 14), (A = 0), (g = Z)); + case Z: + for (; A < 4 + (I >>> 10); ) { + for (; a < 3; ) { + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((t = P), + _--, + (n |= (255 & i.read_byte(r++)) << a), + (a += 8)); + } + ((m[T[A++]] = 7 & n), (n >>>= 3), (a -= 3)); + } + for (; A < 19; ) m[T[A++]] = 0; + if (((E[0] = 7), (e = j.inflate_trees_bits(m, E, S, D, i)) != P)) + return ( + (t = e) == C && ((m = null), (g = ni)), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((A = 0), (g = $)); + case $: + for (; !(258 + (31 & (e = I)) + ((e >> 5) & 31) <= A); ) { + var x, w; + for (e = E[0]; a < e; ) { + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((t = P), + _--, + (n |= (255 & i.read_byte(r++)) << a), + (a += 8)); + } + if ( + ((e = D[3 * (S[0] + (n & G[e])) + 1]), + (w = D[3 * (S[0] + (n & G[e])) + 2]) < 16) + ) + ((n >>>= e), (a -= e), (m[A++] = w)); + else { + for ( + s = 18 == w ? 7 : w - 14, x = 18 == w ? 11 : 3; + a < e + s; + ) { + if (0 === _) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + ((t = P), + _--, + (n |= (255 & i.read_byte(r++)) << a), + (a += 8)); + } + if ( + ((a -= e), + (x += (n >>>= e) & G[s]), + (n >>>= s), + (a -= s), + 258 + (31 & (e = I)) + ((e >> 5) & 31) < (s = A) + x || + (16 == w && s < 1)) + ) + return ( + (m = null), + (g = ni), + (i.msg = "invalid bit length repeat"), + (t = C), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + for (w = 16 == w ? m[s - 1] : 0; (m[s++] = w), 0 != --x; ); + A = s; + } + } + S[0] = -1; + var c = [], + v = [], + h = [], + k = []; + if ( + ((c[0] = 9), + (v[0] = 6), + (e = I), + (e = j.inflate_trees_dynamic( + 257 + (31 & e), + 1 + ((e >> 5) & 31), + m, + c, + v, + h, + k, + D, + i, + )) != P) + ) + return ( + e == C && ((m = null), (g = ni)), + (t = e), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + (U.init(c[0], v[0], D, h[0], D, k[0]), (g = ii)); + case ii: + if ( + ((y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + (t = U.proc(y, i, t)) != q) + ) + return y.inflate_flush(i, t); + if ( + ((t = P), + U.free(i), + (r = i.next_in_index), + (_ = i.avail_in), + (n = y.bitb), + (a = y.bitk), + (d = (l = y.write) < y.read ? y.read - l - 1 : y.end - l), + 0 === z) + ) { + g = V; + break; + } + g = ti; + case ti: + if ( + ((y.write = l), + (t = y.inflate_flush(i, t)), + (d = (l = y.write) < y.read ? y.read - l - 1 : y.end - l), + y.read != y.write) + ) + return ( + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + g = ei; + case ei: + return ( + (t = q), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + case ni: + return ( + (t = C), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + default: + return ( + (t = B), + (y.bitb = n), + (y.bitk = a), + (i.avail_in = _), + (i.total_in += r - i.next_in_index), + (i.next_in_index = r), + (y.write = l), + y.inflate_flush(i, t) + ); + } + }), + (y.free = function (i) { + (y.reset(i, null), (y.window = null), (D = null)); + }), + (y.set_dictionary = function (i, t, e) { + (y.window.set(i.subarray(t, t + e), 0), (y.read = y.write = e)); + }), + (y.sync_point = function () { + return g == W ? 1 : 0; + })); + } + var l = [0, 0, 255, 255]; + function t() { + var e = this; + function _(i) { + return i && i.istate + ? ((i.total_in = i.total_out = 0), + (i.msg = null), + (i.istate.mode = 7), + i.istate.blocks.reset(i, null), + P) + : B; + } + ((e.mode = 0), + (e.method = 0), + (e.was = [0]), + (e.need = 0), + (e.marker = 0), + (e.wbits = 0), + (e.inflateEnd = function (i) { + return (e.blocks && e.blocks.free(i), (e.blocks = null), P); + }), + (e.inflateInit = function (i, t) { + return ( + (i.msg = null), + (e.blocks = null), + t < 8 || 15 < t + ? (e.inflateEnd(i), B) + : ((e.wbits = t), (i.istate.blocks = new n(i, 1 << t)), _(i), P) + ); + }), + (e.inflate = function (i, t) { + var e, n; + if (!i || !i.istate || !i.next_in) return B; + for (t = 4 == t ? F : P, e = F; ; ) + switch (i.istate.mode) { + case 0: + if (0 === i.avail_in) return e; + if ( + ((e = t), + i.avail_in--, + i.total_in++, + 8 != (15 & (i.istate.method = i.read_byte(i.next_in_index++)))) + ) { + ((i.istate.mode = 13), + (i.msg = "unknown compression method"), + (i.istate.marker = 5)); + break; + } + if (8 + (i.istate.method >> 4) > i.istate.wbits) { + ((i.istate.mode = 13), + (i.msg = "invalid window size"), + (i.istate.marker = 5)); + break; + } + i.istate.mode = 1; + case 1: + if (0 === i.avail_in) return e; + if ( + ((e = t), + i.avail_in--, + i.total_in++, + (n = 255 & i.read_byte(i.next_in_index++)), + ((i.istate.method << 8) + n) % 31 != 0) + ) { + ((i.istate.mode = 13), + (i.msg = "incorrect header check"), + (i.istate.marker = 5)); + break; + } + if (0 == (32 & n)) { + i.istate.mode = 7; + break; + } + i.istate.mode = 2; + case 2: + if (0 === i.avail_in) return e; + ((e = t), + i.avail_in--, + i.total_in++, + (i.istate.need = + ((255 & i.read_byte(i.next_in_index++)) << 24) & 4278190080), + (i.istate.mode = 3)); + case 3: + if (0 === i.avail_in) return e; + ((e = t), + i.avail_in--, + i.total_in++, + (i.istate.need += + ((255 & i.read_byte(i.next_in_index++)) << 16) & 16711680), + (i.istate.mode = 4)); + case 4: + if (0 === i.avail_in) return e; + ((e = t), + i.avail_in--, + i.total_in++, + (i.istate.need += + ((255 & i.read_byte(i.next_in_index++)) << 8) & 65280), + (i.istate.mode = 5)); + case 5: + return 0 === i.avail_in + ? e + : ((e = t), + i.avail_in--, + i.total_in++, + (i.istate.need += 255 & i.read_byte(i.next_in_index++)), + (i.istate.mode = 6), + 2); + case 6: + return ( + (i.istate.mode = 13), + (i.msg = "need dictionary"), + (i.istate.marker = 0), + B + ); + case 7: + if ((e = i.istate.blocks.proc(i, e)) == C) { + ((i.istate.mode = 13), (i.istate.marker = 0)); + break; + } + if ((e == P && (e = t), e != q)) return e; + ((e = t), + i.istate.blocks.reset(i, i.istate.was), + (i.istate.mode = 12)); + case 12: + return q; + case 13: + return C; + default: + return B; + } + }), + (e.inflateSetDictionary = function (i, t, e) { + var n = 0, + a = e; + return i && i.istate && 6 == i.istate.mode + ? (a >= 1 << i.istate.wbits && + (n = e - (a = (1 << i.istate.wbits) - 1)), + i.istate.blocks.set_dictionary(t, n, a), + (i.istate.mode = 7), + P) + : B; + }), + (e.inflateSync = function (i) { + var t, e, n, a, r; + if (!i || !i.istate) return B; + if ( + (13 != i.istate.mode && ((i.istate.mode = 13), (i.istate.marker = 0)), + 0 === (t = i.avail_in)) + ) + return F; + for (e = i.next_in_index, n = i.istate.marker; 0 !== t && n < 4; ) + (i.read_byte(e) == l[n] + ? n++ + : (n = 0 !== i.read_byte(e) ? 0 : 4 - n), + e++, + t--); + return ( + (i.total_in += e - i.next_in_index), + (i.next_in_index = e), + (i.avail_in = t), + 4 != (i.istate.marker = n) + ? C + : ((a = i.total_in), + (r = i.total_out), + _(i), + (i.total_in = a), + (i.total_out = r), + (i.istate.mode = 7), + P) + ); + }), + (e.inflateSyncPoint = function (i) { + return i && i.istate && i.istate.blocks + ? i.istate.blocks.sync_point() + : B; + })); + } + function _() {} + _.prototype = { + inflateInit: function (i) { + return ( + (this.istate = new t()), + i || (i = 15), + this.istate.inflateInit(this, i) + ); + }, + inflate: function (i) { + return this.istate ? this.istate.inflate(this, i) : B; + }, + inflateEnd: function () { + if (!this.istate) return B; + var i = this.istate.inflateEnd(this); + return ((this.istate = null), i); + }, + inflateSync: function () { + return this.istate ? this.istate.inflateSync(this) : B; + }, + inflateSetDictionary: function (i, t) { + return this.istate ? this.istate.inflateSetDictionary(this, i, t) : B; + }, + read_byte: function (i) { + return this.next_in.subarray(i, i + 1)[0]; + }, + read_buf: function (i, t) { + return this.next_in.subarray(i, i + t); + }, + }; + var d = i.zip || i; + d.Inflater = d._jzlib_Inflater = function () { + var d = new _(), + s = new Uint8Array(512), + f = !1; + (d.inflateInit(), + (d.next_out = s), + (this.append = function (i, t) { + var e, + n, + a = [], + r = 0, + _ = 0, + l = 0; + if (0 !== i.length) { + ((d.next_in_index = 0), (d.next_in = i), (d.avail_in = i.length)); + do { + if ( + ((d.next_out_index = 0), + (d.avail_out = 512), + 0 !== d.avail_in || f || ((d.next_in_index = 0), (f = !0)), + (e = d.inflate(0)), + f && e === F) + ) { + if (0 !== d.avail_in) throw new Error("inflating: bad input"); + } else if (e !== P && e !== q) + throw new Error("inflating: " + d.msg); + if ((f || e === q) && d.avail_in === i.length) + throw new Error("inflating: bad input"); + (d.next_out_index && + (512 === d.next_out_index + ? a.push(new Uint8Array(s)) + : a.push(new Uint8Array(s.subarray(0, d.next_out_index)))), + (l += d.next_out_index), + t && + 0 < d.next_in_index && + d.next_in_index != r && + (t(d.next_in_index), (r = d.next_in_index))); + } while (0 < d.avail_in || 0 === d.avail_out); + return ( + (n = new Uint8Array(l)), + a.forEach(function (i) { + (n.set(i, _), (_ += i.length)); + }), + n + ); + } + }), + (this.flush = function () { + d.inflateEnd(); + })); + }; +})(this); diff --git a/frontend/assets/patcherjs/modules/zip.js/z-worker.js b/frontend/assets/patcherjs/modules/zip.js/z-worker.js new file mode 100644 index 000000000..16dce86c5 --- /dev/null +++ b/frontend/assets/patcherjs/modules/zip.js/z-worker.js @@ -0,0 +1,116 @@ +/* jshint worker:true */ +!(function (c) { + "use strict"; + if (c.zWorkerInitialized) + throw new Error("z-worker.js should be run only once"); + ((c.zWorkerInitialized = !0), + addEventListener("message", function (t) { + var e, + r, + c = t.data, + n = c.type, + s = c.sn, + p = o[n]; + if (p) + try { + p(c); + } catch (t) { + ((e = { + type: n, + sn: s, + error: ((r = t), { message: r.message, stack: r.stack }), + }), + postMessage(e)); + } + })); + var o = { + importScripts: function (t) { + t.scripts && + 0 < t.scripts.length && + importScripts.apply(void 0, t.scripts); + postMessage({ type: "importScripts" }); + }, + newTask: h, + append: t, + flush: t, + }, + f = {}; + function h(t) { + var e = c[t.codecClass], + r = t.sn; + if (f[r]) throw Error("duplicated sn"); + ((f[r] = { + codec: new e(t.options), + crcInput: "input" === t.crcType, + crcOutput: "output" === t.crcType, + crc: new n(), + }), + postMessage({ type: "newTask", sn: r })); + } + var l = c.performance ? c.performance.now.bind(c.performance) : Date.now; + function t(t) { + var e = t.sn, + r = t.type, + c = t.data, + n = f[e]; + !n && t.codecClass && (h(t), (n = f[e])); + var s, + p = "append" === r, + o = l(); + if (p) + try { + s = n.codec.append(c, function (t) { + postMessage({ type: "progress", sn: e, loaded: t }); + }); + } catch (t) { + throw (delete f[e], t); + } + else (delete f[e], (s = n.codec.flush())); + var a = l() - o; + ((o = l()), + c && n.crcInput && n.crc.append(c), + s && n.crcOutput && n.crc.append(s)); + var i = l() - o, + u = { type: r, sn: e, codecTime: a, crcTime: i }, + d = []; + (s && ((u.data = s), d.push(s.buffer)), + p || (!n.crcInput && !n.crcOutput) || (u.crc = n.crc.get())); + try { + postMessage(u, d); + } catch (t) { + postMessage(u); + } + } + function n() { + this.crc = -1; + } + function e() {} + ((n.prototype.append = function (t) { + for ( + var e = 0 | this.crc, r = this.table, c = 0, n = 0 | t.length; + c < n; + c++ + ) + e = (e >>> 8) ^ r[255 & (e ^ t[c])]; + this.crc = e; + }), + (n.prototype.get = function () { + return ~this.crc; + }), + (n.prototype.table = (function () { + var t, + e, + r, + c = []; + for (t = 0; t < 256; t++) { + for (r = t, e = 0; e < 8; e++) + 1 & r ? (r = (r >>> 1) ^ 3988292384) : (r >>>= 1); + c[t] = r; + } + return c; + })()), + ((c.NOOP = e).prototype.append = function (t, e) { + return t; + }), + (e.prototype.flush = function () {})); +})(this); diff --git a/frontend/assets/patcherjs/modules/zip.js/zip.min.js b/frontend/assets/patcherjs/modules/zip.js/zip.min.js new file mode 100644 index 000000000..8e9b1f059 --- /dev/null +++ b/frontend/assets/patcherjs/modules/zip.js/zip.min.js @@ -0,0 +1,973 @@ +/* + Copyright (c) 2013 Gildas Lormeau. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +!(function (b) { + "use strict"; + var o, + k = "File format is not recognized.", + a = "File contains encrypted entry.", + s = "File is using Zip64 (4gb+ file size).", + w = "Error while reading zip file.", + n = "Error while reading file data.", + y = 524288, + c = "text/plain"; + try { + o = 0 === new Blob([new DataView(new ArrayBuffer(0))]).size; + } catch (e) {} + function r() { + this.crc = -1; + } + function l() {} + function A(e, t) { + var r, n; + return ( + (r = new ArrayBuffer(e)), + (n = new Uint8Array(r)), + t && n.set(t, 0), + { buffer: r, array: n, view: new DataView(r) } + ); + } + function e() {} + function t(n) { + var i, + o = this; + ((o.size = 0), + (o.init = function (e, t) { + var r = new Blob([n], { type: c }); + (i = new f(r)).init(function () { + ((o.size = i.size), e()); + }, t); + }), + (o.readUint8Array = function (e, t, r, n) { + i.readUint8Array(e, t, r, n); + })); + } + function i(f) { + var u, + r = this; + ((r.size = 0), + (r.init = function (e) { + for (var t = f.length; "=" == f.charAt(t - 1); ) t--; + ((u = f.indexOf(",") + 1), (r.size = Math.floor(0.75 * (t - u))), e()); + }), + (r.readUint8Array = function (e, t, r) { + var n, + i = A(t), + o = 4 * Math.floor(e / 3), + a = 4 * Math.ceil((e + t) / 3), + s = b.atob(f.substring(o + u, a + u)), + c = e - 3 * Math.floor(o / 4); + for (n = c; n < c + t; n++) i.array[n - c] = s.charCodeAt(n); + r(i.array); + })); + } + function f(o) { + var t = this; + ((t.size = 0), + (t.init = function (e) { + ((t.size = o.size), e()); + }), + (t.readUint8Array = function (e, t, r, n) { + var i = new FileReader(); + ((i.onload = function (e) { + r(new Uint8Array(e.target.result)); + }), + (i.onerror = n)); + try { + i.readAsArrayBuffer( + (function (e, t, r) { + if (t < 0 || r < 0 || t + r > e.size) + throw new RangeError( + "offset:" + t + ", length:" + r + ", size:" + e.size, + ); + return e.slice + ? e.slice(t, t + r) + : e.webkitSlice + ? e.webkitSlice(t, t + r) + : e.mozSlice + ? e.mozSlice(t, t + r) + : e.msSlice + ? e.msSlice(t, t + r) + : void 0; + })(o, e, t), + ); + } catch (e) { + n(e); + } + })); + } + function u() {} + function h(n) { + var i; + ((this.init = function (e) { + ((i = new Blob([], { type: c })), e()); + }), + (this.writeUint8Array = function (e, t) { + ((i = new Blob([i, o ? e : e.buffer], { type: c })), t()); + }), + (this.getData = function (t, e) { + var r = new FileReader(); + ((r.onload = function (e) { + t(e.target.result); + }), + (r.onerror = e), + r.readAsText(i, n)); + })); + } + function p(t) { + var o = "", + a = ""; + ((this.init = function (e) { + ((o += "data:" + (t || "") + ";base64,"), e()); + }), + (this.writeUint8Array = function (e, t) { + var r, + n = a.length, + i = a; + for (a = "", r = 0; r < 3 * Math.floor((n + e.length) / 3) - n; r++) + i += String.fromCharCode(e[r]); + for (; r < e.length; r++) a += String.fromCharCode(e[r]); + (2 < i.length ? (o += b.btoa(i)) : (a = i), t()); + }), + (this.getData = function (e) { + e(o + b.btoa(a)); + })); + } + function v(r) { + var n; + ((this.init = function (e) { + ((n = new Blob([], { type: r })), e()); + }), + (this.writeUint8Array = function (e, t) { + ((n = new Blob([n, o ? e : e.buffer], { type: r })), t()); + }), + (this.getData = function (e) { + e(n); + })); + } + function S(i, r, e, o, t, a, s, n, c, f) { + var u, + l, + w, + h = 0, + p = r.sn; + function v() { + (i.removeEventListener("message", d, !1), n(l, w)); + } + function d(e) { + var t = e.data, + r = t.data, + n = t.error; + if (n) + return ( + (n.toString = function () { + return "Error: " + this.message; + }), + void c(n) + ); + if (t.sn === p) + switch ( + ("number" == typeof t.codecTime && (i.codecTime += t.codecTime), + "number" == typeof t.crcTime && (i.crcTime += t.crcTime), + t.type) + ) { + case "append": + r + ? ((l += r.length), + o.writeUint8Array( + r, + function () { + g(); + }, + f, + )) + : g(); + break; + case "flush": + ((w = t.crc), + r + ? ((l += r.length), + o.writeUint8Array( + r, + function () { + v(); + }, + f, + )) + : v()); + break; + case "progress": + s && s(u + t.loaded, a); + break; + case "importScripts": + case "newTask": + case "echo": + break; + default: + console.warn("zip.js:launchWorkerProcess: unknown message: ", t); + } + } + function g() { + (u = h * y) <= a + ? e.readUint8Array( + t + u, + Math.min(y, a - u), + function (e) { + s && s(u, a); + var t = 0 === u ? r : { sn: p }; + ((t.type = "append"), (t.data = e)); + try { + i.postMessage(t, [e.buffer]); + } catch (e) { + i.postMessage(t); + } + h++; + }, + c, + ) + : i.postMessage({ sn: p, type: "flush" }); + } + ((l = 0), i.addEventListener("message", d, !1), g()); + } + function _(n, t, i, o, a, e, s, c, f, u) { + var l, + w = 0, + h = 0, + p = "input" === e, + v = "output" === e, + d = new r(); + !(function r() { + var e; + if ((l = w * y) < a) + t.readUint8Array( + o + l, + Math.min(y, a - l), + function (e) { + var t; + try { + t = n.append(e, function (e) { + s && s(l + e, a); + }); + } catch (e) { + return void f(e); + } + (t + ? ((h += t.length), + i.writeUint8Array( + t, + function () { + (w++, setTimeout(r, 1)); + }, + u, + ), + v && d.append(t)) + : (w++, setTimeout(r, 1)), + p && d.append(e), + s && s(l, a)); + }, + f, + ); + else { + try { + e = n.flush(); + } catch (e) { + return void f(e); + } + e + ? (v && d.append(e), + (h += e.length), + i.writeUint8Array( + e, + function () { + c(h, d.get()); + }, + u, + )) + : c(h, d.get()); + } + })(); + } + function D(e, t, r, n, i, o, a, s, c, f, u) { + b.zip.useWebWorkers && a + ? S( + e, + { sn: t, codecClass: "NOOP", crcType: "input" }, + r, + n, + i, + o, + c, + s, + f, + u, + ) + : _(new l(), r, n, i, o, "input", c, s, f, u); + } + function d(e) { + var t, + r, + n = "", + i = [ + "Ç", + "ü", + "é", + "â", + "ä", + "à", + "å", + "ç", + "ê", + "ë", + "è", + "ï", + "î", + "ì", + "Ä", + "Å", + "É", + "æ", + "Æ", + "ô", + "ö", + "ò", + "û", + "ù", + "ÿ", + "Ö", + "Ü", + "ø", + "£", + "Ø", + "×", + "ƒ", + "á", + "í", + "ó", + "ú", + "ñ", + "Ñ", + "ª", + "º", + "¿", + "®", + "¬", + "½", + "¼", + "¡", + "«", + "»", + "_", + "_", + "_", + "¦", + "¦", + "Á", + "Â", + "À", + "©", + "¦", + "¦", + "+", + "+", + "¢", + "¥", + "+", + "+", + "-", + "-", + "+", + "-", + "+", + "ã", + "Ã", + "+", + "+", + "-", + "-", + "¦", + "-", + "+", + "¤", + "ð", + "Ð", + "Ê", + "Ë", + "È", + "i", + "Í", + "Î", + "Ï", + "+", + "+", + "_", + "_", + "¦", + "Ì", + "_", + "Ó", + "ß", + "Ô", + "Ò", + "õ", + "Õ", + "µ", + "þ", + "Þ", + "Ú", + "Û", + "Ù", + "ý", + "Ý", + "¯", + "´", + "­", + "±", + "_", + "¾", + "¶", + "§", + "÷", + "¸", + "°", + "¨", + "·", + "¹", + "³", + "²", + "_", + " ", + ]; + for (t = 0; t < e.length; t++) + n += + 127 < (r = 255 & e.charCodeAt(t)) ? i[r - 128] : String.fromCharCode(r); + return n; + } + function g(e) { + return decodeURIComponent(escape(e)); + } + function L(e) { + var t, + r = ""; + for (t = 0; t < e.length; t++) r += String.fromCharCode(e[t]); + return r; + } + function M(e, t, r, n, i) { + ((e.version = t.view.getUint16(r, !0)), + (e.bitFlag = t.view.getUint16(r + 2, !0)), + (e.compressionMethod = t.view.getUint16(r + 4, !0)), + (e.lastModDateRaw = t.view.getUint32(r + 6, !0)), + (e.lastModDate = (function (e) { + var t = (4294901760 & e) >> 16, + r = 65535 & e; + try { + return new Date( + 1980 + ((65024 & t) >> 9), + ((480 & t) >> 5) - 1, + 31 & t, + (63488 & r) >> 11, + (2016 & r) >> 5, + 2 * (31 & r), + 0, + ); + } catch (e) {} + })(e.lastModDateRaw)), + 1 != (1 & e.bitFlag) + ? ((n || 8 != (8 & e.bitFlag)) && + ((e.crc32 = t.view.getUint32(r + 10, !0)), + (e.compressedSize = t.view.getUint32(r + 14, !0)), + (e.uncompressedSize = t.view.getUint32(r + 18, !0))), + 4294967295 !== e.compressedSize && 4294967295 !== e.uncompressedSize + ? ((e.filenameLength = t.view.getUint16(r + 22, !0)), + (e.extraFieldLength = t.view.getUint16(r + 24, !0))) + : i(s)) + : i(a)); + } + function m(m, t, U) { + var z = 0; + function l() {} + l.prototype.getData = function (w, i, h, p) { + var v = this; + function d(e, t) { + var r, n; + p && + ((r = t), + (n = A(4)).view.setUint32(0, r), + v.crc32 != n.view.getUint32(0)) + ? U("CRC failed.") + : w.getData(function (e) { + i(e); + }); + } + function g(e) { + U(e || n); + } + function y(e) { + U(e || "Error while writing file data."); + } + m.readUint8Array( + v.offset, + 30, + function (e) { + var l, + t = A(e.length, e); + 1347093252 == t.view.getUint32(0) + ? (M(v, t, 4, !1, U), + (l = v.offset + 30 + v.filenameLength + v.extraFieldLength), + w.init(function () { + var e, t, r, n, i, o, a, s, c, f, u; + 0 === v.compressionMethod + ? D(v._worker, z++, m, w, l, v.compressedSize, p, d, h, g, y) + : ((e = v._worker), + (t = z++), + (r = m), + (n = w), + (i = l), + (o = v.compressedSize), + (a = d), + (s = h), + (c = g), + (f = y), + (u = p ? "output" : "none"), + b.zip.useWebWorkers + ? S( + e, + { sn: t, codecClass: "Inflater", crcType: u }, + r, + n, + i, + o, + s, + a, + c, + f, + ) + : _(new b.zip.Inflater(), r, n, i, o, u, s, a, c, f)); + }, y)) + : U(k); + }, + g, + ); + }; + var r = { + getEntries: function (f) { + var u = this._worker; + !(function (n) { + var i = 22; + if (m.size < i) U(k); + else { + var e = i + 65536; + t(i, function () { + t(Math.min(e, m.size), function () { + U(k); + }); + }); + } + function t(e, r) { + m.readUint8Array( + m.size - e, + e, + function (e) { + for (var t = e.length - i; 0 <= t; t--) + if ( + 80 === e[t] && + 75 === e[t + 1] && + 5 === e[t + 2] && + 6 === e[t + 3] + ) + return void n(new DataView(e.buffer, t, i)); + r(); + }, + function () { + U(w); + }, + ); + } + })(function (e) { + var t, c; + ((t = e.getUint32(16, !0)), + (c = e.getUint16(8, !0)), + t < 0 || t >= m.size + ? U(k) + : m.readUint8Array( + t, + m.size - t, + function (e) { + var t, + r, + n, + i, + o = 0, + a = [], + s = A(e.length, e); + for (t = 0; t < c; t++) { + if ( + (((r = new l())._worker = u), + 1347092738 != s.view.getUint32(o)) + ) + return void U(k); + (M(r, s, o + 6, !0, U), + (r.commentLength = s.view.getUint16(o + 32, !0)), + (r.directory = 16 == (16 & s.view.getUint8(o + 38))), + (r.offset = s.view.getUint32(o + 42, !0)), + (n = L( + s.array.subarray(o + 46, o + 46 + r.filenameLength), + )), + (r.filename = 2048 == (2048 & r.bitFlag) ? g(n) : d(n)), + r.directory || + "/" != r.filename.charAt(r.filename.length - 1) || + (r.directory = !0), + (i = L( + s.array.subarray( + o + 46 + r.filenameLength + r.extraFieldLength, + o + + 46 + + r.filenameLength + + r.extraFieldLength + + r.commentLength, + ), + )), + (r.comment = 2048 == (2048 & r.bitFlag) ? g(i) : d(i)), + a.push(r), + (o += + 46 + + r.filenameLength + + r.extraFieldLength + + r.commentLength)); + } + f(a); + }, + function () { + U(w); + }, + )); + }); + }, + close: function (e) { + (this._worker && (this._worker.terminate(), (this._worker = null)), + e && e()); + }, + _worker: null, + }; + b.zip.useWebWorkers + ? E( + "inflater", + function (e) { + ((r._worker = e), t(r)); + }, + function (e) { + U(e); + }, + ) + : t(r); + } + function z(e) { + return unescape(encodeURIComponent(e)); + } + function W(e) { + var t, + r = []; + for (t = 0; t < e.length; t++) r.push(e.charCodeAt(t)); + return r; + } + function U(p, t, s, v) { + var c = {}, + d = [], + g = 0, + y = 0; + function m(e) { + s(e || "Error while writing zip file."); + } + function U(e) { + s(e || n); + } + var r = { + add: function (r, f, n, u, l) { + var i, + o, + a, + w = this._worker; + function h(e, t) { + var r = A(16); + ((g += e || 0), + r.view.setUint32(0, 1347094280), + void 0 !== t && + (i.view.setUint32(10, t, !0), r.view.setUint32(4, t, !0)), + f && + (r.view.setUint32(8, e, !0), + i.view.setUint32(14, e, !0), + r.view.setUint32(12, f.size, !0), + i.view.setUint32(18, f.size, !0)), + p.writeUint8Array( + r.array, + function () { + ((g += 16), n()); + }, + m, + )); + } + function e() { + var e, t; + ((l = l || {}), + (r = r.trim()), + l.directory && "/" != r.charAt(r.length - 1) && (r += "/"), + c.hasOwnProperty(r)) + ? s("File already exists.") + : ((o = W(z(r))), + d.push(r), + (e = function () { + var e, t, r, n, i, o, a, s, c; + f + ? v || 0 === l.level + ? D(w, y++, f, p, 0, f.size, !0, h, u, U, m) + : ((e = w), + (t = y++), + (r = f), + (n = p), + (i = l.level), + (o = h), + (a = u), + (s = U), + (c = m), + b.zip.useWebWorkers + ? S( + e, + { + sn: t, + options: { level: i }, + codecClass: "Deflater", + crcType: "input", + }, + r, + n, + 0, + r.size, + a, + o, + s, + c, + ) + : _( + new b.zip.Deflater(), + r, + n, + 0, + r.size, + "input", + a, + o, + s, + c, + )) + : h(); + }), + (a = l.lastModDate || new Date()), + (i = A(26)), + (c[r] = { + headerArray: i.array, + directory: l.directory, + filename: o, + offset: g, + comment: W(z(l.comment || "")), + }), + i.view.setUint32(0, 335546376), + l.version && i.view.setUint8(0, l.version), + v || 0 === l.level || l.directory || i.view.setUint16(4, 2048), + i.view.setUint16( + 6, + (((a.getHours() << 6) | a.getMinutes()) << 5) | + (a.getSeconds() / 2), + !0, + ), + i.view.setUint16( + 8, + ((((a.getFullYear() - 1980) << 4) | (a.getMonth() + 1)) << 5) | + a.getDate(), + !0, + ), + i.view.setUint16(22, o.length, !0), + (t = A(30 + o.length)).view.setUint32(0, 1347093252), + t.array.set(i.array, 4), + t.array.set(o, 30), + (g += t.array.length), + p.writeUint8Array(t.array, e, m)); + } + f ? f.init(e, U) : e(); + }, + close: function (e) { + this._worker && (this._worker.terminate(), (this._worker = null)); + var t, + r, + n, + i = 0, + o = 0; + for (r = 0; r < d.length; r++) + i += 46 + (n = c[d[r]]).filename.length + n.comment.length; + for (t = A(i + 22), r = 0; r < d.length; r++) + ((n = c[d[r]]), + t.view.setUint32(o, 1347092738), + t.view.setUint16(o + 4, 5120), + t.array.set(n.headerArray, o + 6), + t.view.setUint16(o + 32, n.comment.length, !0), + n.directory && t.view.setUint8(o + 38, 16), + t.view.setUint32(o + 42, n.offset, !0), + t.array.set(n.filename, o + 46), + t.array.set(n.comment, o + 46 + n.filename.length), + (o += 46 + n.filename.length + n.comment.length)); + (t.view.setUint32(o, 1347093766), + t.view.setUint16(o + 8, d.length, !0), + t.view.setUint16(o + 10, d.length, !0), + t.view.setUint32(o + 12, i, !0), + t.view.setUint32(o + 16, g, !0), + p.writeUint8Array( + t.array, + function () { + p.getData(e); + }, + m, + )); + }, + _worker: null, + }; + b.zip.useWebWorkers + ? E( + "deflater", + function (e) { + ((r._worker = e), t(r)); + }, + function (e) { + s(e); + }, + ) + : t(r); + } + ((r.prototype.append = function (e) { + for ( + var t = 0 | this.crc, r = this.table, n = 0, i = 0 | e.length; + n < i; + n++ + ) + t = (t >>> 8) ^ r[255 & (t ^ e[n])]; + this.crc = t; + }), + (r.prototype.get = function () { + return ~this.crc; + }), + (r.prototype.table = (function () { + var e, + t, + r, + n = []; + for (e = 0; e < 256; e++) { + for (r = e, t = 0; t < 8; t++) + 1 & r ? (r = (r >>> 1) ^ 3988292384) : (r >>>= 1); + n[e] = r; + } + return n; + })()), + (l.prototype.append = function (e, t) { + return e; + }), + (l.prototype.flush = function () {}), + ((t.prototype = new e()).constructor = t), + ((i.prototype = new e()).constructor = i), + ((f.prototype = new e()).constructor = f), + (u.prototype.getData = function (e) { + e(this.data); + }), + ((h.prototype = new u()).constructor = h), + ((p.prototype = new u()).constructor = p), + ((v.prototype = new u()).constructor = v)); + var C = { + deflater: ["z-worker.js", "deflate.js"], + inflater: ["z-worker.js", "inflate.js"], + }; + function E(e, n, i) { + if (null === b.zip.workerScripts || null === b.zip.workerScriptsPath) { + var t, r, o; + if (b.zip.workerScripts) { + if (((t = b.zip.workerScripts[e]), !Array.isArray(t))) + return void i( + new Error("zip.workerScripts." + e + " is not an array!"), + ); + ((r = t), + (o = document.createElement("a")), + (t = r.map(function (e) { + return ((o.href = e), o.href); + }))); + } else (t = C[e].slice(0))[0] = (b.zip.workerScriptsPath || "") + t[0]; + var a = new Worker(t[0]); + ((a.codecTime = a.crcTime = 0), + a.postMessage({ type: "importScripts", scripts: t.slice(1) }), + a.addEventListener("message", function e(t) { + var r = t.data; + if (r.error) return (a.terminate(), void i(r.error)); + "importScripts" === r.type && + (a.removeEventListener("message", e), + a.removeEventListener("error", s), + n(a)); + }), + a.addEventListener("error", s)); + } else + i( + new Error( + "Either zip.workerScripts or zip.workerScriptsPath may be set, not both.", + ), + ); + function s(e) { + (a.terminate(), i(e)); + } + } + function F(e) { + console.error(e); + } + b.zip = { + Reader: e, + Writer: u, + BlobReader: f, + Data64URIReader: i, + TextReader: t, + BlobWriter: v, + Data64URIWriter: p, + TextWriter: h, + createReader: function (e, t, r) { + ((r = r || F), + e.init(function () { + m(e, t, r); + }, r)); + }, + createWriter: function (e, t, r, n) { + ((r = r || F), + (n = !!n), + e.init(function () { + U(e, t, r, n); + }, r)); + }, + useWebWorkers: !0, + workerScriptsPath: null, + workerScripts: null, + }; +})(this); diff --git a/frontend/src/components/common/Navigation/MainAppBar.vue b/frontend/src/components/common/Navigation/MainAppBar.vue index 215dfaa56..d54736c95 100644 --- a/frontend/src/components/common/Navigation/MainAppBar.vue +++ b/frontend/src/components/common/Navigation/MainAppBar.vue @@ -8,6 +8,7 @@ import CollectionsBtn from "@/components/common/Navigation/CollectionsBtn.vue"; import CollectionsDrawer from "@/components/common/Navigation/CollectionsDrawer.vue"; import ConsoleModeBtn from "@/components/common/Navigation/ConsoleModeBtn.vue"; import HomeBtn from "@/components/common/Navigation/HomeBtn.vue"; +import PatcherBtn from "@/components/common/Navigation/PatcherBtn.vue"; import PlatformsBtn from "@/components/common/Navigation/PlatformsBtn.vue"; import PlatformsDrawer from "@/components/common/Navigation/PlatformsDrawer.vue"; import ScanBtn from "@/components/common/Navigation/ScanBtn.vue"; @@ -107,6 +108,7 @@ function collapse() { + @@ -108,9 +109,9 @@ function collapse() { -