From 0e2cd035d0509e474b060c72f176d06fc9465b56 Mon Sep 17 00:00:00 2001 From: Nicolas Melin Date: Tue, 14 Jan 2025 18:35:57 +0100 Subject: [PATCH 01/19] Use fastboot lib from gitlab --- app/.npmrc | 1 + app/package-lock.json | 15 + app/package.json | 1 + app/src/controller/device/bootloader.class.js | 4 +- app/src/lib/fastboot/fastboot.js | 9754 ----------------- .../lib/fastboot/vendor/pako_inflate.min.js | 2 - app/src/lib/fastboot/vendor/z-worker-pako.js | 1 - 7 files changed, 19 insertions(+), 9759 deletions(-) create mode 100644 app/.npmrc delete mode 100644 app/src/lib/fastboot/fastboot.js delete mode 100644 app/src/lib/fastboot/vendor/pako_inflate.min.js delete mode 100644 app/src/lib/fastboot/vendor/z-worker-pako.js diff --git a/app/.npmrc b/app/.npmrc new file mode 100644 index 0000000..d9ecbb4 --- /dev/null +++ b/app/.npmrc @@ -0,0 +1 @@ +@e:registry=https://gitlab.e.foundation/api/v4/packages/npm/ diff --git a/app/package-lock.json b/app/package-lock.json index 1639b4a..81f39b7 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,6 +9,7 @@ "version": "0.9", "license": "GPLv3", "dependencies": { + "@e/fastboot": "1.1.4", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" }, @@ -20,6 +21,15 @@ "vite": "^6.0.5" } }, + "node_modules/@e/fastboot": { + "version": "1.1.4", + "resolved": "https://gitlab.e.foundation/api/v4/projects/1751/packages/npm/@e/fastboot/-/@e/fastboot-1.1.4.tgz", + "integrity": "sha1-AXJIHYbdpLqAfM0jmq4pTF0oamE=", + "dependencies": { + "@zip.js/zip.js": "^2.7.6", + "pako": "^2.1.0" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", @@ -1043,6 +1053,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", diff --git a/app/package.json b/app/package.json index 48272bc..45a4a65 100644 --- a/app/package.json +++ b/app/package.json @@ -18,6 +18,7 @@ "vite": "^6.0.5" }, "dependencies": { + "@e/fastboot": "1.1.4", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" } diff --git a/app/src/controller/device/bootloader.class.js b/app/src/controller/device/bootloader.class.js index 86df50c..8cc338a 100644 --- a/app/src/controller/device/bootloader.class.js +++ b/app/src/controller/device/bootloader.class.js @@ -1,5 +1,5 @@ -import * as fastboot from "../../lib/fastboot/fastboot.js"; -import { TimeoutError } from "../../lib/fastboot/fastboot.js"; +import * as fastboot from "@e/fastboot"; +import { TimeoutError } from "@e/fastboot"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; diff --git a/app/src/lib/fastboot/fastboot.js b/app/src/lib/fastboot/fastboot.js deleted file mode 100644 index cfdd1dc..0000000 --- a/app/src/lib/fastboot/fastboot.js +++ /dev/null @@ -1,9754 +0,0 @@ -const ZIP_ENTRY_HEADER_BEGIN_LENGTH = 30; // bytes -var DebugLevel; -(function (DebugLevel) { - DebugLevel[DebugLevel["Silent"] = 0] = "Silent"; - DebugLevel[DebugLevel["Debug"] = 1] = "Debug"; - DebugLevel[DebugLevel["Verbose"] = 2] = "Verbose"; -})(DebugLevel || (DebugLevel = {})); -let debugLevel = DebugLevel.Silent; -function logDebug(...data) { - if (debugLevel >= 1) { - console.log(...data); - } -} -function logVerbose(...data) { - if (debugLevel >= 2) { - console.log(...data); - } -} -/** - * Change the debug level for the fastboot client: - * - 0 = silent - * - 1 = debug, recommended for general use - * - 2 = verbose, for debugging only - * - * @param {number} level - Debug level to use. - */ -function setDebugLevel(level) { - debugLevel = level; -} -/** - * Reads all of the data in the given blob and returns it as an ArrayBuffer. - * - * @param {Blob} blob - Blob with the data to read. - * @returns {Promise} ArrayBuffer containing data from the blob. - * @ignore - */ -function readBlobAsBuffer(blob) { - return new Promise((resolve, reject) => { - let reader = new FileReader(); - reader.onload = () => { - resolve(reader.result); - }; - reader.onerror = () => { - reject(reader.error); - }; - reader.readAsArrayBuffer(blob); - }); -} -function waitForFrame() { - return new Promise((resolve, _reject) => { - window.requestAnimationFrame(resolve); - }); -} -async function runWithTimedProgress(onProgress, action, item, duration, workPromise) { - let startTime = new Date().getTime(); - let stop = false; - onProgress(action, item, 0.0); - let progressPromise = (async () => { - let now; - let targetTime = startTime + duration; - do { - now = new Date().getTime(); - onProgress(action, item, (now - startTime) / duration); - await waitForFrame(); - } while (!stop && now < targetTime); - })(); - await Promise.race([progressPromise, workPromise]); - stop = true; - await progressPromise; - await workPromise; - onProgress(action, item, 1.0); -} -/** Exception class for operations that exceeded their timeout duration. */ -class TimeoutError extends Error { - constructor(timeout) { - super(`Timeout of ${timeout} ms exceeded`); - this.name = "TimeoutError"; - this.timeout = timeout; - } -} -function runWithTimeout(promise, timeout) { - return new Promise((resolve, reject) => { - // Set up timeout - let timedOut = false; - let tid = setTimeout(() => { - // Set sentinel first to prevent race in promise resolving - timedOut = true; - reject(new TimeoutError(timeout)); - }, timeout); - // Passthrough - promise - .then((val) => { - if (!timedOut) { - resolve(val); - } - }) - .catch((err) => { - if (!timedOut) { - reject(err); - } - }) - .finally(() => { - if (!timedOut) { - clearTimeout(tid); - } - }); - }); -} -async function getEntryMetadata(blob, entry) { - const offset = entry.offset; - const headerBeginRaw = await blob.slice(offset, offset + ZIP_ENTRY_HEADER_BEGIN_LENGTH).arrayBuffer(); - const dataView = new DataView(headerBeginRaw); - const compressionMethod = dataView.getUint16(8, true); - const compressedSize = dataView.getUint32(18, true); - const uncompressedSize = dataView.getUint32(22, true); - const fileNameLength = dataView.getUint16(26, true); - const extraFieldLength = dataView.getUint16(28, true); - const headerSize = ZIP_ENTRY_HEADER_BEGIN_LENGTH + fileNameLength + extraFieldLength; - return { - offset, - compressionMethod, - compressedSize, - uncompressedSize, - headerSize, - }; -} -// Wrapper for Entry#getData() that unwraps ProgressEvent errors -async function zipGetData(entry, writer, options) { - try { - return await entry.getData(writer, options); - } - catch (e) { - if (e instanceof ProgressEvent && - e.type === "error" && - e.target !== null) { - throw e.target.error; - } - else { - throw e; - } - } -} - -const FILE_MAGIC = 0xed26ff3a; -const MAJOR_VERSION = 1; -const MINOR_VERSION = 0; -const FILE_HEADER_SIZE = 28; -const CHUNK_HEADER_SIZE = 12; -// AOSP libsparse uses 64 MiB chunks -const RAW_CHUNK_SIZE = 64 * 1024 * 1024; -class ImageError extends Error { - constructor(message) { - super(message); - this.name = "ImageError"; - } -} -var ChunkType; -(function (ChunkType) { - ChunkType[ChunkType["Raw"] = 51905] = "Raw"; - ChunkType[ChunkType["Fill"] = 51906] = "Fill"; - ChunkType[ChunkType["Skip"] = 51907] = "Skip"; - ChunkType[ChunkType["Crc32"] = 51908] = "Crc32"; -})(ChunkType || (ChunkType = {})); -class BlobBuilder { - constructor(type = "") { - this.type = type; - this.blob = new Blob([], { type: this.type }); - } - append(blob) { - this.blob = new Blob([this.blob, blob], { type: this.type }); - } - getBlob() { - return this.blob; - } -} -/** - * Returns a parsed version of the sparse image file header from the given buffer. - * - * @param {ArrayBuffer} buffer - Raw file header data. - * @returns {SparseHeader} Object containing the header information. - */ -function parseFileHeader(buffer) { - let view = new DataView(buffer); - let magic = view.getUint32(0, true); - if (magic !== FILE_MAGIC) { - return null; - } - // v1.0+ - let major = view.getUint16(4, true); - let minor = view.getUint16(6, true); - if (major !== MAJOR_VERSION || minor < MINOR_VERSION) { - throw new ImageError(`Unsupported sparse image version ${major}.${minor}`); - } - let fileHdrSize = view.getUint16(8, true); - let chunkHdrSize = view.getUint16(10, true); - if (fileHdrSize !== FILE_HEADER_SIZE || - chunkHdrSize !== CHUNK_HEADER_SIZE) { - throw new ImageError(`Invalid file header size ${fileHdrSize}, chunk header size ${chunkHdrSize}`); - } - let blockSize = view.getUint32(12, true); - if (blockSize % 4 !== 0) { - throw new ImageError(`Block size ${blockSize} is not a multiple of 4`); - } - return { - blockSize: blockSize, - blocks: view.getUint32(16, true), - chunks: view.getUint32(20, true), - crc32: view.getUint32(24, true), - }; -} -function parseChunkHeader(buffer) { - let view = new DataView(buffer); - // This isn't the same as what createImage takes. - // Further processing needs to be done on the chunks. - return { - type: view.getUint16(0, true), - /* 2: reserved, 16 bits */ - blocks: view.getUint32(4, true), - dataBytes: view.getUint32(8, true) - CHUNK_HEADER_SIZE, - data: null, // to be populated by consumer - }; -} -function calcChunksBlockSize(chunks) { - return chunks - .map((chunk) => chunk.blocks) - .reduce((total, c) => total + c, 0); -} -function calcChunksDataSize(chunks) { - return chunks - .map((chunk) => chunk.data.size) - .reduce((total, c) => total + c, 0); -} -function calcChunksSize(chunks) { - // 28-byte file header, 12-byte chunk headers - let overhead = FILE_HEADER_SIZE + CHUNK_HEADER_SIZE * chunks.length; - return overhead + calcChunksDataSize(chunks); -} -async function createImage(header, chunks) { - let blobBuilder = new BlobBuilder(); - let buffer = new ArrayBuffer(FILE_HEADER_SIZE); - let dataView = new DataView(buffer); - let arrayView = new Uint8Array(buffer); - dataView.setUint32(0, FILE_MAGIC, true); - // v1.0 - dataView.setUint16(4, MAJOR_VERSION, true); - dataView.setUint16(6, MINOR_VERSION, true); - dataView.setUint16(8, FILE_HEADER_SIZE, true); - dataView.setUint16(10, CHUNK_HEADER_SIZE, true); - // Match input parameters - dataView.setUint32(12, header.blockSize, true); - dataView.setUint32(16, header.blocks, true); - dataView.setUint32(20, chunks.length, true); - // We don't care about the CRC. AOSP docs specify that this should be a CRC32, - // but AOSP libsparse always sets 0 and puts the CRC in a final undocumented - // 0xCAC4 chunk instead. - dataView.setUint32(24, 0, true); - blobBuilder.append(new Blob([buffer])); - for (let chunk of chunks) { - buffer = new ArrayBuffer(CHUNK_HEADER_SIZE + chunk.data.size); - dataView = new DataView(buffer); - arrayView = new Uint8Array(buffer); - dataView.setUint16(0, chunk.type, true); - dataView.setUint16(2, 0, true); // reserved - dataView.setUint32(4, chunk.blocks, true); - dataView.setUint32(8, CHUNK_HEADER_SIZE + chunk.data.size, true); - let chunkArrayView = new Uint8Array(await readBlobAsBuffer(chunk.data)); - arrayView.set(chunkArrayView, CHUNK_HEADER_SIZE); - blobBuilder.append(new Blob([buffer])); - } - return blobBuilder.getBlob(); -} -/** - * Creates a sparse image from buffer containing raw image data. - * - * @param {Blob} blob - Blob containing the raw image data. - * @returns {Promise} Promise that resolves the blob containing the new sparse image. - */ -async function fromRaw(blob) { - let header = { - blockSize: 4096, - blocks: blob.size / 4096, - chunks: 1, - crc32: 0, - }; - let chunks = []; - while (blob.size > 0) { - let chunkSize = Math.min(blob.size, RAW_CHUNK_SIZE); - chunks.push({ - type: ChunkType.Raw, - blocks: chunkSize / header.blockSize, - data: blob.slice(0, chunkSize), - }); - blob = blob.slice(chunkSize); - } - return createImage(header, chunks); -} -/** - * Split a sparse image into smaller sparse images within the given size. - * This takes a Blob instead of an ArrayBuffer because it may process images - * larger than RAM. - * - * @param {Blob} blob - Blob containing the sparse image to split. - * @param {number} splitSize - Maximum size per split. - * @yields {Object} Data of the next split image and its output size in bytes. - */ -async function* splitBlob(blob, splitSize) { - logDebug(`Splitting ${blob.size}-byte sparse image into ${splitSize}-byte chunks`); - // Short-circuit if splitting isn't required - if (blob.size <= splitSize) { - logDebug("Blob fits in 1 payload, not splitting"); - yield { - data: await readBlobAsBuffer(blob), - bytes: blob.size, - }; - return; - } - let headerData = await readBlobAsBuffer(blob.slice(0, FILE_HEADER_SIZE)); - let header = parseFileHeader(headerData); - if (header === null) { - throw new ImageError("Blob is not a sparse image"); - } - // Remove CRC32 (if present), otherwise splitting will invalidate it - header.crc32 = 0; - blob = blob.slice(FILE_HEADER_SIZE); - let splitChunks = []; - let splitDataBytes = 0; - for (let i = 0; i < header.chunks; i++) { - let chunkHeaderData = await readBlobAsBuffer(blob.slice(0, CHUNK_HEADER_SIZE)); - let chunk = parseChunkHeader(chunkHeaderData); - chunk.data = blob.slice(CHUNK_HEADER_SIZE, CHUNK_HEADER_SIZE + chunk.dataBytes); - blob = blob.slice(CHUNK_HEADER_SIZE + chunk.dataBytes); - let bytesRemaining = splitSize - calcChunksSize(splitChunks); - logVerbose(` Chunk ${i}: type ${chunk.type}, ${chunk.dataBytes} bytes / ${chunk.blocks} blocks, ${bytesRemaining} bytes remaining`); - if (bytesRemaining >= chunk.dataBytes) { - // Read the chunk and add it - logVerbose(" Space is available, adding chunk"); - splitChunks.push(chunk); - // Track amount of data written on the output device, in bytes - splitDataBytes += chunk.blocks * header.blockSize; - } - else { - // Out of space, finish this split - // Blocks need to be calculated from chunk headers instead of going by size - // because FILL and SKIP chunks cover more blocks than the data they contain. - let splitBlocks = calcChunksBlockSize(splitChunks); - splitChunks.push({ - type: ChunkType.Skip, - blocks: header.blocks - splitBlocks, - data: new Blob([]), - dataBytes: 0, - }); - logVerbose(`Partition is ${header.blocks} blocks, used ${splitBlocks}, padded with ${header.blocks - splitBlocks}, finishing split with ${calcChunksBlockSize(splitChunks)} blocks`); - let splitImage = await createImage(header, splitChunks); - logDebug(`Finished ${splitImage.size}-byte split with ${splitChunks.length} chunks`); - yield { - data: await readBlobAsBuffer(splitImage), - bytes: splitDataBytes, - }; - // Start a new split. Every split is considered a full image by the - // bootloader, so we need to skip the *total* written blocks. - logVerbose(`Starting new split: skipping first ${splitBlocks} blocks and adding chunk`); - splitChunks = [ - { - type: ChunkType.Skip, - blocks: splitBlocks, - data: new Blob([]), - dataBytes: 0, - }, - chunk, - ]; - splitDataBytes = 0; - } - } - // Finish the final split if necessary - if (splitChunks.length > 0 && - (splitChunks.length > 1 || splitChunks[0].type !== ChunkType.Skip)) { - let splitImage = await createImage(header, splitChunks); - logDebug(`Finishing final ${splitImage.size}-byte split with ${splitChunks.length} chunks`); - yield { - data: await readBlobAsBuffer(splitImage), - bytes: splitDataBytes, - }; - } -} - -/* - Copyright (c) 2022 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. - */ - -// deno-lint-ignore-file no-this-alias prefer-const - -// Global - -const MAX_BITS$1 = 15; -const D_CODES = 30; -const BL_CODES = 19; - -const LENGTH_CODES = 29; -const LITERALS = 256; -const L_CODES = (LITERALS + 1 + LENGTH_CODES); -const HEAP_SIZE = (2 * L_CODES + 1); - -const END_BLOCK = 256; - -// Bit length codes must not exceed MAX_BL_BITS bits -const MAX_BL_BITS = 7; - -// repeat previous bit length 3-6 times (2 bits of repeat count) -const REP_3_6 = 16; - -// repeat a zero length 3-10 times (3 bits of repeat count) -const REPZ_3_10 = 17; - -// repeat a zero length 11-138 times (7 bits of repeat count) -const REPZ_11_138 = 18; - -// The lengths of the bit length codes are sent in order of decreasing -// probability, to avoid transmitting the lengths for unused bit -// length codes. - -const Buf_size = 8 * 2; - -// JZlib version : "1.0.2" -const Z_DEFAULT_COMPRESSION = -1; - -// compression strategy -const Z_FILTERED = 1; -const Z_HUFFMAN_ONLY = 2; -const Z_DEFAULT_STRATEGY = 0; - -const Z_NO_FLUSH$1 = 0; -const Z_PARTIAL_FLUSH = 1; -const Z_FULL_FLUSH = 3; -const Z_FINISH$1 = 4; - -const Z_OK$1 = 0; -const Z_STREAM_END$1 = 1; -const Z_NEED_DICT$1 = 2; -const Z_STREAM_ERROR$1 = -2; -const Z_DATA_ERROR$1 = -3; -const Z_BUF_ERROR$1 = -5; - -// Tree - -function extractArray(array) { - return flatArray(array.map(([length, value]) => (new Array(length)).fill(value, 0, length))); -} - -function flatArray(array) { - return array.reduce((a, b) => a.concat(Array.isArray(b) ? flatArray(b) : b), []); -} - -// see definition of array dist_code below -const _dist_code = [0, 1, 2, 3].concat(...extractArray([ - [2, 4], [2, 5], [4, 6], [4, 7], [8, 8], [8, 9], [16, 10], [16, 11], [32, 12], [32, 13], [64, 14], [64, 15], [2, 0], [1, 16], - [1, 17], [2, 18], [2, 19], [4, 20], [4, 21], [8, 22], [8, 23], [16, 24], [16, 25], [32, 26], [32, 27], [64, 28], [64, 29] -])); - -function Tree() { - const that = this; - - // dyn_tree; // the dynamic tree - // max_code; // largest code with non zero frequency - // stat_desc; // the corresponding static tree - - // Compute the optimal bit lengths for a tree and update the total bit - // length - // for the current block. - // IN assertion: the fields freq and dad are set, heap[heap_max] and - // above are the tree nodes sorted by increasing frequency. - // OUT assertions: the field len is set to the optimal bit length, the - // array bl_count contains the frequencies for each bit length. - // The length opt_len is updated; static_len is also updated if stree is - // not null. - function gen_bitlen(s) { - const tree = that.dyn_tree; - const stree = that.stat_desc.static_tree; - const extra = that.stat_desc.extra_bits; - const base = that.stat_desc.extra_base; - const max_length = that.stat_desc.max_length; - let h; // heap index - let n, m; // iterate over the tree elements - let bits; // bit length - let xbits; // extra bits - let f; // frequency - let overflow = 0; // number of elements with bit length too large - - for (bits = 0; bits <= MAX_BITS$1; bits++) - s.bl_count[bits] = 0; - - // In a first pass, compute the optimal bit lengths (which may - // overflow in the case of the bit length tree). - tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap - - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; - if (bits > max_length) { - bits = max_length; - overflow++; - } - tree[n * 2 + 1] = bits; - // We overwrite tree[n*2+1] which is no longer needed - - if (n > that.max_code) - continue; // not a leaf node - - s.bl_count[bits]++; - xbits = 0; - if (n >= base) - xbits = extra[n - base]; - f = tree[n * 2]; - s.opt_len += f * (bits + xbits); - if (stree) - s.static_len += f * (stree[n * 2 + 1] + xbits); - } - if (overflow === 0) - return; - - // This happens for example on obj2 and pic of the Calgary corpus - // Find the first bit length which could increase: - do { - bits = max_length - 1; - while (s.bl_count[bits] === 0) - bits--; - s.bl_count[bits]--; // move one leaf down the tree - s.bl_count[bits + 1] += 2; // move one overflow item as its brother - s.bl_count[max_length]--; - // The brother of the overflow item also moves one step up, - // but this does not affect bl_count[max_length] - overflow -= 2; - } while (overflow > 0); - - for (bits = max_length; bits !== 0; bits--) { - n = s.bl_count[bits]; - while (n !== 0) { - m = s.heap[--h]; - if (m > that.max_code) - continue; - if (tree[m * 2 + 1] != bits) { - s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2]; - tree[m * 2 + 1] = bits; - } - n--; - } - } - } - - // Reverse the first len bits of a code, using straightforward code (a - // faster - // method would use a table) - // IN assertion: 1 <= len <= 15 - function bi_reverse(code, // the value to invert - len // its bit length - ) { - let res = 0; - do { - res |= code & 1; - code >>>= 1; - res <<= 1; - } while (--len > 0); - return res >>> 1; - } - - // Generate the codes for a given tree and bit counts (which need not be - // optimal). - // IN assertion: the array bl_count contains the bit length statistics for - // the given tree and the field len is set for all tree elements. - // OUT assertion: the field code is set for all tree elements of non - // zero code length. - function gen_codes(tree, // the tree to decorate - max_code, // largest code with non zero frequency - bl_count // number of codes at each bit length - ) { - const next_code = []; // next code value for each - // bit length - let code = 0; // running code value - let bits; // bit index - let n; // code index - let len; - - // The distribution counts are first used to generate the code values - // without bit reversal. - for (bits = 1; bits <= MAX_BITS$1; bits++) { - next_code[bits] = code = ((code + bl_count[bits - 1]) << 1); - } - - // Check that the bit counts in bl_count are consistent. The last code - // must be all ones. - // Assert (code + bl_count[MAX_BITS]-1 == (1<= 1; n--) - s.pqdownheap(tree, n); - - // Construct the Huffman tree by repeatedly combining the least two - // frequent nodes. - - node = elems; // next internal node of the tree - do { - // n = node of least frequency - n = s.heap[1]; - s.heap[1] = s.heap[s.heap_len--]; - s.pqdownheap(tree, 1); - m = s.heap[1]; // m = node of next least frequency - - s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency - s.heap[--s.heap_max] = m; - - // Create a new node father of n and m - tree[node * 2] = (tree[n * 2] + tree[m * 2]); - s.depth[node] = Math.max(s.depth[n], s.depth[m]) + 1; - tree[n * 2 + 1] = tree[m * 2 + 1] = node; - - // and insert the new node in the heap - s.heap[1] = node++; - s.pqdownheap(tree, 1); - } while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1]; - - // At this point, the fields freq and dad are set. We can now - // generate the bit lengths. - - gen_bitlen(s); - - // The field len is now set, we can generate the bit codes - gen_codes(tree, that.max_code, s.bl_count); - }; - -} - -Tree._length_code = [0, 1, 2, 3, 4, 5, 6, 7].concat(...extractArray([ - [2, 8], [2, 9], [2, 10], [2, 11], [4, 12], [4, 13], [4, 14], [4, 15], [8, 16], [8, 17], [8, 18], [8, 19], - [16, 20], [16, 21], [16, 22], [16, 23], [32, 24], [32, 25], [32, 26], [31, 27], [1, 28]])); - -Tree.base_length = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0]; - -Tree.base_dist = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, - 24576]; - -// Mapping from a distance to a distance code. dist is the distance - 1 and -// must not have side effects. _dist_code[256] and _dist_code[257] are never -// used. -Tree.d_code = function (dist) { - return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >>> 7)]); -}; - -// extra bits for each length code -Tree.extra_lbits = [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]; - -// extra bits for each distance code -Tree.extra_dbits = [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]; - -// extra bits for each bit length code -Tree.extra_blbits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]; - -Tree.bl_order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - -// StaticTree - -function StaticTree(static_tree, extra_bits, extra_base, elems, max_length) { - const that = this; - that.static_tree = static_tree; - that.extra_bits = extra_bits; - that.extra_base = extra_base; - that.elems = elems; - that.max_length = max_length; -} - -const static_ltree2_first_part = [12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, - 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, - 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, - 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, - 213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 19, 275, 147, 403, 83, 339, 211, 467, 51, 307, - 179, 435, 115, 371, 243, 499, 11, 267, 139, 395, 75, 331, 203, 459, 43, 299, 171, 427, 107, 363, 235, 491, 27, 283, 155, 411, 91, 347, 219, 475, - 59, 315, 187, 443, 123, 379, 251, 507, 7, 263, 135, 391, 71, 327, 199, 455, 39, 295, 167, 423, 103, 359, 231, 487, 23, 279, 151, 407, 87, 343, 215, - 471, 55, 311, 183, 439, 119, 375, 247, 503, 15, 271, 143, 399, 79, 335, 207, 463, 47, 303, 175, 431, 111, 367, 239, 495, 31, 287, 159, 415, 95, - 351, 223, 479, 63, 319, 191, 447, 127, 383, 255, 511, 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36, 100, 20, 84, 52, - 116, 3, 131, 67, 195, 35, 163, 99, 227]; -const static_ltree2_second_part = extractArray([[144, 8], [112, 9], [24, 7], [8, 8]]); -StaticTree.static_ltree = flatArray(static_ltree2_first_part.map((value, index) => [value, static_ltree2_second_part[index]])); - -const static_dtree_first_part = [0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23]; -const static_dtree_second_part = extractArray([[30, 5]]); -StaticTree.static_dtree = flatArray(static_dtree_first_part.map((value, index) => [value, static_dtree_second_part[index]])); - -StaticTree.static_l_desc = new StaticTree(StaticTree.static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS$1); - -StaticTree.static_d_desc = new StaticTree(StaticTree.static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS$1); - -StaticTree.static_bl_desc = new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS); - -// Deflate - -const MAX_MEM_LEVEL = 9; -const DEF_MEM_LEVEL = 8; - -function Config(good_length, max_lazy, nice_length, max_chain, func) { - const that = this; - that.good_length = good_length; - that.max_lazy = max_lazy; - that.nice_length = nice_length; - that.max_chain = max_chain; - that.func = func; -} - -const STORED$1 = 0; -const FAST = 1; -const SLOW = 2; -const config_table = [ - new Config(0, 0, 0, 0, STORED$1), - new Config(4, 4, 8, 4, FAST), - new Config(4, 5, 16, 8, FAST), - new Config(4, 6, 32, 32, FAST), - new Config(4, 4, 16, 16, SLOW), - new Config(8, 16, 32, 32, SLOW), - new Config(8, 16, 128, 128, SLOW), - new Config(8, 32, 128, 256, SLOW), - new Config(32, 128, 258, 1024, SLOW), - new Config(32, 258, 258, 4096, SLOW) -]; - -const z_errmsg = ["need dictionary", // Z_NEED_DICT - // 2 - "stream end", // Z_STREAM_END 1 - "", // Z_OK 0 - "", // Z_ERRNO (-1) - "stream error", // Z_STREAM_ERROR (-2) - "data error", // Z_DATA_ERROR (-3) - "", // Z_MEM_ERROR (-4) - "buffer error", // Z_BUF_ERROR (-5) - "",// Z_VERSION_ERROR (-6) - ""]; - -// block not completed, need more input or more output -const NeedMore = 0; - -// block flush performed -const BlockDone = 1; - -// finish started, need only more output at next deflate -const FinishStarted = 2; - -// finish done, accept no more input or output -const FinishDone = 3; - -// preset dictionary flag in zlib header -const PRESET_DICT$1 = 0x20; - -const INIT_STATE = 42; -const BUSY_STATE = 113; -const FINISH_STATE = 666; - -// The deflate compression method -const Z_DEFLATED$1 = 8; - -const STORED_BLOCK = 0; -const STATIC_TREES = 1; -const DYN_TREES = 2; - -const MIN_MATCH = 3; -const MAX_MATCH = 258; -const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - -function smaller(tree, n, m, depth) { - const tn2 = tree[n * 2]; - const tm2 = tree[m * 2]; - return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); -} - -function Deflate() { - - const that = this; - let strm; // pointer back to this zlib stream - let status; // as the name implies - // pending_buf; // output still pending - let pending_buf_size; // size of pending_buf - // pending_out; // next pending byte to output to the stream - // pending; // nb of bytes in the pending buffer - - // dist_buf; // buffer for distances - // lc_buf; // buffer for literals or lengths - // To simplify the code, dist_buf and lc_buf have the same number of elements. - // To use different lengths, an extra flag array would be necessary. - - let last_flush; // value of flush param for previous deflate call - - let w_size; // LZ77 win size (32K by default) - let w_bits; // log2(w_size) (8..16) - let w_mask; // w_size - 1 - - let win; - // Sliding win. Input bytes are read into the second half of the win, - // and move to the first half later to keep a dictionary of at least wSize - // bytes. With this organization, matches are limited to a distance of - // wSize-MAX_MATCH bytes, but this ensures that IO is always - // performed with a length multiple of the block size. Also, it limits - // the win size to 64K, which is quite useful on MSDOS. - // To do: use the user input buffer as sliding win. - - let window_size; - // Actual size of win: 2*wSize, except when the user input buffer - // is directly used as sliding win. - - let prev; - // Link to older string with same hash index. To limit the size of this - // array to 64K, this link is maintained only for the last 32K strings. - // An index in this array is thus a win index modulo 32K. - - let head; // Heads of the hash chains or NIL. - - let ins_h; // hash index of string to be inserted - let hash_size; // number of elements in hash table - let hash_bits; // log2(hash_size) - let hash_mask; // hash_size-1 - - // Number of bits by which ins_h must be shifted at each input - // step. It must be such that after MIN_MATCH steps, the oldest - // byte no longer takes part in the hash key, that is: - // hash_shift * MIN_MATCH >= hash_bits - let hash_shift; - - // Window position at the beginning of the current output block. Gets - // negative when the win is moved backwards. - - let block_start; - - let match_length; // length of best match - let prev_match; // previous match - let match_available; // set if previous match exists - let strstart; // start of string to insert - let match_start; // start of matching string - let lookahead; // number of valid bytes ahead in win - - // Length of the best match at previous step. Matches not greater than this - // are discarded. This is used in the lazy match evaluation. - let prev_length; - - // To speed up deflation, hash chains are never searched beyond this - // length. A higher limit improves compression ratio but degrades the speed. - let max_chain_length; - - // Attempt to find a better match only when the current match is strictly - // smaller than this value. This mechanism is used only for compression - // levels >= 4. - let max_lazy_match; - - // Insert new strings in the hash table only if the match length is not - // greater than this length. This saves time but degrades compression. - // max_insert_length is used only for compression levels <= 3. - - let level; // compression level (1..9) - let strategy; // favor or force Huffman coding - - // Use a faster search when the previous match is longer than this - let good_match; - - // Stop searching when current match exceeds this - let nice_match; - - let dyn_ltree; // literal and length tree - let dyn_dtree; // distance tree - let bl_tree; // Huffman tree for bit lengths - - const l_desc = new Tree(); // desc for literal tree - const d_desc = new Tree(); // desc for distance tree - const bl_desc = new Tree(); // desc for bit length tree - - // that.heap_len; // number of elements in the heap - // that.heap_max; // element of largest frequency - // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - // The same heap array is used to build all trees. - - // Depth of each subtree used as tie breaker for trees of equal frequency - that.depth = []; - - // Size of match buffer for literals/lengths. There are 4 reasons for - // limiting lit_bufsize to 64K: - // - frequencies can be kept in 16 bit counters - // - if compression is not successful for the first block, all input - // data is still in the win so we can still emit a stored block even - // when input comes from standard input. (This can also be done for - // all blocks if lit_bufsize is not greater than 32K.) - // - if compression is not successful for a file smaller than 64K, we can - // even emit a stored file instead of a stored block (saving 5 bytes). - // This is applicable only for zip (not gzip or zlib). - // - creating new Huffman trees less frequently may not provide fast - // adaptation to changes in the input data statistics. (Take for - // example a binary file with poorly compressible code followed by - // a highly compressible string table.) Smaller buffer sizes give - // fast adaptation but have of course the overhead of transmitting - // trees more frequently. - // - I can't count above 4 - let lit_bufsize; - - let last_lit; // running index in dist_buf and lc_buf - - // that.opt_len; // bit length of current block with optimal trees - // that.static_len; // bit length of current block with static trees - let matches; // number of string matches in current block - let last_eob_len; // bit length of EOB code for last block - - // Output buffer. bits are inserted starting at the bottom (least - // significant bits). - let bi_buf; - - // Number of valid bits in bi_buf. All bits above the last valid bit - // are always zero. - let bi_valid; - - // number of codes at each bit length for an optimal tree - that.bl_count = []; - - // heap used to build the Huffman trees - that.heap = []; - - dyn_ltree = []; - dyn_dtree = []; - bl_tree = []; - - function lm_init() { - window_size = 2 * w_size; - - head[hash_size - 1] = 0; - for (let i = 0; i < hash_size - 1; i++) { - head[i] = 0; - } - - // Set the default configuration parameters: - max_lazy_match = config_table[level].max_lazy; - good_match = config_table[level].good_length; - nice_match = config_table[level].nice_length; - max_chain_length = config_table[level].max_chain; - - strstart = 0; - block_start = 0; - lookahead = 0; - match_length = prev_length = MIN_MATCH - 1; - match_available = 0; - ins_h = 0; - } - - function init_block() { - let i; - // Initialize the trees. - for (i = 0; i < L_CODES; i++) - dyn_ltree[i * 2] = 0; - for (i = 0; i < D_CODES; i++) - dyn_dtree[i * 2] = 0; - for (i = 0; i < BL_CODES; i++) - bl_tree[i * 2] = 0; - - dyn_ltree[END_BLOCK * 2] = 1; - that.opt_len = that.static_len = 0; - last_lit = matches = 0; - } - - // Initialize the tree data structures for a new zlib stream. - function tr_init() { - - l_desc.dyn_tree = dyn_ltree; - l_desc.stat_desc = StaticTree.static_l_desc; - - d_desc.dyn_tree = dyn_dtree; - d_desc.stat_desc = StaticTree.static_d_desc; - - bl_desc.dyn_tree = bl_tree; - bl_desc.stat_desc = StaticTree.static_bl_desc; - - bi_buf = 0; - bi_valid = 0; - last_eob_len = 8; // enough lookahead for inflate - - // Initialize the first block of the first file: - init_block(); - } - - // Restore the heap property by moving down the tree starting at node k, - // exchanging a node with the smallest of its two sons if necessary, - // stopping - // when the heap property is re-established (each father smaller than its - // two sons). - that.pqdownheap = function (tree, // the tree to restore - k // node to move down - ) { - const heap = that.heap; - const v = heap[k]; - let j = k << 1; // left son of k - while (j <= that.heap_len) { - // Set j to the smallest of the two sons: - if (j < that.heap_len && smaller(tree, heap[j + 1], heap[j], that.depth)) { - j++; - } - // Exit if v is smaller than both sons - if (smaller(tree, v, heap[j], that.depth)) - break; - - // Exchange v with the smallest son - heap[k] = heap[j]; - k = j; - // And continue down the tree, setting j to the left son of k - j <<= 1; - } - heap[k] = v; - }; - - // Scan a literal or distance tree to determine the frequencies of the codes - // in the bit length tree. - function scan_tree(tree,// the tree to be scanned - max_code // and its largest code of non zero frequency - ) { - let prevlen = -1; // last emitted length - let curlen; // length of current code - let nextlen = tree[0 * 2 + 1]; // length of next code - let count = 0; // repeat count of the current code - let max_count = 7; // max repeat count - let min_count = 4; // min repeat count - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - tree[(max_code + 1) * 2 + 1] = 0xffff; // guard - - for (let n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - bl_tree[curlen * 2] += count; - } else if (curlen !== 0) { - if (curlen != prevlen) - bl_tree[curlen * 2]++; - bl_tree[REP_3_6 * 2]++; - } else if (count <= 10) { - bl_tree[REPZ_3_10 * 2]++; - } else { - bl_tree[REPZ_11_138 * 2]++; - } - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } else if (curlen == nextlen) { - max_count = 6; - min_count = 3; - } else { - max_count = 7; - min_count = 4; - } - } - } - - // Construct the Huffman tree for the bit lengths and return the index in - // bl_order of the last bit length code to send. - function build_bl_tree() { - let max_blindex; // index of last bit length code of non zero freq - - // Determine the bit length frequencies for literal and distance trees - scan_tree(dyn_ltree, l_desc.max_code); - scan_tree(dyn_dtree, d_desc.max_code); - - // Build the bit length tree: - bl_desc.build_tree(that); - // opt_len now includes the length of the tree representations, except - // the lengths of the bit lengths codes and the 5+5+4 bits for the - // counts. - - // Determine the number of bit length codes to send. The pkzip format - // requires that at least 4 bit length codes be sent. (appnote.txt says - // 3 but the actual value used is 4.) - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { - if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] !== 0) - break; - } - // Update opt_len to include the bit length tree and counts - that.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - - return max_blindex; - } - - // Output a byte on the stream. - // IN assertion: there is enough room in pending_buf. - function put_byte(p) { - that.pending_buf[that.pending++] = p; - } - - function put_short(w) { - put_byte(w & 0xff); - put_byte((w >>> 8) & 0xff); - } - - function putShortMSB(b) { - put_byte((b >> 8) & 0xff); - put_byte((b & 0xff) & 0xff); - } - - function send_bits(value, length) { - let val; - const len = length; - if (bi_valid > Buf_size - len) { - val = value; - // bi_buf |= (val << bi_valid); - bi_buf |= ((val << bi_valid) & 0xffff); - put_short(bi_buf); - bi_buf = val >>> (Buf_size - bi_valid); - bi_valid += len - Buf_size; - } else { - // bi_buf |= (value) << bi_valid; - bi_buf |= (((value) << bi_valid) & 0xffff); - bi_valid += len; - } - } - - function send_code(c, tree) { - const c2 = c * 2; - send_bits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff); - } - - // Send a literal or distance tree in compressed form, using the codes in - // bl_tree. - function send_tree(tree,// the tree to be sent - max_code // and its largest code of non zero frequency - ) { - let n; // iterates over all tree elements - let prevlen = -1; // last emitted length - let curlen; // length of current code - let nextlen = tree[0 * 2 + 1]; // length of next code - let count = 0; // repeat count of the current code - let max_count = 7; // max repeat count - let min_count = 4; // min repeat count - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - do { - send_code(curlen, bl_tree); - } while (--count !== 0); - } else if (curlen !== 0) { - if (curlen != prevlen) { - send_code(curlen, bl_tree); - count--; - } - send_code(REP_3_6, bl_tree); - send_bits(count - 3, 2); - } else if (count <= 10) { - send_code(REPZ_3_10, bl_tree); - send_bits(count - 3, 3); - } else { - send_code(REPZ_11_138, bl_tree); - send_bits(count - 11, 7); - } - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } else if (curlen == nextlen) { - max_count = 6; - min_count = 3; - } else { - max_count = 7; - min_count = 4; - } - } - } - - // Send the header for a block using dynamic Huffman trees: the counts, the - // lengths of the bit length codes, the literal tree and the distance tree. - // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - function send_all_trees(lcodes, dcodes, blcodes) { - let rank; // index in bl_order - - send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt - send_bits(dcodes - 1, 5); - send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt - for (rank = 0; rank < blcodes; rank++) { - send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); - } - send_tree(dyn_ltree, lcodes - 1); // literal tree - send_tree(dyn_dtree, dcodes - 1); // distance tree - } - - // Flush the bit buffer, keeping at most 7 bits in it. - function bi_flush() { - if (bi_valid == 16) { - put_short(bi_buf); - bi_buf = 0; - bi_valid = 0; - } else if (bi_valid >= 8) { - put_byte(bi_buf & 0xff); - bi_buf >>>= 8; - bi_valid -= 8; - } - } - - // Send one empty static block to give enough lookahead for inflate. - // This takes 10 bits, of which 7 may remain in the bit buffer. - // The current inflate code requires 9 bits of lookahead. If the - // last two codes for the previous block (real code plus EOB) were coded - // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - // the last real code. In this case we send two empty static blocks instead - // of one. (There are no problems if the previous block is stored or fixed.) - // To simplify the code, we assume the worst case of last real code encoded - // on one bit only. - function _tr_align() { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - - bi_flush(); - - // Of the 10 bits for the empty block, we have already sent - // (10 - bi_valid) bits. The lookahead for the last real code (before - // the EOB of the previous block) was thus at least one plus the length - // of the EOB plus what we have just sent of the empty static block. - if (1 + last_eob_len + 10 - bi_valid < 9) { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - bi_flush(); - } - last_eob_len = 7; - } - - // Save the match info and tally the frequency counts. Return true if - // the current block must be flushed. - function _tr_tally(dist, // distance of matched string - lc // match length-MIN_MATCH or unmatched char (if dist==0) - ) { - let out_length, in_length, dcode; - that.dist_buf[last_lit] = dist; - that.lc_buf[last_lit] = lc & 0xff; - last_lit++; - - if (dist === 0) { - // lc is the unmatched char - dyn_ltree[lc * 2]++; - } else { - matches++; - // Here, lc is the match length - MIN_MATCH - dist--; // dist = match distance - 1 - dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++; - dyn_dtree[Tree.d_code(dist) * 2]++; - } - - if ((last_lit & 0x1fff) === 0 && level > 2) { - // Compute an upper bound for the compressed length - out_length = last_lit * 8; - in_length = strstart - block_start; - for (dcode = 0; dcode < D_CODES; dcode++) { - out_length += dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]); - } - out_length >>>= 3; - if ((matches < Math.floor(last_lit / 2)) && out_length < Math.floor(in_length / 2)) - return true; - } - - return (last_lit == lit_bufsize - 1); - // We avoid equality with lit_bufsize because of wraparound at 64K - // on 16 bit machines and because stored blocks are restricted to - // 64K-1 bytes. - } - - // Send the block data compressed using the given Huffman trees - function compress_block(ltree, dtree) { - let dist; // distance of matched string - let lc; // match length or unmatched char (if dist === 0) - let lx = 0; // running index in dist_buf and lc_buf - let code; // the code to send - let extra; // number of extra bits to send - - if (last_lit !== 0) { - do { - dist = that.dist_buf[lx]; - lc = that.lc_buf[lx]; - lx++; - - if (dist === 0) { - send_code(lc, ltree); // send a literal byte - } else { - // Here, lc is the match length - MIN_MATCH - code = Tree._length_code[lc]; - - send_code(code + LITERALS + 1, ltree); // send the length - // code - extra = Tree.extra_lbits[code]; - if (extra !== 0) { - lc -= Tree.base_length[code]; - send_bits(lc, extra); // send the extra length bits - } - dist--; // dist is now the match distance - 1 - code = Tree.d_code(dist); - - send_code(code, dtree); // send the distance code - extra = Tree.extra_dbits[code]; - if (extra !== 0) { - dist -= Tree.base_dist[code]; - send_bits(dist, extra); // send the extra distance bits - } - } // literal or match pair ? - } while (lx < last_lit); - } - - send_code(END_BLOCK, ltree); - last_eob_len = ltree[END_BLOCK * 2 + 1]; - } - - // Flush the bit buffer and align the output on a byte boundary - function bi_windup() { - if (bi_valid > 8) { - put_short(bi_buf); - } else if (bi_valid > 0) { - put_byte(bi_buf & 0xff); - } - bi_buf = 0; - bi_valid = 0; - } - - // Copy a stored block, storing first the length and its - // one's complement if requested. - function copy_block(buf, // the input data - len, // its length - header // true if block header must be written - ) { - bi_windup(); // align on byte boundary - last_eob_len = 8; // enough lookahead for inflate - - if (header) { - put_short(len); - put_short(~len); - } - - that.pending_buf.set(win.subarray(buf, buf + len), that.pending); - that.pending += len; - } - - // Send a stored block - function _tr_stored_block(buf, // input block - stored_len, // length of input block - eof // true if this is the last block for a file - ) { - send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type - copy_block(buf, stored_len, true); // with header - } - - // Determine the best encoding for the current block: dynamic trees, static - // trees or store, and output the encoded block to the zip file. - function _tr_flush_block(buf, // input block, or NULL if too old - stored_len, // length of input block - eof // true if this is the last block for a file - ) { - let opt_lenb, static_lenb;// opt_len and static_len in bytes - let max_blindex = 0; // index of last bit length code of non zero freq - - // Build the Huffman trees unless a stored block is forced - if (level > 0) { - // Construct the literal and distance trees - l_desc.build_tree(that); - - d_desc.build_tree(that); - - // At this point, opt_len and static_len are the total bit lengths - // of - // the compressed block data, excluding the tree representations. - - // Build the bit length tree for the above two trees, and get the - // index - // in bl_order of the last bit length code to send. - max_blindex = build_bl_tree(); - - // Determine the best encoding. Compute first the block length in - // bytes - opt_lenb = (that.opt_len + 3 + 7) >>> 3; - static_lenb = (that.static_len + 3 + 7) >>> 3; - - if (static_lenb <= opt_lenb) - opt_lenb = static_lenb; - } else { - opt_lenb = static_lenb = stored_len + 5; // force a stored block - } - - if ((stored_len + 4 <= opt_lenb) && buf != -1) { - // 4: two words for the lengths - // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - // Otherwise we can't have processed more than WSIZE input bytes - // since - // the last block flush, because compression would have been - // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - // transform a block into a stored block. - _tr_stored_block(buf, stored_len, eof); - } else if (static_lenb == opt_lenb) { - send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); - compress_block(StaticTree.static_ltree, StaticTree.static_dtree); - } else { - send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); - send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1); - compress_block(dyn_ltree, dyn_dtree); - } - - // The above check is made mod 2^32, for files larger than 512 MB - // and uLong implemented on 32 bits. - - init_block(); - - if (eof) { - bi_windup(); - } - } - - function flush_block_only(eof) { - _tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof); - block_start = strstart; - strm.flush_pending(); - } - - // Fill the win when the lookahead becomes insufficient. - // Updates strstart and lookahead. - // - // IN assertion: lookahead < MIN_LOOKAHEAD - // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - // At least one byte has been read, or avail_in === 0; reads are - // performed for at least two bytes (required for the zip translate_eol - // option -- not supported here). - function fill_window() { - let n, m; - let p; - let more; // Amount of free space at the end of the win. - - do { - more = (window_size - lookahead - strstart); - - // Deal with !@#$% 64K limit: - if (more === 0 && strstart === 0 && lookahead === 0) { - more = w_size; - } else if (more == -1) { - // Very unlikely, but possible on 16 bit machine if strstart == - // 0 - // and lookahead == 1 (input done one byte at time) - more--; - - // If the win is almost full and there is insufficient - // lookahead, - // move the upper half to the lower one to make room in the - // upper half. - } else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) { - win.set(win.subarray(w_size, w_size + w_size), 0); - - match_start -= w_size; - strstart -= w_size; // we now have strstart >= MAX_DIST - block_start -= w_size; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). We slide even when level == - // 0 - // to keep the hash table consistent if we switch back to level - // > 0 - // later. (Using level 0 permanently is not an optimal usage of - // zlib, so we don't care about this pathological case.) - - n = hash_size; - p = n; - do { - m = (head[--p] & 0xffff); - head[p] = (m >= w_size ? m - w_size : 0); - } while (--n !== 0); - - n = w_size; - p = n; - do { - m = (prev[--p] & 0xffff); - prev[p] = (m >= w_size ? m - w_size : 0); - // If n is not on any hash chain, prev[n] is garbage but - // its value will never be used. - } while (--n !== 0); - more += w_size; - } - - if (strm.avail_in === 0) - return; - - // If there was no sliding: - // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - // more == window_size - lookahead - strstart - // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - // => more >= window_size - 2*WSIZE + 2 - // In the BIG_MEM or MMAP case (not yet supported), - // window_size == input_size + MIN_LOOKAHEAD && - // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - // Otherwise, window_size == 2*WSIZE so more >= 2. - // If there was sliding, more >= WSIZE. So in all cases, more >= 2. - - n = strm.read_buf(win, strstart + lookahead, more); - lookahead += n; - - // Initialize the hash value now that we have some input: - if (lookahead >= MIN_MATCH) { - ins_h = win[strstart] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (win[strstart + 1] & 0xff)) & hash_mask; - } - // If the whole input has less than MIN_MATCH bytes, ins_h is - // garbage, - // but this is not important since only literal bytes will be - // emitted. - } while (lookahead < MIN_LOOKAHEAD && strm.avail_in !== 0); - } - - // Copy without compression as much as possible from the input stream, - // return - // the current block state. - // This function does not insert new strings in the dictionary since - // uncompressible data is probably not useful. This function is used - // only for the level=0 compression option. - // NOTE: this function should be optimized to avoid extra copying from - // win to pending_buf. - function deflate_stored(flush) { - // Stored blocks are limited to 0xffff bytes, pending_buf is limited - // to pending_buf_size, and each stored block has a 5 byte header: - - let max_block_size = 0xffff; - let max_start; - - if (max_block_size > pending_buf_size - 5) { - max_block_size = pending_buf_size - 5; - } - - // Copy as much as possible from input to output: - // eslint-disable-next-line no-constant-condition - while (true) { - // Fill the win as much as possible: - if (lookahead <= 1) { - fill_window(); - if (lookahead === 0 && flush == Z_NO_FLUSH$1) - return NeedMore; - if (lookahead === 0) - break; // flush the current block - } - - strstart += lookahead; - lookahead = 0; - - // Emit a stored block if pending_buf will be full: - max_start = block_start + max_block_size; - if (strstart === 0 || strstart >= max_start) { - // strstart === 0 is possible when wraparound on 16-bit machine - lookahead = (strstart - max_start); - strstart = max_start; - - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - - } - - // Flush if we may have to slide, otherwise block_start may become - // negative and the data will be gone: - if (strstart - block_start >= w_size - MIN_LOOKAHEAD) { - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH$1); - if (strm.avail_out === 0) - return (flush == Z_FINISH$1) ? FinishStarted : NeedMore; - - return flush == Z_FINISH$1 ? FinishDone : BlockDone; - } - - function longest_match(cur_match) { - let chain_length = max_chain_length; // max hash chain length - let scan = strstart; // current string - let match; // matched string - let len; // length of current match - let best_len = prev_length; // best match length so far - const limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; - let _nice_match = nice_match; - - // Stop when cur_match becomes <= limit. To simplify the code, - // we prevent matches with the string of win index 0. - - const wmask = w_mask; - - const strend = strstart + MAX_MATCH; - let scan_end1 = win[scan + best_len - 1]; - let scan_end = win[scan + best_len]; - - // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of - // 16. - // It is easy to get rid of this optimization if necessary. - - // Do not waste too much time if we already have a good match: - if (prev_length >= good_match) { - chain_length >>= 2; - } - - // Do not look for matches beyond the end of the input. This is - // necessary - // to make deflate deterministic. - if (_nice_match > lookahead) - _nice_match = lookahead; - - do { - match = cur_match; - - // Skip to next match if the match length cannot increase - // or if the match length is less than 2: - if (win[match + best_len] != scan_end || win[match + best_len - 1] != scan_end1 || win[match] != win[scan] - || win[++match] != win[scan + 1]) - continue; - - // The check at best_len-1 can be removed because it will be made - // again later. (This heuristic is not always a win.) - // It is not necessary to compare scan[2] and match[2] since they - // are always equal when the other bytes match, given that - // the hash keys are equal and that HASH_BITS >= 8. - scan += 2; - match++; - - // We check for insufficient lookahead only every 8th comparison; - // the 256th check will be made at strstart+258. - // eslint-disable-next-line no-empty - do { - // empty block - } while (win[++scan] == win[++match] && win[++scan] == win[++match] && win[++scan] == win[++match] - && win[++scan] == win[++match] && win[++scan] == win[++match] && win[++scan] == win[++match] - && win[++scan] == win[++match] && win[++scan] == win[++match] && scan < strend); - - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) { - match_start = cur_match; - best_len = len; - if (len >= _nice_match) - break; - scan_end1 = win[scan + best_len - 1]; - scan_end = win[scan + best_len]; - } - - } while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length !== 0); - - if (best_len <= lookahead) - return best_len; - return lookahead; - } - - // Compress as much as possible from the input stream, return the current - // block state. - // This function does not perform lazy evaluation of matches and inserts - // new strings in the dictionary only for unmatched strings or for short - // matches. It is used only for the fast compression options. - function deflate_fast(flush) { - // short hash_head = 0; // head of the hash chain - let hash_head = 0; // head of the hash chain - let bflush; // set if current block must be flushed - - // eslint-disable-next-line no-constant-condition - while (true) { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - if (lookahead < MIN_LOOKAHEAD) { - fill_window(); - if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH$1) { - return NeedMore; - } - if (lookahead === 0) - break; // flush the current block - } - - // Insert the string win[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - if (lookahead >= MIN_MATCH) { - ins_h = (((ins_h) << hash_shift) ^ (win[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - } - - // Find the longest match, discarding those <= prev_length. - // At this point we have always match_length < MIN_MATCH - - if (hash_head !== 0 && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { - // To simplify the code, we prevent matches with the string - // of win index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - if (strategy != Z_HUFFMAN_ONLY) { - match_length = longest_match(hash_head); - } - // longest_match() sets match_start - } - if (match_length >= MIN_MATCH) { - // check_match(strstart, match_start, match_length); - - bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); - - lookahead -= match_length; - - // Insert new strings in the hash table only if the match length - // is not too large. This saves time but degrades compression. - if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) { - match_length--; // string at strstart already in hash table - do { - strstart++; - - ins_h = ((ins_h << hash_shift) ^ (win[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - - // strstart never exceeds WSIZE-MAX_MATCH, so there are - // always MIN_MATCH bytes ahead. - } while (--match_length !== 0); - strstart++; - } else { - strstart += match_length; - match_length = 0; - ins_h = win[strstart] & 0xff; - - ins_h = (((ins_h) << hash_shift) ^ (win[strstart + 1] & 0xff)) & hash_mask; - // If lookahead < MIN_MATCH, ins_h is garbage, but it does - // not - // matter since it will be recomputed at next deflate call. - } - } else { - // No match, output a literal byte - - bflush = _tr_tally(0, win[strstart] & 0xff); - lookahead--; - strstart++; - } - if (bflush) { - - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH$1); - if (strm.avail_out === 0) { - if (flush == Z_FINISH$1) - return FinishStarted; - else - return NeedMore; - } - return flush == Z_FINISH$1 ? FinishDone : BlockDone; - } - - // Same as above, but achieves better compression. We use a lazy - // evaluation for matches: a match is finally adopted only if there is - // no better match at the next win position. - function deflate_slow(flush) { - // short hash_head = 0; // head of hash chain - let hash_head = 0; // head of hash chain - let bflush; // set if current block must be flushed - let max_insert; - - // Process the input block. - // eslint-disable-next-line no-constant-condition - while (true) { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - - if (lookahead < MIN_LOOKAHEAD) { - fill_window(); - if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH$1) { - return NeedMore; - } - if (lookahead === 0) - break; // flush the current block - } - - // Insert the string win[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - - if (lookahead >= MIN_MATCH) { - ins_h = (((ins_h) << hash_shift) ^ (win[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - } - - // Find the longest match, discarding those <= prev_length. - prev_length = match_length; - prev_match = match_start; - match_length = MIN_MATCH - 1; - - if (hash_head !== 0 && prev_length < max_lazy_match && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { - // To simplify the code, we prevent matches with the string - // of win index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - - if (strategy != Z_HUFFMAN_ONLY) { - match_length = longest_match(hash_head); - } - // longest_match() sets match_start - - if (match_length <= 5 && (strategy == Z_FILTERED || (match_length == MIN_MATCH && strstart - match_start > 4096))) { - - // If prev_match is also MIN_MATCH, match_start is garbage - // but we will ignore the current match anyway. - match_length = MIN_MATCH - 1; - } - } - - // If there was a match at the previous step and the current - // match is not better, output the previous match: - if (prev_length >= MIN_MATCH && match_length <= prev_length) { - max_insert = strstart + lookahead - MIN_MATCH; - // Do not insert strings in hash table beyond this. - - // check_match(strstart-1, prev_match, prev_length); - - bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); - - // Insert in hash table all strings up to the end of the match. - // strstart-1 and strstart are already inserted. If there is not - // enough lookahead, the last two strings are not inserted in - // the hash table. - lookahead -= prev_length - 1; - prev_length -= 2; - do { - if (++strstart <= max_insert) { - ins_h = (((ins_h) << hash_shift) ^ (win[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - } - } while (--prev_length !== 0); - match_available = 0; - match_length = MIN_MATCH - 1; - strstart++; - - if (bflush) { - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - } - } else if (match_available !== 0) { - - // If there was no match at the previous position, output a - // single literal. If there was a match but the current match - // is longer, truncate the previous match to a single literal. - - bflush = _tr_tally(0, win[strstart - 1] & 0xff); - - if (bflush) { - flush_block_only(false); - } - strstart++; - lookahead--; - if (strm.avail_out === 0) - return NeedMore; - } else { - // There is no previous match to compare with, wait for - // the next step to decide. - - match_available = 1; - strstart++; - lookahead--; - } - } - - if (match_available !== 0) { - bflush = _tr_tally(0, win[strstart - 1] & 0xff); - match_available = 0; - } - flush_block_only(flush == Z_FINISH$1); - - if (strm.avail_out === 0) { - if (flush == Z_FINISH$1) - return FinishStarted; - else - return NeedMore; - } - - return flush == Z_FINISH$1 ? FinishDone : BlockDone; - } - - function deflateReset(strm) { - strm.total_in = strm.total_out = 0; - strm.msg = null; // - - that.pending = 0; - that.pending_out = 0; - - status = BUSY_STATE; - - last_flush = Z_NO_FLUSH$1; - - tr_init(); - lm_init(); - return Z_OK$1; - } - - that.deflateInit = function (strm, _level, bits, _method, memLevel, _strategy) { - if (!_method) - _method = Z_DEFLATED$1; - if (!memLevel) - memLevel = DEF_MEM_LEVEL; - if (!_strategy) - _strategy = Z_DEFAULT_STRATEGY; - - // byte[] my_version=ZLIB_VERSION; - - // - // if (!version || version[0] != my_version[0] - // || stream_size != sizeof(z_stream)) { - // return Z_VERSION_ERROR; - // } - - strm.msg = null; - - if (_level == Z_DEFAULT_COMPRESSION) - _level = 6; - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || _method != Z_DEFLATED$1 || bits < 9 || bits > 15 || _level < 0 || _level > 9 || _strategy < 0 - || _strategy > Z_HUFFMAN_ONLY) { - return Z_STREAM_ERROR$1; - } - - strm.dstate = that; - - w_bits = bits; - w_size = 1 << w_bits; - w_mask = w_size - 1; - - hash_bits = memLevel + 7; - hash_size = 1 << hash_bits; - hash_mask = hash_size - 1; - hash_shift = Math.floor((hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - win = new Uint8Array(w_size * 2); - prev = []; - head = []; - - lit_bufsize = 1 << (memLevel + 6); // 16K elements by default - - that.pending_buf = new Uint8Array(lit_bufsize * 4); - pending_buf_size = lit_bufsize * 4; - - that.dist_buf = new Uint16Array(lit_bufsize); - that.lc_buf = new Uint8Array(lit_bufsize); - - level = _level; - - strategy = _strategy; - - return deflateReset(strm); - }; - - that.deflateEnd = function () { - if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) { - return Z_STREAM_ERROR$1; - } - // Deallocate in reverse order of allocations: - that.lc_buf = null; - that.dist_buf = null; - that.pending_buf = null; - head = null; - prev = null; - win = null; - // free - that.dstate = null; - return status == BUSY_STATE ? Z_DATA_ERROR$1 : Z_OK$1; - }; - - that.deflateParams = function (strm, _level, _strategy) { - let err = Z_OK$1; - - if (_level == Z_DEFAULT_COMPRESSION) { - _level = 6; - } - if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { - return Z_STREAM_ERROR$1; - } - - if (config_table[level].func != config_table[_level].func && strm.total_in !== 0) { - // Flush the last buffer: - err = strm.deflate(Z_PARTIAL_FLUSH); - } - - if (level != _level) { - level = _level; - max_lazy_match = config_table[level].max_lazy; - good_match = config_table[level].good_length; - nice_match = config_table[level].nice_length; - max_chain_length = config_table[level].max_chain; - } - strategy = _strategy; - return err; - }; - - that.deflateSetDictionary = function (_strm, dictionary, dictLength) { - let length = dictLength; - let n, index = 0; - - if (!dictionary || status != INIT_STATE) - return Z_STREAM_ERROR$1; - - if (length < MIN_MATCH) - return Z_OK$1; - if (length > w_size - MIN_LOOKAHEAD) { - length = w_size - MIN_LOOKAHEAD; - index = dictLength - length; // use the tail of the dictionary - } - win.set(dictionary.subarray(index, index + length), 0); - - strstart = length; - block_start = length; - - // Insert all strings in the hash table (except for the last two bytes). - // s->lookahead stays null, so s->ins_h will be recomputed at the next - // call of fill_window. - - ins_h = win[0] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (win[1] & 0xff)) & hash_mask; - - for (n = 0; n <= length - MIN_MATCH; n++) { - ins_h = (((ins_h) << hash_shift) ^ (win[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - prev[n & w_mask] = head[ins_h]; - head[ins_h] = n; - } - return Z_OK$1; - }; - - that.deflate = function (_strm, flush) { - let i, header, level_flags, old_flush, bstate; - - if (flush > Z_FINISH$1 || flush < 0) { - return Z_STREAM_ERROR$1; - } - - if (!_strm.next_out || (!_strm.next_in && _strm.avail_in !== 0) || (status == FINISH_STATE && flush != Z_FINISH$1)) { - _strm.msg = z_errmsg[Z_NEED_DICT$1 - (Z_STREAM_ERROR$1)]; - return Z_STREAM_ERROR$1; - } - if (_strm.avail_out === 0) { - _strm.msg = z_errmsg[Z_NEED_DICT$1 - (Z_BUF_ERROR$1)]; - return Z_BUF_ERROR$1; - } - - strm = _strm; // just in case - old_flush = last_flush; - last_flush = flush; - - // Write the zlib header - if (status == INIT_STATE) { - header = (Z_DEFLATED$1 + ((w_bits - 8) << 4)) << 8; - level_flags = ((level - 1) & 0xff) >> 1; - - if (level_flags > 3) - level_flags = 3; - header |= (level_flags << 6); - if (strstart !== 0) - header |= PRESET_DICT$1; - header += 31 - (header % 31); - - status = BUSY_STATE; - putShortMSB(header); - } - - // Flush as much pending output as possible - if (that.pending !== 0) { - strm.flush_pending(); - if (strm.avail_out === 0) { - // console.log(" avail_out==0"); - // Since avail_out is 0, deflate will be called again with - // more output space, but possibly with both pending and - // avail_in equal to zero. There won't be anything to do, - // but this is not an error situation so make sure we - // return OK instead of BUF_ERROR at next call of deflate: - last_flush = -1; - return Z_OK$1; - } - - // Make sure there is something to do and avoid duplicate - // consecutive - // flushes. For repeated and useless calls with Z_FINISH, we keep - // returning Z_STREAM_END instead of Z_BUFF_ERROR. - } else if (strm.avail_in === 0 && flush <= old_flush && flush != Z_FINISH$1) { - strm.msg = z_errmsg[Z_NEED_DICT$1 - (Z_BUF_ERROR$1)]; - return Z_BUF_ERROR$1; - } - - // User must not provide more input after the first FINISH: - if (status == FINISH_STATE && strm.avail_in !== 0) { - _strm.msg = z_errmsg[Z_NEED_DICT$1 - (Z_BUF_ERROR$1)]; - return Z_BUF_ERROR$1; - } - - // Start a new block or continue the current one. - if (strm.avail_in !== 0 || lookahead !== 0 || (flush != Z_NO_FLUSH$1 && status != FINISH_STATE)) { - bstate = -1; - switch (config_table[level].func) { - case STORED$1: - bstate = deflate_stored(flush); - break; - case FAST: - bstate = deflate_fast(flush); - break; - case SLOW: - bstate = deflate_slow(flush); - break; - } - - if (bstate == FinishStarted || bstate == FinishDone) { - status = FINISH_STATE; - } - if (bstate == NeedMore || bstate == FinishStarted) { - if (strm.avail_out === 0) { - last_flush = -1; // avoid BUF_ERROR next call, see above - } - return Z_OK$1; - // If flush != Z_NO_FLUSH && avail_out === 0, the next call - // of deflate should use the same flush parameter to make sure - // that the flush is complete. So we don't have to output an - // empty block here, this will be done at next call. This also - // ensures that for a very small output buffer, we emit at most - // one empty block. - } - - if (bstate == BlockDone) { - if (flush == Z_PARTIAL_FLUSH) { - _tr_align(); - } else { // FULL_FLUSH or SYNC_FLUSH - _tr_stored_block(0, 0, false); - // For a full flush, this empty block will be recognized - // as a special marker by inflate_sync(). - if (flush == Z_FULL_FLUSH) { - // state.head[s.hash_size-1]=0; - for (i = 0; i < hash_size/*-1*/; i++) - // forget history - head[i] = 0; - } - } - strm.flush_pending(); - if (strm.avail_out === 0) { - last_flush = -1; // avoid BUF_ERROR at next call, see above - return Z_OK$1; - } - } - } - - if (flush != Z_FINISH$1) - return Z_OK$1; - return Z_STREAM_END$1; - }; -} - -// ZStream - -function ZStream$1() { - const that = this; - that.next_in_index = 0; - that.next_out_index = 0; - // that.next_in; // next input byte - that.avail_in = 0; // number of bytes available at next_in - that.total_in = 0; // total nb of input bytes read so far - // that.next_out; // next output byte should be put there - that.avail_out = 0; // remaining free space at next_out - that.total_out = 0; // total nb of bytes output so far - // that.msg; - // that.dstate; -} - -ZStream$1.prototype = { - deflateInit(level, bits) { - const that = this; - that.dstate = new Deflate(); - if (!bits) - bits = MAX_BITS$1; - return that.dstate.deflateInit(that, level, bits); - }, - - deflate(flush) { - const that = this; - if (!that.dstate) { - return Z_STREAM_ERROR$1; - } - return that.dstate.deflate(that, flush); - }, - - deflateEnd() { - const that = this; - if (!that.dstate) - return Z_STREAM_ERROR$1; - const ret = that.dstate.deflateEnd(); - that.dstate = null; - return ret; - }, - - deflateParams(level, strategy) { - const that = this; - if (!that.dstate) - return Z_STREAM_ERROR$1; - return that.dstate.deflateParams(that, level, strategy); - }, - - deflateSetDictionary(dictionary, dictLength) { - const that = this; - if (!that.dstate) - return Z_STREAM_ERROR$1; - return that.dstate.deflateSetDictionary(that, dictionary, dictLength); - }, - - // Read a new buffer from the current input stream, update the - // total number of bytes read. All deflate() input goes through - // this function so some applications may wish to modify it to avoid - // allocating a large strm->next_in buffer and copying from it. - // (See also flush_pending()). - read_buf(buf, start, size) { - const that = this; - let len = that.avail_in; - if (len > size) - len = size; - if (len === 0) - return 0; - that.avail_in -= len; - buf.set(that.next_in.subarray(that.next_in_index, that.next_in_index + len), start); - that.next_in_index += len; - that.total_in += len; - return len; - }, - - // Flush as much pending output as possible. All deflate() output goes - // through this function so some applications may wish to modify it - // to avoid allocating a large strm->next_out buffer and copying into it. - // (See also read_buf()). - flush_pending() { - const that = this; - let len = that.dstate.pending; - - if (len > that.avail_out) - len = that.avail_out; - if (len === 0) - return; - - // if (that.dstate.pending_buf.length <= that.dstate.pending_out || that.next_out.length <= that.next_out_index - // || that.dstate.pending_buf.length < (that.dstate.pending_out + len) || that.next_out.length < (that.next_out_index + - // len)) { - // console.log(that.dstate.pending_buf.length + ", " + that.dstate.pending_out + ", " + that.next_out.length + ", " + - // that.next_out_index + ", " + len); - // console.log("avail_out=" + that.avail_out); - // } - - that.next_out.set(that.dstate.pending_buf.subarray(that.dstate.pending_out, that.dstate.pending_out + len), that.next_out_index); - - that.next_out_index += len; - that.dstate.pending_out += len; - that.total_out += len; - that.avail_out -= len; - that.dstate.pending -= len; - if (that.dstate.pending === 0) { - that.dstate.pending_out = 0; - } - } -}; - -// Deflate - -function ZipDeflate(options) { - const that = this; - const z = new ZStream$1(); - const bufsize = getMaximumCompressedSize(options && options.chunkSize ? options.chunkSize : 64 * 1024); - const flush = Z_NO_FLUSH$1; - const buf = new Uint8Array(bufsize); - let level = options ? options.level : Z_DEFAULT_COMPRESSION; - if (typeof level == "undefined") - level = Z_DEFAULT_COMPRESSION; - z.deflateInit(level); - z.next_out = buf; - - that.append = function (data, onprogress) { - let err, array, lastIndex = 0, bufferIndex = 0, bufferSize = 0; - const buffers = []; - if (!data.length) - return; - z.next_in_index = 0; - z.next_in = data; - z.avail_in = data.length; - do { - z.next_out_index = 0; - z.avail_out = bufsize; - err = z.deflate(flush); - if (err != Z_OK$1) - throw new Error("deflating: " + z.msg); - if (z.next_out_index) - if (z.next_out_index == bufsize) - buffers.push(new Uint8Array(buf)); - else - buffers.push(buf.slice(0, z.next_out_index)); - bufferSize += z.next_out_index; - if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) { - onprogress(z.next_in_index); - lastIndex = z.next_in_index; - } - } while (z.avail_in > 0 || z.avail_out === 0); - if (buffers.length > 1) { - array = new Uint8Array(bufferSize); - buffers.forEach(function (chunk) { - array.set(chunk, bufferIndex); - bufferIndex += chunk.length; - }); - } else { - array = buffers[0] || new Uint8Array(); - } - return array; - }; - that.flush = function () { - let err, array, bufferIndex = 0, bufferSize = 0; - const buffers = []; - do { - z.next_out_index = 0; - z.avail_out = bufsize; - err = z.deflate(Z_FINISH$1); - if (err != Z_STREAM_END$1 && err != Z_OK$1) - throw new Error("deflating: " + z.msg); - if (bufsize - z.avail_out > 0) - buffers.push(buf.slice(0, z.next_out_index)); - bufferSize += z.next_out_index; - } while (z.avail_in > 0 || z.avail_out === 0); - z.deflateEnd(); - array = new Uint8Array(bufferSize); - buffers.forEach(function (chunk) { - array.set(chunk, bufferIndex); - bufferIndex += chunk.length; - }); - return array; - }; -} - -function getMaximumCompressedSize(uncompressedSize) { - return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1)); -} - -/* - Copyright (c) 2022 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. - */ - -// deno-lint-ignore-file no-this-alias prefer-const - -// Global - -const MAX_BITS = 15; - -const Z_OK = 0; -const Z_STREAM_END = 1; -const Z_NEED_DICT = 2; -const Z_STREAM_ERROR = -2; -const Z_DATA_ERROR = -3; -const Z_MEM_ERROR = -4; -const Z_BUF_ERROR = -5; - -const inflate_mask = [0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, - 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff]; - -const MANY = 1440; - -// JZlib version : "1.0.2" -const Z_NO_FLUSH = 0; -const Z_FINISH = 4; - -// InfTree -const fixed_bl = 9; -const fixed_bd = 5; - -const fixed_tl = [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]; -const fixed_td = [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]; - -// Tables for deflate from PKZIP's appnote.txt. -const cplens = [ // Copy lengths for literal codes 257..285 - 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]; - -// see note #13 above about 258 -const cplext = [ // Extra bits for literal codes 257..285 - 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 // 112==invalid -]; - -const cpdist = [ // Copy offsets for distance codes 0..29 - 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]; - -const cpdext = [ // Extra bits for distance codes - 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]; - -// If BMAX needs to be larger than 16, then h and x[] should be uLong. -const BMAX = 15; // maximum bit length of any code - -function InfTree() { - const that = this; - - let hn; // hufts used in space - let v; // work area for huft_build - let c; // bit length count table - let r; // table entry for structure assignment - let u; // table stack - let x; // bit offsets, then code stack - - function huft_build(b, // code lengths in bits (all assumed <= - // BMAX) - bindex, n, // number of codes (assumed <= 288) - s, // number of simple-valued codes (0..s-1) - d, // list of base values for non-simple codes - e, // list of extra bits for non-simple codes - t, // result: starting table - m, // maximum lookup bits, returns actual - hp,// space for trees - hn,// hufts used in space - v // working area: values in order of bit length - ) { - // Given a list of code lengths and a maximum table size, make a set of - // tables to decode that set of codes. Return Z_OK on success, - // Z_BUF_ERROR - // if the given code set is incomplete (the tables are still built in - // this - // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set - // of - // lengths), or Z_MEM_ERROR if not enough memory. - - let a; // counter for codes of length k - let f; // i repeats in table every f entries - let g; // maximum code length - let h; // table level - let i; // counter, current code - let j; // counter - let k; // number of bits in current code - let l; // bits per table (returned in m) - let mask; // (1 << w) - 1, to avoid cc -O bug on HP - let p; // pointer into c[], b[], or v[] - let q; // points to current table - let w; // bits before this table == (l * h) - let xp; // pointer into x - let y; // number of dummy codes added - let z; // number of entries in current table - - // Generate counts for each bit length - - p = 0; - i = n; - do { - c[b[bindex + p]]++; - p++; - i--; // assume all entries <= BMAX - } while (i !== 0); - - if (c[0] == n) { // null input--all zero length codes - t[0] = -1; - m[0] = 0; - return Z_OK; - } - - // Find minimum and maximum length, bound *m by those - l = m[0]; - for (j = 1; j <= BMAX; j++) - if (c[j] !== 0) - break; - k = j; // minimum code length - if (l < j) { - l = j; - } - for (i = BMAX; i !== 0; i--) { - if (c[i] !== 0) - break; - } - g = i; // maximum code length - if (l > i) { - l = i; - } - m[0] = l; - - // Adjust last length count to fill out codes, if needed - for (y = 1 << j; j < i; j++, y <<= 1) { - if ((y -= c[j]) < 0) { - return Z_DATA_ERROR; - } - } - if ((y -= c[i]) < 0) { - return Z_DATA_ERROR; - } - c[i] += y; - - // Generate starting offsets into the value table for each length - x[1] = j = 0; - p = 1; - xp = 2; - while (--i !== 0) { // note that i == g from above - x[xp] = (j += c[p]); - xp++; - p++; - } - - // Make a table of values in order of bit lengths - i = 0; - p = 0; - do { - if ((j = b[bindex + p]) !== 0) { - v[x[j]++] = i; - } - p++; - } while (++i < n); - n = x[g]; // set n to length of v - - // Generate the Huffman codes and for each, make the table entries - x[0] = i = 0; // first Huffman code is zero - p = 0; // grab values in bit order - h = -1; // no tables yet--level -1 - w = -l; // bits decoded == (l * h) - u[0] = 0; // just to keep compilers happy - q = 0; // ditto - z = 0; // ditto - - // go through the bit lengths (k already is bits in shortest code) - for (; k <= g; k++) { - a = c[k]; - while (a-- !== 0) { - // here i is the Huffman code of length k bits for value *p - // make tables up to required level - while (k > w + l) { - h++; - w += l; // previous table always l bits - // compute minimum size table less than or equal to l bits - z = g - w; - z = (z > l) ? l : z; // table size upper limit - if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table - // too few codes for - // k-w bit table - f -= a + 1; // deduct codes from patterns left - xp = k; - if (j < z) { - while (++j < z) { // try smaller tables up to z bits - if ((f <<= 1) <= c[++xp]) - break; // enough codes to use up j bits - f -= c[xp]; // else deduct codes from patterns - } - } - } - z = 1 << j; // table entries for j-bit table - - // allocate new table - if (hn[0] + z > MANY) { // (note: doesn't matter for fixed) - return Z_DATA_ERROR; // overflow of MANY - } - u[h] = q = /* hp+ */hn[0]; // DEBUG - hn[0] += z; - - // connect to last table, if there is one - if (h !== 0) { - x[h] = i; // save pattern for backing up - r[0] = /* (byte) */j; // bits in this table - r[1] = /* (byte) */l; // bits to dump before this table - j = i >>> (w - l); - r[2] = /* (int) */(q - u[h - 1] - j); // offset to this table - hp.set(r, (u[h - 1] + j) * 3); - // to - // last - // table - } else { - t[0] = q; // first table is returned result - } - } - - // set up table entry in r - r[1] = /* (byte) */(k - w); - if (p >= n) { - r[0] = 128 + 64; // out of values--invalid code - } else if (v[p] < s) { - r[0] = /* (byte) */(v[p] < 256 ? 0 : 32 + 64); // 256 is - // end-of-block - r[2] = v[p++]; // simple code is just the value - } else { - r[0] = /* (byte) */(e[v[p] - s] + 16 + 64); // non-simple--look - // up in lists - r[2] = d[v[p++] - s]; - } - - // fill code-like entries with r - f = 1 << (k - w); - for (j = i >>> w; j < z; j += f) { - hp.set(r, (q + j) * 3); - } - - // backwards increment the k-bit code i - for (j = 1 << (k - 1); (i & j) !== 0; j >>>= 1) { - i ^= j; - } - i ^= j; - - // backup over finished tables - mask = (1 << w) - 1; // needed on HP, cc -O bug - while ((i & mask) != x[h]) { - h--; // don't need to update q - w -= l; - mask = (1 << w) - 1; - } - } - } - // Return Z_BUF_ERROR if we were given an incomplete table - return y !== 0 && g != 1 ? Z_BUF_ERROR : Z_OK; - } - - function initWorkArea(vsize) { - let i; - if (!hn) { - hn = []; // []; //new Array(1); - v = []; // new Array(vsize); - c = new Int32Array(BMAX + 1); // new Array(BMAX + 1); - r = []; // new Array(3); - u = new Int32Array(BMAX); // new Array(BMAX); - x = new Int32Array(BMAX + 1); // new Array(BMAX + 1); - } - if (v.length < vsize) { - v = []; // new Array(vsize); - } - for (i = 0; i < vsize; i++) { - v[i] = 0; - } - for (i = 0; i < BMAX + 1; i++) { - c[i] = 0; - } - for (i = 0; i < 3; i++) { - r[i] = 0; - } - // for(int i=0; i 257)) { - if (result == Z_DATA_ERROR) { - z.msg = "oversubscribed distance tree"; - } else if (result == Z_BUF_ERROR) { - z.msg = "incomplete distance tree"; - result = Z_DATA_ERROR; - } else if (result != Z_MEM_ERROR) { - z.msg = "empty distance tree with lengths"; - result = Z_DATA_ERROR; - } - return result; - } - - return Z_OK; - }; - -} - -InfTree.inflate_trees_fixed = function (bl, // literal desired/actual bit depth - bd, // distance desired/actual bit depth - tl,// literal/length tree result - td// distance tree result -) { - bl[0] = fixed_bl; - bd[0] = fixed_bd; - tl[0] = fixed_tl; - td[0] = fixed_td; - return Z_OK; -}; - -// InfCodes - -// waiting for "i:"=input, -// "o:"=output, -// "x:"=nothing -const START = 0; // x: set up for LEN -const LEN = 1; // i: get length/literal/eob next -const LENEXT = 2; // i: getting length extra (have base) -const DIST = 3; // i: get distance next -const DISTEXT = 4;// i: getting distance extra -const COPY = 5; // o: copying bytes in win, waiting -// for space -const LIT = 6; // o: got literal, waiting for output -// space -const WASH = 7; // o: got eob, possibly still output -// waiting -const END = 8; // x: got eob and all data flushed -const BADCODE = 9;// x: got error - -function InfCodes() { - const that = this; - - let mode; // current inflate_codes mode - - // mode dependent information - let len = 0; - - let tree; // pointer into tree - let tree_index = 0; - let need = 0; // bits needed - - let lit = 0; - - // if EXT or COPY, where and how much - let get = 0; // bits to get for extra - let dist = 0; // distance back to copy from - - let lbits = 0; // ltree bits decoded per branch - let dbits = 0; // dtree bits decoder per branch - let ltree; // literal/length/eob tree - let ltree_index = 0; // literal/length/eob tree - let dtree; // distance tree - let dtree_index = 0; // distance tree - - // Called with number of bytes left to write in win at least 258 - // (the maximum string length) and number of input bytes available - // at least ten. The ten bytes are six bytes for the longest length/ - // distance pair plus four bytes for overloading the bit buffer. - - function inflate_fast(bl, bd, tl, tl_index, td, td_index, s, z) { - let t; // temporary pointer - let tp; // temporary pointer - let tp_index; // temporary pointer - let e; // extra bits or operation - let b; // bit buffer - let k; // bits in bit buffer - let p; // input data pointer - let n; // bytes available there - let q; // output win write pointer - let m; // bytes to end of win or read pointer - let ml; // mask for literal/length tree - let md; // mask for distance tree - let c; // bytes to copy - let d; // distance back to copy from - let r; // copy source pointer - - let tp_index_t_3; // (tp_index+t)*3 - - // load input, output, bit values - p = z.next_in_index; - n = z.avail_in; - b = s.bitb; - k = s.bitk; - q = s.write; - m = q < s.read ? s.read - q - 1 : s.end - q; - - // initialize masks - ml = inflate_mask[bl]; - md = inflate_mask[bd]; - - // do until not enough input or output space for fast loop - do { // assume called with m >= 258 && n >= 10 - // get literal/length code - while (k < (20)) { // max bits for literal/length code - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - t = b & ml; - tp = tl; - tp_index = tl_index; - tp_index_t_3 = (tp_index + t) * 3; - if ((e = tp[tp_index_t_3]) === 0) { - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - - s.win[q++] = /* (byte) */tp[tp_index_t_3 + 2]; - m--; - continue; - } - do { - - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - - if ((e & 16) !== 0) { - e &= 15; - c = tp[tp_index_t_3 + 2] + (/* (int) */b & inflate_mask[e]); - - b >>= e; - k -= e; - - // decode distance base of block to copy - while (k < (15)) { // max bits for distance code - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - t = b & md; - tp = td; - tp_index = td_index; - tp_index_t_3 = (tp_index + t) * 3; - e = tp[tp_index_t_3]; - - do { - - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - - if ((e & 16) !== 0) { - // get extra bits to add to distance base - e &= 15; - while (k < (e)) { // get extra bits (up to 13) - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); - - b >>= (e); - k -= (e); - - // do the copy - m -= c; - if (q >= d) { // offset before dest - // just copy - r = q - d; - if (q - r > 0 && 2 > (q - r)) { - s.win[q++] = s.win[r++]; // minimum - // count is - // three, - s.win[q++] = s.win[r++]; // so unroll - // loop a - // little - c -= 2; - } else { - s.win.set(s.win.subarray(r, r + 2), q); - q += 2; - r += 2; - c -= 2; - } - } else { // else offset after destination - r = q - d; - do { - r += s.end; // force pointer in win - } while (r < 0); // covers invalid distances - e = s.end - r; - if (c > e) { // if source crosses, - c -= e; // wrapped copy - if (q - r > 0 && e > (q - r)) { - do { - s.win[q++] = s.win[r++]; - } while (--e !== 0); - } else { - s.win.set(s.win.subarray(r, r + e), q); - q += e; - r += e; - e = 0; - } - r = 0; // copy rest from start of win - } - - } - - // copy all or what's left - if (q - r > 0 && c > (q - r)) { - do { - s.win[q++] = s.win[r++]; - } while (--c !== 0); - } else { - s.win.set(s.win.subarray(r, r + c), q); - q += c; - r += c; - c = 0; - } - break; - } else if ((e & 64) === 0) { - t += tp[tp_index_t_3 + 2]; - t += (b & inflate_mask[e]); - tp_index_t_3 = (tp_index + t) * 3; - e = tp[tp_index_t_3]; - } else { - z.msg = "invalid distance code"; - - c = z.avail_in - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= c << 3; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - - return Z_DATA_ERROR; - } - // eslint-disable-next-line no-constant-condition - } while (true); - break; - } - - if ((e & 64) === 0) { - t += tp[tp_index_t_3 + 2]; - t += (b & inflate_mask[e]); - tp_index_t_3 = (tp_index + t) * 3; - if ((e = tp[tp_index_t_3]) === 0) { - - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - - s.win[q++] = /* (byte) */tp[tp_index_t_3 + 2]; - m--; - break; - } - } else if ((e & 32) !== 0) { - - c = z.avail_in - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= c << 3; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - - return Z_STREAM_END; - } else { - z.msg = "invalid literal/length code"; - - c = z.avail_in - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= c << 3; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - - return Z_DATA_ERROR; - } - // eslint-disable-next-line no-constant-condition - } while (true); - } while (m >= 258 && n >= 10); - - // not enough input or output--restore pointers and return - c = z.avail_in - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= c << 3; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - - return Z_OK; - } - - that.init = function (bl, bd, tl, tl_index, td, td_index) { - mode = START; - lbits = /* (byte) */bl; - dbits = /* (byte) */bd; - ltree = tl; - ltree_index = tl_index; - dtree = td; - dtree_index = td_index; - tree = null; - }; - - that.proc = function (s, z, r) { - let j; // temporary storage - let tindex; // temporary pointer - let e; // extra bits or operation - let b = 0; // bit buffer - let k = 0; // bits in bit buffer - let p = 0; // input data pointer - let n; // bytes available there - let q; // output win write pointer - let m; // bytes to end of win or read pointer - let f; // pointer to copy strings from - - // copy input/output information to locals (UPDATE macro restores) - p = z.next_in_index; - n = z.avail_in; - b = s.bitb; - k = s.bitk; - q = s.write; - m = q < s.read ? s.read - q - 1 : s.end - q; - - // process input and output based on current state - // eslint-disable-next-line no-constant-condition - while (true) { - switch (mode) { - // waiting for "i:"=input, "o:"=output, "x:"=nothing - case START: // x: set up for LEN - if (m >= 258 && n >= 10) { - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - r = inflate_fast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, s, z); - - p = z.next_in_index; - n = z.avail_in; - b = s.bitb; - k = s.bitk; - q = s.write; - m = q < s.read ? s.read - q - 1 : s.end - q; - - if (r != Z_OK) { - mode = r == Z_STREAM_END ? WASH : BADCODE; - break; - } - } - need = lbits; - tree = ltree; - tree_index = ltree_index; - - mode = LEN; - /* falls through */ - case LEN: // i: get length/literal/eob next - j = need; - - while (k < (j)) { - if (n !== 0) - r = Z_OK; - else { - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - tindex = (tree_index + (b & inflate_mask[j])) * 3; - - b >>>= (tree[tindex + 1]); - k -= (tree[tindex + 1]); - - e = tree[tindex]; - - if (e === 0) { // literal - lit = tree[tindex + 2]; - mode = LIT; - break; - } - if ((e & 16) !== 0) { // length - get = e & 15; - len = tree[tindex + 2]; - mode = LENEXT; - break; - } - if ((e & 64) === 0) { // next table - need = e; - tree_index = tindex / 3 + tree[tindex + 2]; - break; - } - if ((e & 32) !== 0) { // end of block - mode = WASH; - break; - } - mode = BADCODE; // invalid code - z.msg = "invalid literal/length code"; - r = Z_DATA_ERROR; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - case LENEXT: // i: getting length extra (have base) - j = get; - - while (k < (j)) { - if (n !== 0) - r = Z_OK; - else { - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - len += (b & inflate_mask[j]); - - b >>= j; - k -= j; - - need = dbits; - tree = dtree; - tree_index = dtree_index; - mode = DIST; - /* falls through */ - case DIST: // i: get distance next - j = need; - - while (k < (j)) { - if (n !== 0) - r = Z_OK; - else { - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - tindex = (tree_index + (b & inflate_mask[j])) * 3; - - b >>= tree[tindex + 1]; - k -= tree[tindex + 1]; - - e = (tree[tindex]); - if ((e & 16) !== 0) { // distance - get = e & 15; - dist = tree[tindex + 2]; - mode = DISTEXT; - break; - } - if ((e & 64) === 0) { // next table - need = e; - tree_index = tindex / 3 + tree[tindex + 2]; - break; - } - mode = BADCODE; // invalid code - z.msg = "invalid distance code"; - r = Z_DATA_ERROR; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - case DISTEXT: // i: getting distance extra - j = get; - - while (k < (j)) { - if (n !== 0) - r = Z_OK; - else { - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - dist += (b & inflate_mask[j]); - - b >>= j; - k -= j; - - mode = COPY; - /* falls through */ - case COPY: // o: copying bytes in win, waiting for space - f = q - dist; - while (f < 0) { // modulo win size-"while" instead - f += s.end; // of "if" handles invalid distances - } - while (len !== 0) { - - if (m === 0) { - if (q == s.end && s.read !== 0) { - q = 0; - m = q < s.read ? s.read - q - 1 : s.end - q; - } - if (m === 0) { - s.write = q; - r = s.inflate_flush(z, r); - q = s.write; - m = q < s.read ? s.read - q - 1 : s.end - q; - - if (q == s.end && s.read !== 0) { - q = 0; - m = q < s.read ? s.read - q - 1 : s.end - q; - } - - if (m === 0) { - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - } - } - - s.win[q++] = s.win[f++]; - m--; - - if (f == s.end) - f = 0; - len--; - } - mode = START; - break; - case LIT: // o: got literal, waiting for output space - if (m === 0) { - if (q == s.end && s.read !== 0) { - q = 0; - m = q < s.read ? s.read - q - 1 : s.end - q; - } - if (m === 0) { - s.write = q; - r = s.inflate_flush(z, r); - q = s.write; - m = q < s.read ? s.read - q - 1 : s.end - q; - - if (q == s.end && s.read !== 0) { - q = 0; - m = q < s.read ? s.read - q - 1 : s.end - q; - } - if (m === 0) { - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - } - } - r = Z_OK; - - s.win[q++] = /* (byte) */lit; - m--; - - mode = START; - break; - case WASH: // o: got eob, possibly more output - if (k > 7) { // return unused byte, if any - k -= 8; - n++; - p--; // can always return one - } - - s.write = q; - r = s.inflate_flush(z, r); - q = s.write; - m = q < s.read ? s.read - q - 1 : s.end - q; - - if (s.read != s.write) { - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - mode = END; - /* falls through */ - case END: - r = Z_STREAM_END; - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - case BADCODE: // x: got error - - r = Z_DATA_ERROR; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - default: - r = Z_STREAM_ERROR; - - s.bitb = b; - s.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - } - }; - - that.free = function () { - // ZFREE(z, c); - }; - -} - -// InfBlocks - -// Table for deflate from PKZIP's appnote.txt. -const border = [ // Order of the bit length code lengths - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - -const TYPE = 0; // get type bits (3, including end bit) -const LENS = 1; // get lengths for stored -const STORED = 2;// processing stored block -const TABLE = 3; // get table lengths -const BTREE = 4; // get bit lengths tree for a dynamic -// block -const DTREE = 5; // get length, distance trees for a -// dynamic block -const CODES = 6; // processing fixed or dynamic block -const DRY = 7; // output remaining win bytes -const DONELOCKS = 8; // finished last block, done -const BADBLOCKS = 9; // ot a data error--stuck here - -function InfBlocks(z, w) { - const that = this; - - let mode = TYPE; // current inflate_block mode - - let left = 0; // if STORED, bytes left to copy - - let table = 0; // table lengths (14 bits) - let index = 0; // index into blens (or border) - let blens; // bit lengths of codes - const bb = [0]; // bit length tree depth - const tb = [0]; // bit length decoding tree - - const codes = new InfCodes(); // if CODES, current state - - let last = 0; // true if this block is the last block - - let hufts = new Int32Array(MANY * 3); // single malloc for tree space - const check = 0; // check on output - const inftree = new InfTree(); - - that.bitk = 0; // bits in bit buffer - that.bitb = 0; // bit buffer - that.win = new Uint8Array(w); // sliding win - that.end = w; // one byte after sliding win - that.read = 0; // win read pointer - that.write = 0; // win write pointer - - that.reset = function (z, c) { - if (c) - c[0] = check; - // if (mode == BTREE || mode == DTREE) { - // } - if (mode == CODES) { - codes.free(z); - } - mode = TYPE; - that.bitk = 0; - that.bitb = 0; - that.read = that.write = 0; - }; - - that.reset(z, null); - - // copy as much as possible from the sliding win to the output area - that.inflate_flush = function (z, r) { - let n; - let p; - let q; - - // local copies of source and destination pointers - p = z.next_out_index; - q = that.read; - - // compute number of bytes to copy as far as end of win - n = /* (int) */((q <= that.write ? that.write : that.end) - q); - if (n > z.avail_out) - n = z.avail_out; - if (n !== 0 && r == Z_BUF_ERROR) - r = Z_OK; - - // update counters - z.avail_out -= n; - z.total_out += n; - - // copy as far as end of win - z.next_out.set(that.win.subarray(q, q + n), p); - p += n; - q += n; - - // see if more to copy at beginning of win - if (q == that.end) { - // wrap pointers - q = 0; - if (that.write == that.end) - that.write = 0; - - // compute bytes to copy - n = that.write - q; - if (n > z.avail_out) - n = z.avail_out; - if (n !== 0 && r == Z_BUF_ERROR) - r = Z_OK; - - // update counters - z.avail_out -= n; - z.total_out += n; - - // copy - z.next_out.set(that.win.subarray(q, q + n), p); - p += n; - q += n; - } - - // update pointers - z.next_out_index = p; - that.read = q; - - // done - return r; - }; - - that.proc = function (z, r) { - let t; // temporary storage - let b; // bit buffer - let k; // bits in bit buffer - let p; // input data pointer - let n; // bytes available there - let q; // output win write pointer - let m; // bytes to end of win or read pointer - - let i; - - // copy input/output information to locals (UPDATE macro restores) - // { - p = z.next_in_index; - n = z.avail_in; - b = that.bitb; - k = that.bitk; - // } - // { - q = that.write; - m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); - // } - - // process input based on current state - // DEBUG dtree - // eslint-disable-next-line no-constant-condition - while (true) { - let bl, bd, tl, td, bl_, bd_, tl_, td_; - switch (mode) { - case TYPE: - - while (k < (3)) { - if (n !== 0) { - r = Z_OK; - } else { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - t = /* (int) */(b & 7); - last = t & 1; - - switch (t >>> 1) { - case 0: // stored - // { - b >>>= (3); - k -= (3); - // } - t = k & 7; // go to byte boundary - - // { - b >>>= (t); - k -= (t); - // } - mode = LENS; // get length of stored block - break; - case 1: // fixed - // { - bl = []; // new Array(1); - bd = []; // new Array(1); - tl = [[]]; // new Array(1); - td = [[]]; // new Array(1); - - InfTree.inflate_trees_fixed(bl, bd, tl, td); - codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); - // } - - // { - b >>>= (3); - k -= (3); - // } - - mode = CODES; - break; - case 2: // dynamic - - // { - b >>>= (3); - k -= (3); - // } - - mode = TABLE; - break; - case 3: // illegal - - // { - b >>>= (3); - k -= (3); - // } - mode = BADBLOCKS; - z.msg = "invalid block type"; - r = Z_DATA_ERROR; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - break; - case LENS: - - while (k < (32)) { - if (n !== 0) { - r = Z_OK; - } else { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - if ((((~b) >>> 16) & 0xffff) != (b & 0xffff)) { - mode = BADBLOCKS; - z.msg = "invalid stored block lengths"; - r = Z_DATA_ERROR; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - left = (b & 0xffff); - b = k = 0; // dump bits - mode = left !== 0 ? STORED : (last !== 0 ? DRY : TYPE); - break; - case STORED: - if (n === 0) { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - - if (m === 0) { - if (q == that.end && that.read !== 0) { - q = 0; - m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); - } - if (m === 0) { - that.write = q; - r = that.inflate_flush(z, r); - q = that.write; - m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); - if (q == that.end && that.read !== 0) { - q = 0; - m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); - } - if (m === 0) { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - } - } - r = Z_OK; - - t = left; - if (t > n) - t = n; - if (t > m) - t = m; - that.win.set(z.read_buf(p, t), q); - p += t; - n -= t; - q += t; - m -= t; - if ((left -= t) !== 0) - break; - mode = last !== 0 ? DRY : TYPE; - break; - case TABLE: - - while (k < (14)) { - if (n !== 0) { - r = Z_OK; - } else { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - table = t = (b & 0x3fff); - if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { - mode = BADBLOCKS; - z.msg = "too many length or distance symbols"; - r = Z_DATA_ERROR; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); - if (!blens || blens.length < t) { - blens = []; // new Array(t); - } else { - for (i = 0; i < t; i++) { - blens[i] = 0; - } - } - - // { - b >>>= (14); - k -= (14); - // } - - index = 0; - mode = BTREE; - /* falls through */ - case BTREE: - while (index < 4 + (table >>> 10)) { - while (k < (3)) { - if (n !== 0) { - r = Z_OK; - } else { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - blens[border[index++]] = b & 7; - - // { - b >>>= (3); - k -= (3); - // } - } - - while (index < 19) { - blens[border[index++]] = 0; - } - - bb[0] = 7; - t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); - if (t != Z_OK) { - r = t; - if (r == Z_DATA_ERROR) { - blens = null; - mode = BADBLOCKS; - } - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - - index = 0; - mode = DTREE; - /* falls through */ - case DTREE: - // eslint-disable-next-line no-constant-condition - while (true) { - t = table; - if (index >= 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) { - break; - } - - let j, c; - - t = bb[0]; - - while (k < (t)) { - if (n !== 0) { - r = Z_OK; - } else { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - // if (tb[0] == -1) { - // System.err.println("null..."); - // } - - t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; - c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; - - if (c < 16) { - b >>>= (t); - k -= (t); - blens[index++] = c; - } else { // c == 16..18 - i = c == 18 ? 7 : c - 14; - j = c == 18 ? 11 : 3; - - while (k < (t + i)) { - if (n !== 0) { - r = Z_OK; - } else { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - n--; - b |= (z.read_byte(p++) & 0xff) << k; - k += 8; - } - - b >>>= (t); - k -= (t); - - j += (b & inflate_mask[i]); - - b >>>= (i); - k -= (i); - - i = index; - t = table; - if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { - blens = null; - mode = BADBLOCKS; - z.msg = "invalid bit length repeat"; - r = Z_DATA_ERROR; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - - c = c == 16 ? blens[i - 1] : 0; - do { - blens[i++] = c; - } while (--j !== 0); - index = i; - } - } - - tb[0] = -1; - // { - bl_ = []; // new Array(1); - bd_ = []; // new Array(1); - tl_ = []; // new Array(1); - td_ = []; // new Array(1); - bl_[0] = 9; // must be <= 9 for lookahead assumptions - bd_[0] = 6; // must be <= 9 for lookahead assumptions - - t = table; - t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl_, bd_, tl_, td_, hufts, z); - - if (t != Z_OK) { - if (t == Z_DATA_ERROR) { - blens = null; - mode = BADBLOCKS; - } - r = t; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - codes.init(bl_[0], bd_[0], hufts, tl_[0], hufts, td_[0]); - // } - mode = CODES; - /* falls through */ - case CODES: - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - - if ((r = codes.proc(that, z, r)) != Z_STREAM_END) { - return that.inflate_flush(z, r); - } - r = Z_OK; - codes.free(z); - - p = z.next_in_index; - n = z.avail_in; - b = that.bitb; - k = that.bitk; - q = that.write; - m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); - - if (last === 0) { - mode = TYPE; - break; - } - mode = DRY; - /* falls through */ - case DRY: - that.write = q; - r = that.inflate_flush(z, r); - q = that.write; - m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); - if (that.read != that.write) { - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - mode = DONELOCKS; - /* falls through */ - case DONELOCKS: - r = Z_STREAM_END; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - case BADBLOCKS: - r = Z_DATA_ERROR; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - - default: - r = Z_STREAM_ERROR; - - that.bitb = b; - that.bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - that.write = q; - return that.inflate_flush(z, r); - } - } - }; - - that.free = function (z) { - that.reset(z, null); - that.win = null; - hufts = null; - // ZFREE(z, s); - }; - - that.set_dictionary = function (d, start, n) { - that.win.set(d.subarray(start, start + n), 0); - that.read = that.write = n; - }; - - // Returns true if inflate is currently at the end of a block generated - // by Z_SYNC_FLUSH or Z_FULL_FLUSH. - that.sync_point = function () { - return mode == LENS ? 1 : 0; - }; - -} - -// Inflate - -// preset dictionary flag in zlib header -const PRESET_DICT = 0x20; - -const Z_DEFLATED = 8; - -const METHOD = 0; // waiting for method byte -const FLAG = 1; // waiting for flag byte -const DICT4 = 2; // four dictionary check bytes to go -const DICT3 = 3; // three dictionary check bytes to go -const DICT2 = 4; // two dictionary check bytes to go -const DICT1 = 5; // one dictionary check byte to go -const DICT0 = 6; // waiting for inflateSetDictionary -const BLOCKS = 7; // decompressing blocks -const DONE = 12; // finished check, done -const BAD = 13; // got an error--stay here - -const mark = [0, 0, 0xff, 0xff]; - -function Inflate() { - const that = this; - - that.mode = 0; // current inflate mode - - // mode dependent information - that.method = 0; // if FLAGS, method byte - - // if CHECK, check values to compare - that.was = [0]; // new Array(1); // computed check value - that.need = 0; // stream check value - - // if BAD, inflateSync's marker bytes count - that.marker = 0; - - // mode independent information - that.wbits = 0; // log2(win size) (8..15, defaults to 15) - - // this.blocks; // current inflate_blocks state - - function inflateReset(z) { - if (!z || !z.istate) - return Z_STREAM_ERROR; - - z.total_in = z.total_out = 0; - z.msg = null; - z.istate.mode = BLOCKS; - z.istate.blocks.reset(z, null); - return Z_OK; - } - - that.inflateEnd = function (z) { - if (that.blocks) - that.blocks.free(z); - that.blocks = null; - // ZFREE(z, z->state); - return Z_OK; - }; - - that.inflateInit = function (z, w) { - z.msg = null; - that.blocks = null; - - // set win size - if (w < 8 || w > 15) { - that.inflateEnd(z); - return Z_STREAM_ERROR; - } - that.wbits = w; - - z.istate.blocks = new InfBlocks(z, 1 << w); - - // reset state - inflateReset(z); - return Z_OK; - }; - - that.inflate = function (z, f) { - let r; - let b; - - if (!z || !z.istate || !z.next_in) - return Z_STREAM_ERROR; - const istate = z.istate; - f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; - r = Z_BUF_ERROR; - // eslint-disable-next-line no-constant-condition - while (true) { - switch (istate.mode) { - case METHOD: - - if (z.avail_in === 0) - return r; - r = f; - - z.avail_in--; - z.total_in++; - if (((istate.method = z.read_byte(z.next_in_index++)) & 0xf) != Z_DEFLATED) { - istate.mode = BAD; - z.msg = "unknown compression method"; - istate.marker = 5; // can't try inflateSync - break; - } - if ((istate.method >> 4) + 8 > istate.wbits) { - istate.mode = BAD; - z.msg = "invalid win size"; - istate.marker = 5; // can't try inflateSync - break; - } - istate.mode = FLAG; - /* falls through */ - case FLAG: - - if (z.avail_in === 0) - return r; - r = f; - - z.avail_in--; - z.total_in++; - b = (z.read_byte(z.next_in_index++)) & 0xff; - - if ((((istate.method << 8) + b) % 31) !== 0) { - istate.mode = BAD; - z.msg = "incorrect header check"; - istate.marker = 5; // can't try inflateSync - break; - } - - if ((b & PRESET_DICT) === 0) { - istate.mode = BLOCKS; - break; - } - istate.mode = DICT4; - /* falls through */ - case DICT4: - - if (z.avail_in === 0) - return r; - r = f; - - z.avail_in--; - z.total_in++; - istate.need = ((z.read_byte(z.next_in_index++) & 0xff) << 24) & 0xff000000; - istate.mode = DICT3; - /* falls through */ - case DICT3: - - if (z.avail_in === 0) - return r; - r = f; - - z.avail_in--; - z.total_in++; - istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 16) & 0xff0000; - istate.mode = DICT2; - /* falls through */ - case DICT2: - - if (z.avail_in === 0) - return r; - r = f; - - z.avail_in--; - z.total_in++; - istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 8) & 0xff00; - istate.mode = DICT1; - /* falls through */ - case DICT1: - - if (z.avail_in === 0) - return r; - r = f; - - z.avail_in--; - z.total_in++; - istate.need += (z.read_byte(z.next_in_index++) & 0xff); - istate.mode = DICT0; - return Z_NEED_DICT; - case DICT0: - istate.mode = BAD; - z.msg = "need dictionary"; - istate.marker = 0; // can try inflateSync - return Z_STREAM_ERROR; - case BLOCKS: - - r = istate.blocks.proc(z, r); - if (r == Z_DATA_ERROR) { - istate.mode = BAD; - istate.marker = 0; // can try inflateSync - break; - } - if (r == Z_OK) { - r = f; - } - if (r != Z_STREAM_END) { - return r; - } - r = f; - istate.blocks.reset(z, istate.was); - istate.mode = DONE; - /* falls through */ - case DONE: - z.avail_in = 0; - return Z_STREAM_END; - case BAD: - return Z_DATA_ERROR; - default: - return Z_STREAM_ERROR; - } - } - }; - - that.inflateSetDictionary = function (z, dictionary, dictLength) { - let index = 0, length = dictLength; - if (!z || !z.istate || z.istate.mode != DICT0) - return Z_STREAM_ERROR; - const istate = z.istate; - if (length >= (1 << istate.wbits)) { - length = (1 << istate.wbits) - 1; - index = dictLength - length; - } - istate.blocks.set_dictionary(dictionary, index, length); - istate.mode = BLOCKS; - return Z_OK; - }; - - that.inflateSync = function (z) { - let n; // number of bytes to look at - let p; // pointer to bytes - let m; // number of marker bytes found in a row - let r, w; // temporaries to save total_in and total_out - - // set up - if (!z || !z.istate) - return Z_STREAM_ERROR; - const istate = z.istate; - if (istate.mode != BAD) { - istate.mode = BAD; - istate.marker = 0; - } - if ((n = z.avail_in) === 0) - return Z_BUF_ERROR; - p = z.next_in_index; - m = istate.marker; - - // search - while (n !== 0 && m < 4) { - if (z.read_byte(p) == mark[m]) { - m++; - } else if (z.read_byte(p) !== 0) { - m = 0; - } else { - m = 4 - m; - } - p++; - n--; - } - - // restore - z.total_in += p - z.next_in_index; - z.next_in_index = p; - z.avail_in = n; - istate.marker = m; - - // return no joy or set up to restart on a new block - if (m != 4) { - return Z_DATA_ERROR; - } - r = z.total_in; - w = z.total_out; - inflateReset(z); - z.total_in = r; - z.total_out = w; - istate.mode = BLOCKS; - return Z_OK; - }; - - // Returns true if inflate is currently at the end of a block generated - // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - // implementation to provide an additional safety check. PPP uses - // Z_SYNC_FLUSH - // but removes the length bytes of the resulting empty stored block. When - // decompressing, PPP checks that at the end of input packet, inflate is - // waiting for these length bytes. - that.inflateSyncPoint = function (z) { - if (!z || !z.istate || !z.istate.blocks) - return Z_STREAM_ERROR; - return z.istate.blocks.sync_point(); - }; -} - -// ZStream - -function ZStream() { -} - -ZStream.prototype = { - inflateInit(bits) { - const that = this; - that.istate = new Inflate(); - if (!bits) - bits = MAX_BITS; - return that.istate.inflateInit(that, bits); - }, - - inflate(f) { - const that = this; - if (!that.istate) - return Z_STREAM_ERROR; - return that.istate.inflate(that, f); - }, - - inflateEnd() { - const that = this; - if (!that.istate) - return Z_STREAM_ERROR; - const ret = that.istate.inflateEnd(that); - that.istate = null; - return ret; - }, - - inflateSync() { - const that = this; - if (!that.istate) - return Z_STREAM_ERROR; - return that.istate.inflateSync(that); - }, - inflateSetDictionary(dictionary, dictLength) { - const that = this; - if (!that.istate) - return Z_STREAM_ERROR; - return that.istate.inflateSetDictionary(that, dictionary, dictLength); - }, - read_byte(start) { - const that = this; - return that.next_in[start]; - }, - read_buf(start, size) { - const that = this; - return that.next_in.subarray(start, start + size); - } -}; - -// Inflater - -function ZipInflate(options) { - const that = this; - const z = new ZStream(); - const bufsize = options && options.chunkSize ? Math.floor(options.chunkSize * 2) : 128 * 1024; - const flush = Z_NO_FLUSH; - const buf = new Uint8Array(bufsize); - let nomoreinput = false; - - z.inflateInit(); - z.next_out = buf; - - that.append = function (data, onprogress) { - const buffers = []; - let err, array, lastIndex = 0, bufferIndex = 0, bufferSize = 0; - if (data.length === 0) - return; - z.next_in_index = 0; - z.next_in = data; - z.avail_in = data.length; - do { - z.next_out_index = 0; - z.avail_out = bufsize; - if ((z.avail_in === 0) && (!nomoreinput)) { // if buffer is empty and more input is available, refill it - z.next_in_index = 0; - nomoreinput = true; - } - err = z.inflate(flush); - if (nomoreinput && (err === Z_BUF_ERROR)) { - if (z.avail_in !== 0) - throw new Error("inflating: bad input"); - } else if (err !== Z_OK && err !== Z_STREAM_END) - throw new Error("inflating: " + z.msg); - if ((nomoreinput || err === Z_STREAM_END) && (z.avail_in === data.length)) - throw new Error("inflating: bad input"); - if (z.next_out_index) - if (z.next_out_index === bufsize) - buffers.push(new Uint8Array(buf)); - else - buffers.push(buf.slice(0, z.next_out_index)); - bufferSize += z.next_out_index; - if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) { - onprogress(z.next_in_index); - lastIndex = z.next_in_index; - } - } while (z.avail_in > 0 || z.avail_out === 0); - if (buffers.length > 1) { - array = new Uint8Array(bufferSize); - buffers.forEach(function (chunk) { - array.set(chunk, bufferIndex); - bufferIndex += chunk.length; - }); - } else { - array = buffers[0] || new Uint8Array(); - } - return array; - }; - that.flush = function () { - z.inflateEnd(); - }; -} - -/* - Copyright (c) 2022 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. - */ - -const MAX_32_BITS = 0xffffffff; -const MAX_16_BITS = 0xffff; -const COMPRESSION_METHOD_DEFLATE = 0x08; -const COMPRESSION_METHOD_STORE = 0x00; -const COMPRESSION_METHOD_AES = 0x63; - -const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; -const SPLIT_ZIP_FILE_SIGNATURE = 0x08074b50; -const CENTRAL_FILE_HEADER_SIGNATURE = 0x02014b50; -const END_OF_CENTRAL_DIR_SIGNATURE = 0x06054b50; -const ZIP64_END_OF_CENTRAL_DIR_SIGNATURE = 0x06064b50; -const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE = 0x07064b50; -const END_OF_CENTRAL_DIR_LENGTH = 22; -const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH = 20; -const ZIP64_END_OF_CENTRAL_DIR_LENGTH = 56; - -const EXTRAFIELD_TYPE_ZIP64 = 0x0001; -const EXTRAFIELD_TYPE_AES = 0x9901; -const EXTRAFIELD_TYPE_NTFS = 0x000a; -const EXTRAFIELD_TYPE_NTFS_TAG1 = 0x0001; -const EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP = 0x5455; -const EXTRAFIELD_TYPE_UNICODE_PATH = 0x7075; -const EXTRAFIELD_TYPE_UNICODE_COMMENT = 0x6375; - -const BITFLAG_ENCRYPTED = 0x01; -const BITFLAG_LEVEL = 0x06; -const BITFLAG_DATA_DESCRIPTOR = 0x0008; -const BITFLAG_LANG_ENCODING_FLAG = 0x0800; -const FILE_ATTR_MSDOS_DIR_MASK = 0x10; - -const DIRECTORY_SIGNATURE = "/"; - -const UNDEFINED_VALUE = undefined; -const UNDEFINED_TYPE$1 = "undefined"; -const FUNCTION_TYPE$1 = "function"; - -/* - Copyright (c) 2022 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. - */ - -class StreamAdapter { - - constructor(Codec) { - return class extends TransformStream { - constructor(_format, options) { - const codec = new Codec(options); - super({ - transform(chunk, controller) { - controller.enqueue(codec.append(chunk)); - }, - flush(controller) { - const chunk = codec.flush(); - if (chunk) { - controller.enqueue(chunk); - } - } - }); - } - }; - } -} - -/* - Copyright (c) 2022 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. - */ - -const MINIMUM_CHUNK_SIZE = 64; -let maxWorkers = 2; -try { - if (typeof navigator != UNDEFINED_TYPE$1 && navigator.hardwareConcurrency) { - maxWorkers = navigator.hardwareConcurrency; - } -} catch (_error) { - // ignored -} -const DEFAULT_CONFIGURATION = { - chunkSize: 512 * 1024, - maxWorkers, - terminateWorkerTimeout: 5000, - useWebWorkers: true, - useCompressionStream: true, - workerScripts: UNDEFINED_VALUE, - CompressionStreamNative: typeof CompressionStream != UNDEFINED_TYPE$1 && CompressionStream, - DecompressionStreamNative: typeof DecompressionStream != UNDEFINED_TYPE$1 && DecompressionStream -}; - -const config = Object.assign({}, DEFAULT_CONFIGURATION); - -function getConfiguration() { - return config; -} - -function getChunkSize(config) { - return Math.max(config.chunkSize, MINIMUM_CHUNK_SIZE); -} - -function configure(configuration) { - const { - baseURL, - chunkSize, - maxWorkers, - terminateWorkerTimeout, - useCompressionStream, - useWebWorkers, - Deflate, - Inflate, - CompressionStream, - DecompressionStream, - workerScripts - } = configuration; - setIfDefined("baseURL", baseURL); - setIfDefined("chunkSize", chunkSize); - setIfDefined("maxWorkers", maxWorkers); - setIfDefined("terminateWorkerTimeout", terminateWorkerTimeout); - setIfDefined("useCompressionStream", useCompressionStream); - setIfDefined("useWebWorkers", useWebWorkers); - if (Deflate) { - config.CompressionStream = new StreamAdapter(Deflate); - } - if (Inflate) { - config.DecompressionStream = new StreamAdapter(Inflate); - } - setIfDefined("CompressionStream", CompressionStream); - setIfDefined("DecompressionStream", DecompressionStream); - if (workerScripts !== UNDEFINED_VALUE) { - const { deflate, inflate } = workerScripts; - if (deflate || inflate) { - if (!config.workerScripts) { - config.workerScripts = {}; - } - } - if (deflate) { - if (!Array.isArray(deflate)) { - throw new Error("workerScripts.deflate must be an array"); - } - config.workerScripts.deflate = deflate; - } - if (inflate) { - if (!Array.isArray(inflate)) { - throw new Error("workerScripts.inflate must be an array"); - } - config.workerScripts.inflate = inflate; - } - } -} - -function setIfDefined(propertyName, propertyValue) { - if (propertyValue !== UNDEFINED_VALUE) { - config[propertyName] = propertyValue; - } -} - -/* - Copyright (c) 2022 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. - */ - -const table$1 = { - "application": { - "andrew-inset": "ez", - "annodex": "anx", - "atom+xml": "atom", - "atomcat+xml": "atomcat", - "atomserv+xml": "atomsrv", - "bbolin": "lin", - "cap": ["cap", "pcap"], - "cu-seeme": "cu", - "davmount+xml": "davmount", - "dsptype": "tsp", - "ecmascript": ["es", "ecma"], - "futuresplash": "spl", - "hta": "hta", - "java-archive": "jar", - "java-serialized-object": "ser", - "java-vm": "class", - "javascript": "js", - "m3g": "m3g", - "mac-binhex40": "hqx", - "mathematica": ["nb", "ma", "mb"], - "msaccess": "mdb", - "msword": ["doc", "dot"], - "mxf": "mxf", - "oda": "oda", - "ogg": "ogx", - "pdf": "pdf", - "pgp-keys": "key", - "pgp-signature": ["asc", "sig"], - "pics-rules": "prf", - "postscript": ["ps", "ai", "eps", "epsi", "epsf", "eps2", "eps3"], - "rar": "rar", - "rdf+xml": "rdf", - "rss+xml": "rss", - "rtf": "rtf", - "smil": ["smi", "smil"], - "xhtml+xml": ["xhtml", "xht"], - "xml": ["xml", "xsl", "xsd"], - "xspf+xml": "xspf", - "zip": "zip", - "vnd.android.package-archive": "apk", - "vnd.cinderella": "cdy", - "vnd.google-earth.kml+xml": "kml", - "vnd.google-earth.kmz": "kmz", - "vnd.mozilla.xul+xml": "xul", - "vnd.ms-excel": ["xls", "xlb", "xlt", "xlm", "xla", "xlc", "xlw"], - "vnd.ms-pki.seccat": "cat", - "vnd.ms-pki.stl": "stl", - "vnd.ms-powerpoint": ["ppt", "pps", "pot"], - "vnd.oasis.opendocument.chart": "odc", - "vnd.oasis.opendocument.database": "odb", - "vnd.oasis.opendocument.formula": "odf", - "vnd.oasis.opendocument.graphics": "odg", - "vnd.oasis.opendocument.graphics-template": "otg", - "vnd.oasis.opendocument.image": "odi", - "vnd.oasis.opendocument.presentation": "odp", - "vnd.oasis.opendocument.presentation-template": "otp", - "vnd.oasis.opendocument.spreadsheet": "ods", - "vnd.oasis.opendocument.spreadsheet-template": "ots", - "vnd.oasis.opendocument.text": "odt", - "vnd.oasis.opendocument.text-master": "odm", - "vnd.oasis.opendocument.text-template": "ott", - "vnd.oasis.opendocument.text-web": "oth", - "vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx", - "vnd.openxmlformats-officedocument.spreadsheetml.template": "xltx", - "vnd.openxmlformats-officedocument.presentationml.presentation": "pptx", - "vnd.openxmlformats-officedocument.presentationml.slideshow": "ppsx", - "vnd.openxmlformats-officedocument.presentationml.template": "potx", - "vnd.openxmlformats-officedocument.wordprocessingml.document": "docx", - "vnd.openxmlformats-officedocument.wordprocessingml.template": "dotx", - "vnd.smaf": "mmf", - "vnd.stardivision.calc": "sdc", - "vnd.stardivision.chart": "sds", - "vnd.stardivision.draw": "sda", - "vnd.stardivision.impress": "sdd", - "vnd.stardivision.math": ["sdf", "smf"], - "vnd.stardivision.writer": ["sdw", "vor"], - "vnd.stardivision.writer-global": "sgl", - "vnd.sun.xml.calc": "sxc", - "vnd.sun.xml.calc.template": "stc", - "vnd.sun.xml.draw": "sxd", - "vnd.sun.xml.draw.template": "std", - "vnd.sun.xml.impress": "sxi", - "vnd.sun.xml.impress.template": "sti", - "vnd.sun.xml.math": "sxm", - "vnd.sun.xml.writer": "sxw", - "vnd.sun.xml.writer.global": "sxg", - "vnd.sun.xml.writer.template": "stw", - "vnd.symbian.install": ["sis", "sisx"], - "vnd.visio": ["vsd", "vst", "vss", "vsw"], - "vnd.wap.wbxml": "wbxml", - "vnd.wap.wmlc": "wmlc", - "vnd.wap.wmlscriptc": "wmlsc", - "vnd.wordperfect": "wpd", - "vnd.wordperfect5.1": "wp5", - "x-123": "wk", - "x-7z-compressed": "7z", - "x-abiword": "abw", - "x-apple-diskimage": "dmg", - "x-bcpio": "bcpio", - "x-bittorrent": "torrent", - "x-cbr": ["cbr", "cba", "cbt", "cb7"], - "x-cbz": "cbz", - "x-cdf": ["cdf", "cda"], - "x-cdlink": "vcd", - "x-chess-pgn": "pgn", - "x-cpio": "cpio", - "x-csh": "csh", - "x-debian-package": ["deb", "udeb"], - "x-director": ["dcr", "dir", "dxr", "cst", "cct", "cxt", "w3d", "fgd", "swa"], - "x-dms": "dms", - "x-doom": "wad", - "x-dvi": "dvi", - "x-httpd-eruby": "rhtml", - "x-font": "pcf.Z", - "x-freemind": "mm", - "x-gnumeric": "gnumeric", - "x-go-sgf": "sgf", - "x-graphing-calculator": "gcf", - "x-gtar": ["gtar", "taz"], - "x-hdf": "hdf", - "x-httpd-php": ["phtml", "pht", "php"], - "x-httpd-php-source": "phps", - "x-httpd-php3": "php3", - "x-httpd-php3-preprocessed": "php3p", - "x-httpd-php4": "php4", - "x-httpd-php5": "php5", - "x-ica": "ica", - "x-info": "info", - "x-internet-signup": ["ins", "isp"], - "x-iphone": "iii", - "x-iso9660-image": "iso", - "x-java-jnlp-file": "jnlp", - "x-jmol": "jmz", - "x-killustrator": "kil", - "x-koan": ["skp", "skd", "skt", "skm"], - "x-kpresenter": ["kpr", "kpt"], - "x-kword": ["kwd", "kwt"], - "x-latex": "latex", - "x-lha": "lha", - "x-lyx": "lyx", - "x-lzh": "lzh", - "x-lzx": "lzx", - "x-maker": ["frm", "maker", "frame", "fm", "fb", "book", "fbdoc"], - "x-ms-wmd": "wmd", - "x-ms-wmz": "wmz", - "x-msdos-program": ["com", "exe", "bat", "dll"], - "x-msi": "msi", - "x-netcdf": ["nc", "cdf"], - "x-ns-proxy-autoconfig": ["pac", "dat"], - "x-nwc": "nwc", - "x-object": "o", - "x-oz-application": "oza", - "x-pkcs7-certreqresp": "p7r", - "x-python-code": ["pyc", "pyo"], - "x-qgis": ["qgs", "shp", "shx"], - "x-quicktimeplayer": "qtl", - "x-redhat-package-manager": "rpm", - "x-ruby": "rb", - "x-sh": "sh", - "x-shar": "shar", - "x-shockwave-flash": ["swf", "swfl"], - "x-silverlight": "scr", - "x-stuffit": "sit", - "x-sv4cpio": "sv4cpio", - "x-sv4crc": "sv4crc", - "x-tar": "tar", - "x-tcl": "tcl", - "x-tex-gf": "gf", - "x-tex-pk": "pk", - "x-texinfo": ["texinfo", "texi"], - "x-trash": ["~", "%", "bak", "old", "sik"], - "x-troff": ["t", "tr", "roff"], - "x-troff-man": "man", - "x-troff-me": "me", - "x-troff-ms": "ms", - "x-ustar": "ustar", - "x-wais-source": "src", - "x-wingz": "wz", - "x-x509-ca-cert": ["crt", "der", "cer"], - "x-xcf": "xcf", - "x-xfig": "fig", - "x-xpinstall": "xpi", - "applixware": "aw", - "atomsvc+xml": "atomsvc", - "ccxml+xml": "ccxml", - "cdmi-capability": "cdmia", - "cdmi-container": "cdmic", - "cdmi-domain": "cdmid", - "cdmi-object": "cdmio", - "cdmi-queue": "cdmiq", - "docbook+xml": "dbk", - "dssc+der": "dssc", - "dssc+xml": "xdssc", - "emma+xml": "emma", - "epub+zip": "epub", - "exi": "exi", - "font-tdpfr": "pfr", - "gml+xml": "gml", - "gpx+xml": "gpx", - "gxf": "gxf", - "hyperstudio": "stk", - "inkml+xml": ["ink", "inkml"], - "ipfix": "ipfix", - "json": "json", - "jsonml+json": "jsonml", - "lost+xml": "lostxml", - "mads+xml": "mads", - "marc": "mrc", - "marcxml+xml": "mrcx", - "mathml+xml": "mathml", - "mbox": "mbox", - "mediaservercontrol+xml": "mscml", - "metalink+xml": "metalink", - "metalink4+xml": "meta4", - "mets+xml": "mets", - "mods+xml": "mods", - "mp21": ["m21", "mp21"], - "mp4": "mp4s", - "oebps-package+xml": "opf", - "omdoc+xml": "omdoc", - "onenote": ["onetoc", "onetoc2", "onetmp", "onepkg"], - "oxps": "oxps", - "patch-ops-error+xml": "xer", - "pgp-encrypted": "pgp", - "pkcs10": "p10", - "pkcs7-mime": ["p7m", "p7c"], - "pkcs7-signature": "p7s", - "pkcs8": "p8", - "pkix-attr-cert": "ac", - "pkix-crl": "crl", - "pkix-pkipath": "pkipath", - "pkixcmp": "pki", - "pls+xml": "pls", - "prs.cww": "cww", - "pskc+xml": "pskcxml", - "reginfo+xml": "rif", - "relax-ng-compact-syntax": "rnc", - "resource-lists+xml": "rl", - "resource-lists-diff+xml": "rld", - "rls-services+xml": "rs", - "rpki-ghostbusters": "gbr", - "rpki-manifest": "mft", - "rpki-roa": "roa", - "rsd+xml": "rsd", - "sbml+xml": "sbml", - "scvp-cv-request": "scq", - "scvp-cv-response": "scs", - "scvp-vp-request": "spq", - "scvp-vp-response": "spp", - "sdp": "sdp", - "set-payment-initiation": "setpay", - "set-registration-initiation": "setreg", - "shf+xml": "shf", - "sparql-query": "rq", - "sparql-results+xml": "srx", - "srgs": "gram", - "srgs+xml": "grxml", - "sru+xml": "sru", - "ssdl+xml": "ssdl", - "ssml+xml": "ssml", - "tei+xml": ["tei", "teicorpus"], - "thraud+xml": "tfi", - "timestamped-data": "tsd", - "vnd.3gpp.pic-bw-large": "plb", - "vnd.3gpp.pic-bw-small": "psb", - "vnd.3gpp.pic-bw-var": "pvb", - "vnd.3gpp2.tcap": "tcap", - "vnd.3m.post-it-notes": "pwn", - "vnd.accpac.simply.aso": "aso", - "vnd.accpac.simply.imp": "imp", - "vnd.acucobol": "acu", - "vnd.acucorp": ["atc", "acutc"], - "vnd.adobe.air-application-installer-package+zip": "air", - "vnd.adobe.formscentral.fcdt": "fcdt", - "vnd.adobe.fxp": ["fxp", "fxpl"], - "vnd.adobe.xdp+xml": "xdp", - "vnd.adobe.xfdf": "xfdf", - "vnd.ahead.space": "ahead", - "vnd.airzip.filesecure.azf": "azf", - "vnd.airzip.filesecure.azs": "azs", - "vnd.amazon.ebook": "azw", - "vnd.americandynamics.acc": "acc", - "vnd.amiga.ami": "ami", - "vnd.anser-web-certificate-issue-initiation": "cii", - "vnd.anser-web-funds-transfer-initiation": "fti", - "vnd.antix.game-component": "atx", - "vnd.apple.installer+xml": "mpkg", - "vnd.apple.mpegurl": "m3u8", - "vnd.aristanetworks.swi": "swi", - "vnd.astraea-software.iota": "iota", - "vnd.audiograph": "aep", - "vnd.blueice.multipass": "mpm", - "vnd.bmi": "bmi", - "vnd.businessobjects": "rep", - "vnd.chemdraw+xml": "cdxml", - "vnd.chipnuts.karaoke-mmd": "mmd", - "vnd.claymore": "cla", - "vnd.cloanto.rp9": "rp9", - "vnd.clonk.c4group": ["c4g", "c4d", "c4f", "c4p", "c4u"], - "vnd.cluetrust.cartomobile-config": "c11amc", - "vnd.cluetrust.cartomobile-config-pkg": "c11amz", - "vnd.commonspace": "csp", - "vnd.contact.cmsg": "cdbcmsg", - "vnd.cosmocaller": "cmc", - "vnd.crick.clicker": "clkx", - "vnd.crick.clicker.keyboard": "clkk", - "vnd.crick.clicker.palette": "clkp", - "vnd.crick.clicker.template": "clkt", - "vnd.crick.clicker.wordbank": "clkw", - "vnd.criticaltools.wbs+xml": "wbs", - "vnd.ctc-posml": "pml", - "vnd.cups-ppd": "ppd", - "vnd.curl.car": "car", - "vnd.curl.pcurl": "pcurl", - "vnd.dart": "dart", - "vnd.data-vision.rdz": "rdz", - "vnd.dece.data": ["uvf", "uvvf", "uvd", "uvvd"], - "vnd.dece.ttml+xml": ["uvt", "uvvt"], - "vnd.dece.unspecified": ["uvx", "uvvx"], - "vnd.dece.zip": ["uvz", "uvvz"], - "vnd.denovo.fcselayout-link": "fe_launch", - "vnd.dna": "dna", - "vnd.dolby.mlp": "mlp", - "vnd.dpgraph": "dpg", - "vnd.dreamfactory": "dfac", - "vnd.ds-keypoint": "kpxx", - "vnd.dvb.ait": "ait", - "vnd.dvb.service": "svc", - "vnd.dynageo": "geo", - "vnd.ecowin.chart": "mag", - "vnd.enliven": "nml", - "vnd.epson.esf": "esf", - "vnd.epson.msf": "msf", - "vnd.epson.quickanime": "qam", - "vnd.epson.salt": "slt", - "vnd.epson.ssf": "ssf", - "vnd.eszigno3+xml": ["es3", "et3"], - "vnd.ezpix-album": "ez2", - "vnd.ezpix-package": "ez3", - "vnd.fdf": "fdf", - "vnd.fdsn.mseed": "mseed", - "vnd.fdsn.seed": ["seed", "dataless"], - "vnd.flographit": "gph", - "vnd.fluxtime.clip": "ftc", - "vnd.framemaker": ["fm", "frame", "maker", "book"], - "vnd.frogans.fnc": "fnc", - "vnd.frogans.ltf": "ltf", - "vnd.fsc.weblaunch": "fsc", - "vnd.fujitsu.oasys": "oas", - "vnd.fujitsu.oasys2": "oa2", - "vnd.fujitsu.oasys3": "oa3", - "vnd.fujitsu.oasysgp": "fg5", - "vnd.fujitsu.oasysprs": "bh2", - "vnd.fujixerox.ddd": "ddd", - "vnd.fujixerox.docuworks": "xdw", - "vnd.fujixerox.docuworks.binder": "xbd", - "vnd.fuzzysheet": "fzs", - "vnd.genomatix.tuxedo": "txd", - "vnd.geogebra.file": "ggb", - "vnd.geogebra.tool": "ggt", - "vnd.geometry-explorer": ["gex", "gre"], - "vnd.geonext": "gxt", - "vnd.geoplan": "g2w", - "vnd.geospace": "g3w", - "vnd.gmx": "gmx", - "vnd.grafeq": ["gqf", "gqs"], - "vnd.groove-account": "gac", - "vnd.groove-help": "ghf", - "vnd.groove-identity-message": "gim", - "vnd.groove-injector": "grv", - "vnd.groove-tool-message": "gtm", - "vnd.groove-tool-template": "tpl", - "vnd.groove-vcard": "vcg", - "vnd.hal+xml": "hal", - "vnd.handheld-entertainment+xml": "zmm", - "vnd.hbci": "hbci", - "vnd.hhe.lesson-player": "les", - "vnd.hp-hpgl": "hpgl", - "vnd.hp-hpid": "hpid", - "vnd.hp-hps": "hps", - "vnd.hp-jlyt": "jlt", - "vnd.hp-pcl": "pcl", - "vnd.hp-pclxl": "pclxl", - "vnd.hydrostatix.sof-data": "sfd-hdstx", - "vnd.ibm.minipay": "mpy", - "vnd.ibm.modcap": ["afp", "listafp", "list3820"], - "vnd.ibm.rights-management": "irm", - "vnd.ibm.secure-container": "sc", - "vnd.iccprofile": ["icc", "icm"], - "vnd.igloader": "igl", - "vnd.immervision-ivp": "ivp", - "vnd.immervision-ivu": "ivu", - "vnd.insors.igm": "igm", - "vnd.intercon.formnet": ["xpw", "xpx"], - "vnd.intergeo": "i2g", - "vnd.intu.qbo": "qbo", - "vnd.intu.qfx": "qfx", - "vnd.ipunplugged.rcprofile": "rcprofile", - "vnd.irepository.package+xml": "irp", - "vnd.is-xpr": "xpr", - "vnd.isac.fcs": "fcs", - "vnd.jam": "jam", - "vnd.jcp.javame.midlet-rms": "rms", - "vnd.jisp": "jisp", - "vnd.joost.joda-archive": "joda", - "vnd.kahootz": ["ktz", "ktr"], - "vnd.kde.karbon": "karbon", - "vnd.kde.kchart": "chrt", - "vnd.kde.kformula": "kfo", - "vnd.kde.kivio": "flw", - "vnd.kde.kontour": "kon", - "vnd.kde.kpresenter": ["kpr", "kpt"], - "vnd.kde.kspread": "ksp", - "vnd.kde.kword": ["kwd", "kwt"], - "vnd.kenameaapp": "htke", - "vnd.kidspiration": "kia", - "vnd.kinar": ["kne", "knp"], - "vnd.koan": ["skp", "skd", "skt", "skm"], - "vnd.kodak-descriptor": "sse", - "vnd.las.las+xml": "lasxml", - "vnd.llamagraphics.life-balance.desktop": "lbd", - "vnd.llamagraphics.life-balance.exchange+xml": "lbe", - "vnd.lotus-1-2-3": "123", - "vnd.lotus-approach": "apr", - "vnd.lotus-freelance": "pre", - "vnd.lotus-notes": "nsf", - "vnd.lotus-organizer": "org", - "vnd.lotus-screencam": "scm", - "vnd.lotus-wordpro": "lwp", - "vnd.macports.portpkg": "portpkg", - "vnd.mcd": "mcd", - "vnd.medcalcdata": "mc1", - "vnd.mediastation.cdkey": "cdkey", - "vnd.mfer": "mwf", - "vnd.mfmp": "mfm", - "vnd.micrografx.flo": "flo", - "vnd.micrografx.igx": "igx", - "vnd.mif": "mif", - "vnd.mobius.daf": "daf", - "vnd.mobius.dis": "dis", - "vnd.mobius.mbk": "mbk", - "vnd.mobius.mqy": "mqy", - "vnd.mobius.msl": "msl", - "vnd.mobius.plc": "plc", - "vnd.mobius.txf": "txf", - "vnd.mophun.application": "mpn", - "vnd.mophun.certificate": "mpc", - "vnd.ms-artgalry": "cil", - "vnd.ms-cab-compressed": "cab", - "vnd.ms-excel.addin.macroenabled.12": "xlam", - "vnd.ms-excel.sheet.binary.macroenabled.12": "xlsb", - "vnd.ms-excel.sheet.macroenabled.12": "xlsm", - "vnd.ms-excel.template.macroenabled.12": "xltm", - "vnd.ms-fontobject": "eot", - "vnd.ms-htmlhelp": "chm", - "vnd.ms-ims": "ims", - "vnd.ms-lrm": "lrm", - "vnd.ms-officetheme": "thmx", - "vnd.ms-powerpoint.addin.macroenabled.12": "ppam", - "vnd.ms-powerpoint.presentation.macroenabled.12": "pptm", - "vnd.ms-powerpoint.slide.macroenabled.12": "sldm", - "vnd.ms-powerpoint.slideshow.macroenabled.12": "ppsm", - "vnd.ms-powerpoint.template.macroenabled.12": "potm", - "vnd.ms-project": ["mpp", "mpt"], - "vnd.ms-word.document.macroenabled.12": "docm", - "vnd.ms-word.template.macroenabled.12": "dotm", - "vnd.ms-works": ["wps", "wks", "wcm", "wdb"], - "vnd.ms-wpl": "wpl", - "vnd.ms-xpsdocument": "xps", - "vnd.mseq": "mseq", - "vnd.musician": "mus", - "vnd.muvee.style": "msty", - "vnd.mynfc": "taglet", - "vnd.neurolanguage.nlu": "nlu", - "vnd.nitf": ["ntf", "nitf"], - "vnd.noblenet-directory": "nnd", - "vnd.noblenet-sealer": "nns", - "vnd.noblenet-web": "nnw", - "vnd.nokia.n-gage.data": "ngdat", - "vnd.nokia.n-gage.symbian.install": "n-gage", - "vnd.nokia.radio-preset": "rpst", - "vnd.nokia.radio-presets": "rpss", - "vnd.novadigm.edm": "edm", - "vnd.novadigm.edx": "edx", - "vnd.novadigm.ext": "ext", - "vnd.oasis.opendocument.chart-template": "otc", - "vnd.oasis.opendocument.formula-template": "odft", - "vnd.oasis.opendocument.image-template": "oti", - "vnd.olpc-sugar": "xo", - "vnd.oma.dd2+xml": "dd2", - "vnd.openofficeorg.extension": "oxt", - "vnd.openxmlformats-officedocument.presentationml.slide": "sldx", - "vnd.osgeo.mapguide.package": "mgp", - "vnd.osgi.dp": "dp", - "vnd.osgi.subsystem": "esa", - "vnd.palm": ["pdb", "pqa", "oprc"], - "vnd.pawaafile": "paw", - "vnd.pg.format": "str", - "vnd.pg.osasli": "ei6", - "vnd.picsel": "efif", - "vnd.pmi.widget": "wg", - "vnd.pocketlearn": "plf", - "vnd.powerbuilder6": "pbd", - "vnd.previewsystems.box": "box", - "vnd.proteus.magazine": "mgz", - "vnd.publishare-delta-tree": "qps", - "vnd.pvi.ptid1": "ptid", - "vnd.quark.quarkxpress": ["qxd", "qxt", "qwd", "qwt", "qxl", "qxb"], - "vnd.realvnc.bed": "bed", - "vnd.recordare.musicxml": "mxl", - "vnd.recordare.musicxml+xml": "musicxml", - "vnd.rig.cryptonote": "cryptonote", - "vnd.rn-realmedia": "rm", - "vnd.rn-realmedia-vbr": "rmvb", - "vnd.route66.link66+xml": "link66", - "vnd.sailingtracker.track": "st", - "vnd.seemail": "see", - "vnd.sema": "sema", - "vnd.semd": "semd", - "vnd.semf": "semf", - "vnd.shana.informed.formdata": "ifm", - "vnd.shana.informed.formtemplate": "itp", - "vnd.shana.informed.interchange": "iif", - "vnd.shana.informed.package": "ipk", - "vnd.simtech-mindmapper": ["twd", "twds"], - "vnd.smart.teacher": "teacher", - "vnd.solent.sdkm+xml": ["sdkm", "sdkd"], - "vnd.spotfire.dxp": "dxp", - "vnd.spotfire.sfs": "sfs", - "vnd.stepmania.package": "smzip", - "vnd.stepmania.stepchart": "sm", - "vnd.sus-calendar": ["sus", "susp"], - "vnd.svd": "svd", - "vnd.syncml+xml": "xsm", - "vnd.syncml.dm+wbxml": "bdm", - "vnd.syncml.dm+xml": "xdm", - "vnd.tao.intent-module-archive": "tao", - "vnd.tcpdump.pcap": ["pcap", "cap", "dmp"], - "vnd.tmobile-livetv": "tmo", - "vnd.trid.tpt": "tpt", - "vnd.triscape.mxs": "mxs", - "vnd.trueapp": "tra", - "vnd.ufdl": ["ufd", "ufdl"], - "vnd.uiq.theme": "utz", - "vnd.umajin": "umj", - "vnd.unity": "unityweb", - "vnd.uoml+xml": "uoml", - "vnd.vcx": "vcx", - "vnd.visionary": "vis", - "vnd.vsf": "vsf", - "vnd.webturbo": "wtb", - "vnd.wolfram.player": "nbp", - "vnd.wqd": "wqd", - "vnd.wt.stf": "stf", - "vnd.xara": "xar", - "vnd.xfdl": "xfdl", - "vnd.yamaha.hv-dic": "hvd", - "vnd.yamaha.hv-script": "hvs", - "vnd.yamaha.hv-voice": "hvp", - "vnd.yamaha.openscoreformat": "osf", - "vnd.yamaha.openscoreformat.osfpvg+xml": "osfpvg", - "vnd.yamaha.smaf-audio": "saf", - "vnd.yamaha.smaf-phrase": "spf", - "vnd.yellowriver-custom-menu": "cmp", - "vnd.zul": ["zir", "zirz"], - "vnd.zzazz.deck+xml": "zaz", - "voicexml+xml": "vxml", - "widget": "wgt", - "winhlp": "hlp", - "wsdl+xml": "wsdl", - "wspolicy+xml": "wspolicy", - "x-ace-compressed": "ace", - "x-authorware-bin": ["aab", "x32", "u32", "vox"], - "x-authorware-map": "aam", - "x-authorware-seg": "aas", - "x-blorb": ["blb", "blorb"], - "x-bzip": "bz", - "x-bzip2": ["bz2", "boz"], - "x-cfs-compressed": "cfs", - "x-chat": "chat", - "x-conference": "nsc", - "x-dgc-compressed": "dgc", - "x-dtbncx+xml": "ncx", - "x-dtbook+xml": "dtb", - "x-dtbresource+xml": "res", - "x-eva": "eva", - "x-font-bdf": "bdf", - "x-font-ghostscript": "gsf", - "x-font-linux-psf": "psf", - "x-font-otf": "otf", - "x-font-pcf": "pcf", - "x-font-snf": "snf", - "x-font-ttf": ["ttf", "ttc"], - "x-font-type1": ["pfa", "pfb", "pfm", "afm"], - "x-font-woff": "woff", - "x-freearc": "arc", - "x-gca-compressed": "gca", - "x-glulx": "ulx", - "x-gramps-xml": "gramps", - "x-install-instructions": "install", - "x-lzh-compressed": ["lzh", "lha"], - "x-mie": "mie", - "x-mobipocket-ebook": ["prc", "mobi"], - "x-ms-application": "application", - "x-ms-shortcut": "lnk", - "x-ms-xbap": "xbap", - "x-msbinder": "obd", - "x-mscardfile": "crd", - "x-msclip": "clp", - "x-msdownload": ["exe", "dll", "com", "bat", "msi"], - "x-msmediaview": ["mvb", "m13", "m14"], - "x-msmetafile": ["wmf", "wmz", "emf", "emz"], - "x-msmoney": "mny", - "x-mspublisher": "pub", - "x-msschedule": "scd", - "x-msterminal": "trm", - "x-mswrite": "wri", - "x-nzb": "nzb", - "x-pkcs12": ["p12", "pfx"], - "x-pkcs7-certificates": ["p7b", "spc"], - "x-research-info-systems": "ris", - "x-silverlight-app": "xap", - "x-sql": "sql", - "x-stuffitx": "sitx", - "x-subrip": "srt", - "x-t3vm-image": "t3", - "x-tads": "gam", - "x-tex": "tex", - "x-tex-tfm": "tfm", - "x-tgif": "obj", - "x-xliff+xml": "xlf", - "x-xz": "xz", - "x-zmachine": ["z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8"], - "xaml+xml": "xaml", - "xcap-diff+xml": "xdf", - "xenc+xml": "xenc", - "xml-dtd": "dtd", - "xop+xml": "xop", - "xproc+xml": "xpl", - "xslt+xml": "xslt", - "xv+xml": ["mxml", "xhvml", "xvml", "xvm"], - "yang": "yang", - "yin+xml": "yin", - "envoy": "evy", - "fractals": "fif", - "internet-property-stream": "acx", - "olescript": "axs", - "vnd.ms-outlook": "msg", - "vnd.ms-pkicertstore": "sst", - "x-compress": "z", - "x-compressed": "tgz", - "x-gzip": "gz", - "x-perfmon": ["pma", "pmc", "pml", "pmr", "pmw"], - "x-pkcs7-mime": ["p7c", "p7m"], - "ynd.ms-pkipko": "pko" - }, - "audio": { - "amr": "amr", - "amr-wb": "awb", - "annodex": "axa", - "basic": ["au", "snd"], - "flac": "flac", - "midi": ["mid", "midi", "kar", "rmi"], - "mpeg": ["mpga", "mpega", "mp2", "mp3", "m4a", "mp2a", "m2a", "m3a"], - "mpegurl": "m3u", - "ogg": ["oga", "ogg", "spx"], - "prs.sid": "sid", - "x-aiff": ["aif", "aiff", "aifc"], - "x-gsm": "gsm", - "x-ms-wma": "wma", - "x-ms-wax": "wax", - "x-pn-realaudio": "ram", - "x-realaudio": "ra", - "x-sd2": "sd2", - "x-wav": "wav", - "adpcm": "adp", - "mp4": "mp4a", - "s3m": "s3m", - "silk": "sil", - "vnd.dece.audio": ["uva", "uvva"], - "vnd.digital-winds": "eol", - "vnd.dra": "dra", - "vnd.dts": "dts", - "vnd.dts.hd": "dtshd", - "vnd.lucent.voice": "lvp", - "vnd.ms-playready.media.pya": "pya", - "vnd.nuera.ecelp4800": "ecelp4800", - "vnd.nuera.ecelp7470": "ecelp7470", - "vnd.nuera.ecelp9600": "ecelp9600", - "vnd.rip": "rip", - "webm": "weba", - "x-aac": "aac", - "x-caf": "caf", - "x-matroska": "mka", - "x-pn-realaudio-plugin": "rmp", - "xm": "xm", - "mid": ["mid", "rmi"] - }, - "chemical": { - "x-alchemy": "alc", - "x-cache": ["cac", "cache"], - "x-cache-csf": "csf", - "x-cactvs-binary": ["cbin", "cascii", "ctab"], - "x-cdx": "cdx", - "x-chem3d": "c3d", - "x-cif": "cif", - "x-cmdf": "cmdf", - "x-cml": "cml", - "x-compass": "cpa", - "x-crossfire": "bsd", - "x-csml": ["csml", "csm"], - "x-ctx": "ctx", - "x-cxf": ["cxf", "cef"], - "x-embl-dl-nucleotide": ["emb", "embl"], - "x-gamess-input": ["inp", "gam", "gamin"], - "x-gaussian-checkpoint": ["fch", "fchk"], - "x-gaussian-cube": "cub", - "x-gaussian-input": ["gau", "gjc", "gjf"], - "x-gaussian-log": "gal", - "x-gcg8-sequence": "gcg", - "x-genbank": "gen", - "x-hin": "hin", - "x-isostar": ["istr", "ist"], - "x-jcamp-dx": ["jdx", "dx"], - "x-kinemage": "kin", - "x-macmolecule": "mcm", - "x-macromodel-input": ["mmd", "mmod"], - "x-mdl-molfile": "mol", - "x-mdl-rdfile": "rd", - "x-mdl-rxnfile": "rxn", - "x-mdl-sdfile": ["sd", "sdf"], - "x-mdl-tgf": "tgf", - "x-mmcif": "mcif", - "x-mol2": "mol2", - "x-molconn-Z": "b", - "x-mopac-graph": "gpt", - "x-mopac-input": ["mop", "mopcrt", "mpc", "zmt"], - "x-mopac-out": "moo", - "x-ncbi-asn1": "asn", - "x-ncbi-asn1-ascii": ["prt", "ent"], - "x-ncbi-asn1-binary": ["val", "aso"], - "x-pdb": ["pdb", "ent"], - "x-rosdal": "ros", - "x-swissprot": "sw", - "x-vamas-iso14976": "vms", - "x-vmd": "vmd", - "x-xtel": "xtel", - "x-xyz": "xyz" - }, - "image": { - "gif": "gif", - "ief": "ief", - "jpeg": ["jpeg", "jpg", "jpe"], - "pcx": "pcx", - "png": "png", - "svg+xml": ["svg", "svgz"], - "tiff": ["tiff", "tif"], - "vnd.djvu": ["djvu", "djv"], - "vnd.wap.wbmp": "wbmp", - "x-canon-cr2": "cr2", - "x-canon-crw": "crw", - "x-cmu-raster": "ras", - "x-coreldraw": "cdr", - "x-coreldrawpattern": "pat", - "x-coreldrawtemplate": "cdt", - "x-corelphotopaint": "cpt", - "x-epson-erf": "erf", - "x-icon": "ico", - "x-jg": "art", - "x-jng": "jng", - "x-nikon-nef": "nef", - "x-olympus-orf": "orf", - "x-photoshop": "psd", - "x-portable-anymap": "pnm", - "x-portable-bitmap": "pbm", - "x-portable-graymap": "pgm", - "x-portable-pixmap": "ppm", - "x-rgb": "rgb", - "x-xbitmap": "xbm", - "x-xpixmap": "xpm", - "x-xwindowdump": "xwd", - "bmp": "bmp", - "cgm": "cgm", - "g3fax": "g3", - "ktx": "ktx", - "prs.btif": "btif", - "sgi": "sgi", - "vnd.dece.graphic": ["uvi", "uvvi", "uvg", "uvvg"], - "vnd.dwg": "dwg", - "vnd.dxf": "dxf", - "vnd.fastbidsheet": "fbs", - "vnd.fpx": "fpx", - "vnd.fst": "fst", - "vnd.fujixerox.edmics-mmr": "mmr", - "vnd.fujixerox.edmics-rlc": "rlc", - "vnd.ms-modi": "mdi", - "vnd.ms-photo": "wdp", - "vnd.net-fpx": "npx", - "vnd.xiff": "xif", - "webp": "webp", - "x-3ds": "3ds", - "x-cmx": "cmx", - "x-freehand": ["fh", "fhc", "fh4", "fh5", "fh7"], - "x-pict": ["pic", "pct"], - "x-tga": "tga", - "cis-cod": "cod", - "pipeg": "jfif" - }, - "message": { - "rfc822": ["eml", "mime", "mht", "mhtml", "nws"] - }, - "model": { - "iges": ["igs", "iges"], - "mesh": ["msh", "mesh", "silo"], - "vrml": ["wrl", "vrml"], - "x3d+vrml": ["x3dv", "x3dvz"], - "x3d+xml": ["x3d", "x3dz"], - "x3d+binary": ["x3db", "x3dbz"], - "vnd.collada+xml": "dae", - "vnd.dwf": "dwf", - "vnd.gdl": "gdl", - "vnd.gtw": "gtw", - "vnd.mts": "mts", - "vnd.vtu": "vtu" - }, - "text": { - "cache-manifest": ["manifest", "appcache"], - "calendar": ["ics", "icz", "ifb"], - "css": "css", - "csv": "csv", - "h323": "323", - "html": ["html", "htm", "shtml", "stm"], - "iuls": "uls", - "mathml": "mml", - "plain": ["txt", "text", "brf", "conf", "def", "list", "log", "in", "bas"], - "richtext": "rtx", - "scriptlet": ["sct", "wsc"], - "texmacs": ["tm", "ts"], - "tab-separated-values": "tsv", - "vnd.sun.j2me.app-descriptor": "jad", - "vnd.wap.wml": "wml", - "vnd.wap.wmlscript": "wmls", - "x-bibtex": "bib", - "x-boo": "boo", - "x-c++hdr": ["h++", "hpp", "hxx", "hh"], - "x-c++src": ["c++", "cpp", "cxx", "cc"], - "x-component": "htc", - "x-dsrc": "d", - "x-diff": ["diff", "patch"], - "x-haskell": "hs", - "x-java": "java", - "x-literate-haskell": "lhs", - "x-moc": "moc", - "x-pascal": ["p", "pas"], - "x-pcs-gcd": "gcd", - "x-perl": ["pl", "pm"], - "x-python": "py", - "x-scala": "scala", - "x-setext": "etx", - "x-tcl": ["tcl", "tk"], - "x-tex": ["tex", "ltx", "sty", "cls"], - "x-vcalendar": "vcs", - "x-vcard": "vcf", - "n3": "n3", - "prs.lines.tag": "dsc", - "sgml": ["sgml", "sgm"], - "troff": ["t", "tr", "roff", "man", "me", "ms"], - "turtle": "ttl", - "uri-list": ["uri", "uris", "urls"], - "vcard": "vcard", - "vnd.curl": "curl", - "vnd.curl.dcurl": "dcurl", - "vnd.curl.scurl": "scurl", - "vnd.curl.mcurl": "mcurl", - "vnd.dvb.subtitle": "sub", - "vnd.fly": "fly", - "vnd.fmi.flexstor": "flx", - "vnd.graphviz": "gv", - "vnd.in3d.3dml": "3dml", - "vnd.in3d.spot": "spot", - "x-asm": ["s", "asm"], - "x-c": ["c", "cc", "cxx", "cpp", "h", "hh", "dic"], - "x-fortran": ["f", "for", "f77", "f90"], - "x-opml": "opml", - "x-nfo": "nfo", - "x-sfv": "sfv", - "x-uuencode": "uu", - "webviewhtml": "htt" - }, - "video": { - "avif": ".avif", - "3gpp": "3gp", - "annodex": "axv", - "dl": "dl", - "dv": ["dif", "dv"], - "fli": "fli", - "gl": "gl", - "mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v", "mp2", "mpa", "mpv2"], - "mp4": ["mp4", "mp4v", "mpg4"], - "quicktime": ["qt", "mov"], - "ogg": "ogv", - "vnd.mpegurl": ["mxu", "m4u"], - "x-flv": "flv", - "x-la-asf": ["lsf", "lsx"], - "x-mng": "mng", - "x-ms-asf": ["asf", "asx", "asr"], - "x-ms-wm": "wm", - "x-ms-wmv": "wmv", - "x-ms-wmx": "wmx", - "x-ms-wvx": "wvx", - "x-msvideo": "avi", - "x-sgi-movie": "movie", - "x-matroska": ["mpv", "mkv", "mk3d", "mks"], - "3gpp2": "3g2", - "h261": "h261", - "h263": "h263", - "h264": "h264", - "jpeg": "jpgv", - "jpm": ["jpm", "jpgm"], - "mj2": ["mj2", "mjp2"], - "vnd.dece.hd": ["uvh", "uvvh"], - "vnd.dece.mobile": ["uvm", "uvvm"], - "vnd.dece.pd": ["uvp", "uvvp"], - "vnd.dece.sd": ["uvs", "uvvs"], - "vnd.dece.video": ["uvv", "uvvv"], - "vnd.dvb.file": "dvb", - "vnd.fvt": "fvt", - "vnd.ms-playready.media.pyv": "pyv", - "vnd.uvvu.mp4": ["uvu", "uvvu"], - "vnd.vivo": "viv", - "webm": "webm", - "x-f4v": "f4v", - "x-m4v": "m4v", - "x-ms-vob": "vob", - "x-smv": "smv" - }, - "x-conference": { - "x-cooltalk": "ice" - }, - "x-world": { - "x-vrml": ["vrm", "vrml", "wrl", "flr", "wrz", "xaf", "xof"] - } -}; - -(() => { - const mimeTypes = {}; - for (const type in table$1) { - // eslint-disable-next-line no-prototype-builtins - if (table$1.hasOwnProperty(type)) { - for (const subtype in table$1[type]) { - // eslint-disable-next-line no-prototype-builtins - if (table$1[type].hasOwnProperty(subtype)) { - const value = table$1[type][subtype]; - if (typeof value == "string") { - mimeTypes[value] = type + "/" + subtype; - } else { - for (let indexMimeType = 0; indexMimeType < value.length; indexMimeType++) { - mimeTypes[value[indexMimeType]] = type + "/" + subtype; - } - } - } - } - } - } - return mimeTypes; -})(); - -/* - Copyright (c) 2022 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. - */ - -const table = []; -for (let i = 0; i < 256; i++) { - let t = i; - for (let j = 0; j < 8; j++) { - if (t & 1) { - t = (t >>> 1) ^ 0xEDB88320; - } else { - t = t >>> 1; - } - } - table[i] = t; -} - -class Crc32 { - - constructor(crc) { - this.crc = crc || -1; - } - - append(data) { - let crc = this.crc | 0; - for (let offset = 0, length = data.length | 0; offset < length; offset++) { - crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF]; - } - this.crc = crc; - } - - get() { - return ~this.crc; - } -} - -/* - Copyright (c) 2022 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. - */ - -class Crc32Stream extends TransformStream { - - constructor() { - const crc32 = new Crc32(); - super({ - transform(chunk) { - crc32.append(chunk); - }, - flush(controller) { - const value = new Uint8Array(4); - const dataView = new DataView(value.buffer); - dataView.setUint32(0, crc32.get()); - controller.enqueue(value); - } - }); - } -} - -/* - Copyright (c) 2022 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 encodeText(value) { - if (typeof TextEncoder == "undefined") { - value = unescape(encodeURIComponent(value)); - const result = new Uint8Array(value.length); - for (let i = 0; i < result.length; i++) { - result[i] = value.charCodeAt(i); - } - return result; - } else { - return new TextEncoder().encode(value); - } -} - -// Derived from https://github.com/xqdoo00o/jszip/blob/master/lib/sjcl.js and https://github.com/bitwiseshiftleft/sjcl - -// deno-lint-ignore-file no-this-alias - -/* - * SJCL is open. You can use, modify and redistribute it under a BSD - * license or under the GNU GPL, version 2.0. - */ - -/** @fileOverview Javascript cryptography implementation. - * - * Crush to remove comments, shorten variable names and - * generally reduce transmission size. - * - * @author Emily Stark - * @author Mike Hamburg - * @author Dan Boneh - */ - -/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */ - -/** @fileOverview Arrays of bits, encoded as arrays of Numbers. - * - * @author Emily Stark - * @author Mike Hamburg - * @author Dan Boneh - */ - -/** - * Arrays of bits, encoded as arrays of Numbers. - * @namespace - * @description - *

- * These objects are the currency accepted by SJCL's crypto functions. - *

- * - *

- * Most of our crypto primitives operate on arrays of 4-byte words internally, - * but many of them can take arguments that are not a multiple of 4 bytes. - * This library encodes arrays of bits (whose size need not be a multiple of 8 - * bits) as arrays of 32-bit words. The bits are packed, big-endian, into an - * array of words, 32 bits at a time. Since the words are double-precision - * floating point numbers, they fit some extra data. We use this (in a private, - * possibly-changing manner) to encode the number of bits actually present - * in the last word of the array. - *

- * - *

- * Because bitwise ops clear this out-of-band data, these arrays can be passed - * to ciphers like AES which want arrays of words. - *

- */ -const bitArray = { - /** - * Concatenate two bit arrays. - * @param {bitArray} a1 The first array. - * @param {bitArray} a2 The second array. - * @return {bitArray} The concatenation of a1 and a2. - */ - concat(a1, a2) { - if (a1.length === 0 || a2.length === 0) { - return a1.concat(a2); - } - - const last = a1[a1.length - 1], shift = bitArray.getPartial(last); - if (shift === 32) { - return a1.concat(a2); - } else { - return bitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1)); - } - }, - - /** - * Find the length of an array of bits. - * @param {bitArray} a The array. - * @return {Number} The length of a, in bits. - */ - bitLength(a) { - const l = a.length; - if (l === 0) { - return 0; - } - const x = a[l - 1]; - return (l - 1) * 32 + bitArray.getPartial(x); - }, - - /** - * Truncate an array. - * @param {bitArray} a The array. - * @param {Number} len The length to truncate to, in bits. - * @return {bitArray} A new array, truncated to len bits. - */ - clamp(a, len) { - if (a.length * 32 < len) { - return a; - } - a = a.slice(0, Math.ceil(len / 32)); - const l = a.length; - len = len & 31; - if (l > 0 && len) { - a[l - 1] = bitArray.partial(len, a[l - 1] & 0x80000000 >> (len - 1), 1); - } - return a; - }, - - /** - * Make a partial word for a bit array. - * @param {Number} len The number of bits in the word. - * @param {Number} x The bits. - * @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side. - * @return {Number} The partial word. - */ - partial(len, x, _end) { - if (len === 32) { - return x; - } - return (_end ? x | 0 : x << (32 - len)) + len * 0x10000000000; - }, - - /** - * Get the number of bits used by a partial word. - * @param {Number} x The partial word. - * @return {Number} The number of bits used by the partial word. - */ - getPartial(x) { - return Math.round(x / 0x10000000000) || 32; - }, - - /** Shift an array right. - * @param {bitArray} a The array to shift. - * @param {Number} shift The number of bits to shift. - * @param {Number} [carry=0] A byte to carry in - * @param {bitArray} [out=[]] An array to prepend to the output. - * @private - */ - _shiftRight(a, shift, carry, out) { - if (out === undefined) { - out = []; - } - - for (; shift >= 32; shift -= 32) { - out.push(carry); - carry = 0; - } - if (shift === 0) { - return out.concat(a); - } - - for (let i = 0; i < a.length; i++) { - out.push(carry | a[i] >>> shift); - carry = a[i] << (32 - shift); - } - const last2 = a.length ? a[a.length - 1] : 0; - const shift2 = bitArray.getPartial(last2); - out.push(bitArray.partial(shift + shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(), 1)); - return out; - } -}; - -/** @fileOverview Bit array codec implementations. - * - * @author Emily Stark - * @author Mike Hamburg - * @author Dan Boneh - */ - -/** - * Arrays of bytes - * @namespace - */ -const codec = { - bytes: { - /** Convert from a bitArray to an array of bytes. */ - fromBits(arr) { - const bl = bitArray.bitLength(arr); - const byteLength = bl / 8; - const out = new Uint8Array(byteLength); - let tmp; - for (let i = 0; i < byteLength; i++) { - if ((i & 3) === 0) { - tmp = arr[i / 4]; - } - out[i] = tmp >>> 24; - tmp <<= 8; - } - return out; - }, - /** Convert from an array of bytes to a bitArray. */ - toBits(bytes) { - const out = []; - let i; - let tmp = 0; - for (i = 0; i < bytes.length; i++) { - tmp = tmp << 8 | bytes[i]; - if ((i & 3) === 3) { - out.push(tmp); - tmp = 0; - } - } - if (i & 3) { - out.push(bitArray.partial(8 * (i & 3), tmp)); - } - return out; - } - } -}; - -const hash = {}; - -/** - * Context for a SHA-1 operation in progress. - * @constructor - */ -hash.sha1 = class { - constructor(hash) { - const sha1 = this; - /** - * The hash's block size, in bits. - * @constant - */ - sha1.blockSize = 512; - /** - * The SHA-1 initialization vector. - * @private - */ - sha1._init = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]; - /** - * The SHA-1 hash key. - * @private - */ - sha1._key = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6]; - if (hash) { - sha1._h = hash._h.slice(0); - sha1._buffer = hash._buffer.slice(0); - sha1._length = hash._length; - } else { - sha1.reset(); - } - } - - /** - * Reset the hash state. - * @return this - */ - reset() { - const sha1 = this; - sha1._h = sha1._init.slice(0); - sha1._buffer = []; - sha1._length = 0; - return sha1; - } - - /** - * Input several words to the hash. - * @param {bitArray|String} data the data to hash. - * @return this - */ - update(data) { - const sha1 = this; - if (typeof data === "string") { - data = codec.utf8String.toBits(data); - } - const b = sha1._buffer = bitArray.concat(sha1._buffer, data); - const ol = sha1._length; - const nl = sha1._length = ol + bitArray.bitLength(data); - if (nl > 9007199254740991) { - throw new Error("Cannot hash more than 2^53 - 1 bits"); - } - const c = new Uint32Array(b); - let j = 0; - for (let i = sha1.blockSize + ol - ((sha1.blockSize + ol) & (sha1.blockSize - 1)); i <= nl; - i += sha1.blockSize) { - sha1._block(c.subarray(16 * j, 16 * (j + 1))); - j += 1; - } - b.splice(0, 16 * j); - return sha1; - } - - /** - * Complete hashing and output the hash value. - * @return {bitArray} The hash value, an array of 5 big-endian words. TODO - */ - finalize() { - const sha1 = this; - let b = sha1._buffer; - const h = sha1._h; - - // Round out and push the buffer - b = bitArray.concat(b, [bitArray.partial(1, 1)]); - // Round out the buffer to a multiple of 16 words, less the 2 length words. - for (let i = b.length + 2; i & 15; i++) { - b.push(0); - } - - // append the length - b.push(Math.floor(sha1._length / 0x100000000)); - b.push(sha1._length | 0); - - while (b.length) { - sha1._block(b.splice(0, 16)); - } - - sha1.reset(); - return h; - } - - /** - * The SHA-1 logical functions f(0), f(1), ..., f(79). - * @private - */ - _f(t, b, c, d) { - if (t <= 19) { - return (b & c) | (~b & d); - } else if (t <= 39) { - return b ^ c ^ d; - } else if (t <= 59) { - return (b & c) | (b & d) | (c & d); - } else if (t <= 79) { - return b ^ c ^ d; - } - } - - /** - * Circular left-shift operator. - * @private - */ - _S(n, x) { - return (x << n) | (x >>> 32 - n); - } - - /** - * Perform one cycle of SHA-1. - * @param {Uint32Array|bitArray} words one block of words. - * @private - */ - _block(words) { - const sha1 = this; - const h = sha1._h; - // When words is passed to _block, it has 16 elements. SHA1 _block - // function extends words with new elements (at the end there are 80 elements). - // The problem is that if we use Uint32Array instead of Array, - // the length of Uint32Array cannot be changed. Thus, we replace words with a - // normal Array here. - const w = Array(80); // do not use Uint32Array here as the instantiation is slower - for (let j = 0; j < 16; j++) { - w[j] = words[j]; - } - - let a = h[0]; - let b = h[1]; - let c = h[2]; - let d = h[3]; - let e = h[4]; - - for (let t = 0; t <= 79; t++) { - if (t >= 16) { - w[t] = sha1._S(1, w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]); - } - const tmp = (sha1._S(5, a) + sha1._f(t, b, c, d) + e + w[t] + - sha1._key[Math.floor(t / 20)]) | 0; - e = d; - d = c; - c = sha1._S(30, b); - b = a; - a = tmp; - } - - h[0] = (h[0] + a) | 0; - h[1] = (h[1] + b) | 0; - h[2] = (h[2] + c) | 0; - h[3] = (h[3] + d) | 0; - h[4] = (h[4] + e) | 0; - } -}; - -/** @fileOverview Low-level AES implementation. - * - * This file contains a low-level implementation of AES, optimized for - * size and for efficiency on several browsers. It is based on - * OpenSSL's aes_core.c, a public-domain implementation by Vincent - * Rijmen, Antoon Bosselaers and Paulo Barreto. - * - * An older version of this implementation is available in the public - * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh, - * Stanford University 2008-2010 and BSD-licensed for liability - * reasons. - * - * @author Emily Stark - * @author Mike Hamburg - * @author Dan Boneh - */ - -const cipher = {}; - -/** - * Schedule out an AES key for both encryption and decryption. This - * is a low-level class. Use a cipher mode to do bulk encryption. - * - * @constructor - * @param {Array} key The key as an array of 4, 6 or 8 words. - */ -cipher.aes = class { - constructor(key) { - /** - * The expanded S-box and inverse S-box tables. These will be computed - * on the client so that we don't have to send them down the wire. - * - * There are two tables, _tables[0] is for encryption and - * _tables[1] is for decryption. - * - * The first 4 sub-tables are the expanded S-box with MixColumns. The - * last (_tables[01][4]) is the S-box itself. - * - * @private - */ - const aes = this; - aes._tables = [[[], [], [], [], []], [[], [], [], [], []]]; - - if (!aes._tables[0][0][0]) { - aes._precompute(); - } - - const sbox = aes._tables[0][4]; - const decTable = aes._tables[1]; - const keyLen = key.length; - - let i, encKey, decKey, rcon = 1; - - if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { - throw new Error("invalid aes key size"); - } - - aes._key = [encKey = key.slice(0), decKey = []]; - - // schedule encryption keys - for (i = keyLen; i < 4 * keyLen + 28; i++) { - let tmp = encKey[i - 1]; - - // apply sbox - if (i % keyLen === 0 || (keyLen === 8 && i % keyLen === 4)) { - tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; - - // shift rows and add rcon - if (i % keyLen === 0) { - tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24; - rcon = rcon << 1 ^ (rcon >> 7) * 283; - } - } - - encKey[i] = encKey[i - keyLen] ^ tmp; - } - - // schedule decryption keys - for (let j = 0; i; j++, i--) { - const tmp = encKey[j & 3 ? i : i - 4]; - if (i <= 4 || j < 4) { - decKey[j] = tmp; - } else { - decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ - decTable[1][sbox[tmp >> 16 & 255]] ^ - decTable[2][sbox[tmp >> 8 & 255]] ^ - decTable[3][sbox[tmp & 255]]; - } - } - } - // public - /* Something like this might appear here eventually - name: "AES", - blockSize: 4, - keySizes: [4,6,8], - */ - - /** - * Encrypt an array of 4 big-endian words. - * @param {Array} data The plaintext. - * @return {Array} The ciphertext. - */ - encrypt(data) { - return this._crypt(data, 0); - } - - /** - * Decrypt an array of 4 big-endian words. - * @param {Array} data The ciphertext. - * @return {Array} The plaintext. - */ - decrypt(data) { - return this._crypt(data, 1); - } - - /** - * Expand the S-box tables. - * - * @private - */ - _precompute() { - const encTable = this._tables[0]; - const decTable = this._tables[1]; - const sbox = encTable[4]; - const sboxInv = decTable[4]; - const d = []; - const th = []; - let xInv, x2, x4, x8; - - // Compute double and third tables - for (let i = 0; i < 256; i++) { - th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i; - } - - for (let x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { - // Compute sbox - let s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4; - s = s >> 8 ^ s & 255 ^ 99; - sbox[x] = s; - sboxInv[s] = x; - - // Compute MixColumns - x8 = d[x4 = d[x2 = d[x]]]; - let tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; - let tEnc = d[s] * 0x101 ^ s * 0x1010100; - - for (let i = 0; i < 4; i++) { - encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8; - decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8; - } - } - - // Compactify. Considerable speedup on Firefox. - for (let i = 0; i < 5; i++) { - encTable[i] = encTable[i].slice(0); - decTable[i] = decTable[i].slice(0); - } - } - - /** - * Encryption and decryption core. - * @param {Array} input Four words to be encrypted or decrypted. - * @param dir The direction, 0 for encrypt and 1 for decrypt. - * @return {Array} The four encrypted or decrypted words. - * @private - */ - _crypt(input, dir) { - if (input.length !== 4) { - throw new Error("invalid aes block size"); - } - - const key = this._key[dir]; - - const nInnerRounds = key.length / 4 - 2; - const out = [0, 0, 0, 0]; - const table = this._tables[dir]; - - // load up the tables - const t0 = table[0]; - const t1 = table[1]; - const t2 = table[2]; - const t3 = table[3]; - const sbox = table[4]; - - // state variables a,b,c,d are loaded with pre-whitened data - let a = input[0] ^ key[0]; - let b = input[dir ? 3 : 1] ^ key[1]; - let c = input[2] ^ key[2]; - let d = input[dir ? 1 : 3] ^ key[3]; - let kIndex = 4; - let a2, b2, c2; - - // Inner rounds. Cribbed from OpenSSL. - for (let i = 0; i < nInnerRounds; i++) { - a2 = t0[a >>> 24] ^ t1[b >> 16 & 255] ^ t2[c >> 8 & 255] ^ t3[d & 255] ^ key[kIndex]; - b2 = t0[b >>> 24] ^ t1[c >> 16 & 255] ^ t2[d >> 8 & 255] ^ t3[a & 255] ^ key[kIndex + 1]; - c2 = t0[c >>> 24] ^ t1[d >> 16 & 255] ^ t2[a >> 8 & 255] ^ t3[b & 255] ^ key[kIndex + 2]; - d = t0[d >>> 24] ^ t1[a >> 16 & 255] ^ t2[b >> 8 & 255] ^ t3[c & 255] ^ key[kIndex + 3]; - kIndex += 4; - a = a2; b = b2; c = c2; - } - - // Last round. - for (let i = 0; i < 4; i++) { - out[dir ? 3 & -i : i] = - sbox[a >>> 24] << 24 ^ - sbox[b >> 16 & 255] << 16 ^ - sbox[c >> 8 & 255] << 8 ^ - sbox[d & 255] ^ - key[kIndex++]; - a2 = a; a = b; b = c; c = d; d = a2; - } - - return out; - } -}; - -/** - * Random values - * @namespace - */ -const random = { - /** - * Generate random words with pure js, cryptographically not as strong & safe as native implementation. - * @param {TypedArray} typedArray The array to fill. - * @return {TypedArray} The random values. - */ - getRandomValues(typedArray) { - const words = new Uint32Array(typedArray.buffer); - const r = (m_w) => { - let m_z = 0x3ade68b1; - const mask = 0xffffffff; - return function () { - m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; - m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; - const result = ((((m_z << 0x10) + m_w) & mask) / 0x100000000) + .5; - return result * (Math.random() > .5 ? 1 : -1); - }; - }; - for (let i = 0, rcache; i < typedArray.length; i += 4) { - const _r = r((rcache || Math.random()) * 0x100000000); - rcache = _r() * 0x3ade67b7; - words[i / 4] = (_r() * 0x100000000) | 0; - } - return typedArray; - } -}; - -/** @fileOverview CTR mode implementation. - * - * Special thanks to Roy Nicholson for pointing out a bug in our - * implementation. - * - * @author Emily Stark - * @author Mike Hamburg - * @author Dan Boneh - */ - -/** Brian Gladman's CTR Mode. -* @constructor -* @param {Object} _prf The aes instance to generate key. -* @param {bitArray} _iv The iv for ctr mode, it must be 128 bits. -*/ - -const mode = {}; - -/** - * Brian Gladman's CTR Mode. - * @namespace - */ -mode.ctrGladman = class { - constructor(prf, iv) { - this._prf = prf; - this._initIv = iv; - this._iv = iv; - } - - reset() { - this._iv = this._initIv; - } - - /** Input some data to calculate. - * @param {bitArray} data the data to process, it must be intergral multiple of 128 bits unless it's the last. - */ - update(data) { - return this.calculate(this._prf, data, this._iv); - } - - incWord(word) { - if (((word >> 24) & 0xff) === 0xff) { //overflow - let b1 = (word >> 16) & 0xff; - let b2 = (word >> 8) & 0xff; - let b3 = word & 0xff; - - if (b1 === 0xff) { // overflow b1 - b1 = 0; - if (b2 === 0xff) { - b2 = 0; - if (b3 === 0xff) { - b3 = 0; - } else { - ++b3; - } - } else { - ++b2; - } - } else { - ++b1; - } - - word = 0; - word += (b1 << 16); - word += (b2 << 8); - word += b3; - } else { - word += (0x01 << 24); - } - return word; - } - - incCounter(counter) { - if ((counter[0] = this.incWord(counter[0])) === 0) { - // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8 - counter[1] = this.incWord(counter[1]); - } - } - - calculate(prf, data, iv) { - let l; - if (!(l = data.length)) { - return []; - } - const bl = bitArray.bitLength(data); - for (let i = 0; i < l; i += 4) { - this.incCounter(iv); - const e = prf.encrypt(iv); - data[i] ^= e[0]; - data[i + 1] ^= e[1]; - data[i + 2] ^= e[2]; - data[i + 3] ^= e[3]; - } - return bitArray.clamp(data, bl); - } -}; - -const misc = { - importKey(password) { - return new misc.hmacSha1(codec.bytes.toBits(password)); - }, - pbkdf2(prf, salt, count, length) { - count = count || 10000; - if (length < 0 || count < 0) { - throw new Error("invalid params to pbkdf2"); - } - const byteLength = ((length >> 5) + 1) << 2; - let u, ui, i, j, k; - const arrayBuffer = new ArrayBuffer(byteLength); - const out = new DataView(arrayBuffer); - let outLength = 0; - const b = bitArray; - salt = codec.bytes.toBits(salt); - for (k = 1; outLength < (byteLength || 1); k++) { - u = ui = prf.encrypt(b.concat(salt, [k])); - for (i = 1; i < count; i++) { - ui = prf.encrypt(ui); - for (j = 0; j < ui.length; j++) { - u[j] ^= ui[j]; - } - } - for (i = 0; outLength < (byteLength || 1) && i < u.length; i++) { - out.setInt32(outLength, u[i]); - outLength += 4; - } - } - return arrayBuffer.slice(0, length / 8); - } -}; - -/** @fileOverview HMAC implementation. - * - * @author Emily Stark - * @author Mike Hamburg - * @author Dan Boneh - */ - -/** HMAC with the specified hash function. - * @constructor - * @param {bitArray} key the key for HMAC. - * @param {Object} [Hash=hash.sha1] The hash function to use. - */ -misc.hmacSha1 = class { - - constructor(key) { - const hmac = this; - const Hash = hmac._hash = hash.sha1; - const exKey = [[], []]; - hmac._baseHash = [new Hash(), new Hash()]; - const bs = hmac._baseHash[0].blockSize / 32; - - if (key.length > bs) { - key = new Hash().update(key).finalize(); - } - - for (let i = 0; i < bs; i++) { - exKey[0][i] = key[i] ^ 0x36363636; - exKey[1][i] = key[i] ^ 0x5C5C5C5C; - } - - hmac._baseHash[0].update(exKey[0]); - hmac._baseHash[1].update(exKey[1]); - hmac._resultHash = new Hash(hmac._baseHash[0]); - } - reset() { - const hmac = this; - hmac._resultHash = new hmac._hash(hmac._baseHash[0]); - hmac._updated = false; - } - - update(data) { - const hmac = this; - hmac._updated = true; - hmac._resultHash.update(data); - } - - digest() { - const hmac = this; - const w = hmac._resultHash.finalize(); - const result = new (hmac._hash)(hmac._baseHash[1]).update(w).finalize(); - - hmac.reset(); - - return result; - } - - encrypt(data) { - if (!this._updated) { - this.update(data); - return this.digest(data); - } else { - throw new Error("encrypt on already updated hmac called!"); - } - } -}; - -/* - Copyright (c) 2022 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. - */ - -const GET_RANDOM_VALUES_SUPPORTED = typeof crypto != "undefined" && typeof crypto.getRandomValues == "function"; - -const ERR_INVALID_PASSWORD = "Invalid password"; -const ERR_INVALID_SIGNATURE = "Invalid signature"; -const ERR_ABORT_CHECK_PASSWORD = "zipjs-abort-check-password"; - -function getRandomValues(array) { - if (GET_RANDOM_VALUES_SUPPORTED) { - return crypto.getRandomValues(array); - } else { - return random.getRandomValues(array); - } -} - -/* - Copyright (c) 2022 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. - */ - -const BLOCK_LENGTH = 16; -const RAW_FORMAT = "raw"; -const PBKDF2_ALGORITHM = { name: "PBKDF2" }; -const HASH_ALGORITHM = { name: "HMAC" }; -const HASH_FUNCTION = "SHA-1"; -const BASE_KEY_ALGORITHM = Object.assign({ hash: HASH_ALGORITHM }, PBKDF2_ALGORITHM); -const DERIVED_BITS_ALGORITHM = Object.assign({ iterations: 1000, hash: { name: HASH_FUNCTION } }, PBKDF2_ALGORITHM); -const DERIVED_BITS_USAGE = ["deriveBits"]; -const SALT_LENGTH = [8, 12, 16]; -const KEY_LENGTH = [16, 24, 32]; -const SIGNATURE_LENGTH = 10; -const COUNTER_DEFAULT_VALUE = [0, 0, 0, 0]; -const UNDEFINED_TYPE = "undefined"; -const FUNCTION_TYPE = "function"; -// deno-lint-ignore valid-typeof -const CRYPTO_API_SUPPORTED = typeof crypto != UNDEFINED_TYPE; -const subtle = CRYPTO_API_SUPPORTED && crypto.subtle; -const SUBTLE_API_SUPPORTED = CRYPTO_API_SUPPORTED && typeof subtle != UNDEFINED_TYPE; -const codecBytes = codec.bytes; -const Aes = cipher.aes; -const CtrGladman = mode.ctrGladman; -const HmacSha1 = misc.hmacSha1; - -let IMPORT_KEY_SUPPORTED = CRYPTO_API_SUPPORTED && SUBTLE_API_SUPPORTED && typeof subtle.importKey == FUNCTION_TYPE; -let DERIVE_BITS_SUPPORTED = CRYPTO_API_SUPPORTED && SUBTLE_API_SUPPORTED && typeof subtle.deriveBits == FUNCTION_TYPE; - -class AESDecryptionStream extends TransformStream { - - constructor({ password, signed, encryptionStrength, checkPasswordOnly }) { - super({ - start() { - Object.assign(this, { - ready: new Promise(resolve => this.resolveReady = resolve), - password, - signed, - strength: encryptionStrength - 1, - pending: new Uint8Array() - }); - }, - async transform(chunk, controller) { - const aesCrypto = this; - const { - password, - strength, - resolveReady, - ready - } = aesCrypto; - if (password) { - await createDecryptionKeys(aesCrypto, strength, password, subarray(chunk, 0, SALT_LENGTH[strength] + 2)); - chunk = subarray(chunk, SALT_LENGTH[strength] + 2); - if (checkPasswordOnly) { - controller.error(new Error(ERR_ABORT_CHECK_PASSWORD)); - } else { - resolveReady(); - } - } else { - await ready; - } - const output = new Uint8Array(chunk.length - SIGNATURE_LENGTH - ((chunk.length - SIGNATURE_LENGTH) % BLOCK_LENGTH)); - controller.enqueue(append(aesCrypto, chunk, output, 0, SIGNATURE_LENGTH, true)); - }, - async flush(controller) { - const { - signed, - ctr, - hmac, - pending, - ready - } = this; - await ready; - const chunkToDecrypt = subarray(pending, 0, pending.length - SIGNATURE_LENGTH); - const originalSignature = subarray(pending, pending.length - SIGNATURE_LENGTH); - let decryptedChunkArray = new Uint8Array(); - if (chunkToDecrypt.length) { - const encryptedChunk = toBits(codecBytes, chunkToDecrypt); - hmac.update(encryptedChunk); - const decryptedChunk = ctr.update(encryptedChunk); - decryptedChunkArray = fromBits(codecBytes, decryptedChunk); - } - if (signed) { - const signature = subarray(fromBits(codecBytes, hmac.digest()), 0, SIGNATURE_LENGTH); - for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) { - if (signature[indexSignature] != originalSignature[indexSignature]) { - throw new Error(ERR_INVALID_SIGNATURE); - } - } - } - controller.enqueue(decryptedChunkArray); - } - }); - } -} - -class AESEncryptionStream extends TransformStream { - - constructor({ password, encryptionStrength }) { - // deno-lint-ignore prefer-const - let stream; - super({ - start() { - Object.assign(this, { - ready: new Promise(resolve => this.resolveReady = resolve), - password, - strength: encryptionStrength - 1, - pending: new Uint8Array() - }); - }, - async transform(chunk, controller) { - const aesCrypto = this; - const { - password, - strength, - resolveReady, - ready - } = aesCrypto; - let preamble = new Uint8Array(); - if (password) { - preamble = await createEncryptionKeys(aesCrypto, strength, password); - resolveReady(); - } else { - await ready; - } - const output = new Uint8Array(preamble.length + chunk.length - (chunk.length % BLOCK_LENGTH)); - output.set(preamble, 0); - controller.enqueue(append(aesCrypto, chunk, output, preamble.length, 0)); - }, - async flush(controller) { - const { - ctr, - hmac, - pending, - ready - } = this; - await ready; - let encryptedChunkArray = new Uint8Array(); - if (pending.length) { - const encryptedChunk = ctr.update(toBits(codecBytes, pending)); - hmac.update(encryptedChunk); - encryptedChunkArray = fromBits(codecBytes, encryptedChunk); - } - stream.signature = fromBits(codecBytes, hmac.digest()).slice(0, SIGNATURE_LENGTH); - controller.enqueue(concat(encryptedChunkArray, stream.signature)); - } - }); - stream = this; - } -} - -function append(aesCrypto, input, output, paddingStart, paddingEnd, verifySignature) { - const { - ctr, - hmac, - pending - } = aesCrypto; - const inputLength = input.length - paddingEnd; - if (pending.length) { - input = concat(pending, input); - output = expand(output, inputLength - (inputLength % BLOCK_LENGTH)); - } - let offset; - for (offset = 0; offset <= inputLength - BLOCK_LENGTH; offset += BLOCK_LENGTH) { - const inputChunk = toBits(codecBytes, subarray(input, offset, offset + BLOCK_LENGTH)); - if (verifySignature) { - hmac.update(inputChunk); - } - const outputChunk = ctr.update(inputChunk); - if (!verifySignature) { - hmac.update(outputChunk); - } - output.set(fromBits(codecBytes, outputChunk), offset + paddingStart); - } - aesCrypto.pending = subarray(input, offset); - return output; -} - -async function createDecryptionKeys(decrypt, strength, password, preamble) { - const passwordVerificationKey = await createKeys$1(decrypt, strength, password, subarray(preamble, 0, SALT_LENGTH[strength])); - const passwordVerification = subarray(preamble, SALT_LENGTH[strength]); - if (passwordVerificationKey[0] != passwordVerification[0] || passwordVerificationKey[1] != passwordVerification[1]) { - throw new Error(ERR_INVALID_PASSWORD); - } -} - -async function createEncryptionKeys(encrypt, strength, password) { - const salt = getRandomValues(new Uint8Array(SALT_LENGTH[strength])); - const passwordVerification = await createKeys$1(encrypt, strength, password, salt); - return concat(salt, passwordVerification); -} - -async function createKeys$1(aesCrypto, strength, password, salt) { - aesCrypto.password = null; - const encodedPassword = encodeText(password); - const baseKey = await importKey(RAW_FORMAT, encodedPassword, BASE_KEY_ALGORITHM, false, DERIVED_BITS_USAGE); - const derivedBits = await deriveBits(Object.assign({ salt }, DERIVED_BITS_ALGORITHM), baseKey, 8 * ((KEY_LENGTH[strength] * 2) + 2)); - const compositeKey = new Uint8Array(derivedBits); - const key = toBits(codecBytes, subarray(compositeKey, 0, KEY_LENGTH[strength])); - const authentication = toBits(codecBytes, subarray(compositeKey, KEY_LENGTH[strength], KEY_LENGTH[strength] * 2)); - const passwordVerification = subarray(compositeKey, KEY_LENGTH[strength] * 2); - Object.assign(aesCrypto, { - keys: { - key, - authentication, - passwordVerification - }, - ctr: new CtrGladman(new Aes(key), Array.from(COUNTER_DEFAULT_VALUE)), - hmac: new HmacSha1(authentication) - }); - return passwordVerification; -} - -async function importKey(format, password, algorithm, extractable, keyUsages) { - if (IMPORT_KEY_SUPPORTED) { - try { - return await subtle.importKey(format, password, algorithm, extractable, keyUsages); - } catch (_error) { - IMPORT_KEY_SUPPORTED = false; - return misc.importKey(password); - } - } else { - return misc.importKey(password); - } -} - -async function deriveBits(algorithm, baseKey, length) { - if (DERIVE_BITS_SUPPORTED) { - try { - return await subtle.deriveBits(algorithm, baseKey, length); - } catch (_error) { - DERIVE_BITS_SUPPORTED = false; - return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length); - } - } else { - return misc.pbkdf2(baseKey, algorithm.salt, DERIVED_BITS_ALGORITHM.iterations, length); - } -} - -function concat(leftArray, rightArray) { - let array = leftArray; - if (leftArray.length + rightArray.length) { - array = new Uint8Array(leftArray.length + rightArray.length); - array.set(leftArray, 0); - array.set(rightArray, leftArray.length); - } - return array; -} - -function expand(inputArray, length) { - if (length && length > inputArray.length) { - const array = inputArray; - inputArray = new Uint8Array(length); - inputArray.set(array, 0); - } - return inputArray; -} - -function subarray(array, begin, end) { - return array.subarray(begin, end); -} - -function fromBits(codecBytes, chunk) { - return codecBytes.fromBits(chunk); -} -function toBits(codecBytes, chunk) { - return codecBytes.toBits(chunk); -} - -/* - Copyright (c) 2022 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. - */ - -const HEADER_LENGTH = 12; - -class ZipCryptoDecryptionStream extends TransformStream { - - constructor({ password, passwordVerification, checkPasswordOnly }) { - super({ - start() { - Object.assign(this, { - password, - passwordVerification - }); - createKeys(this, password); - }, - transform(chunk, controller) { - const zipCrypto = this; - if (zipCrypto.password) { - const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH)); - zipCrypto.password = null; - if (decryptedHeader[HEADER_LENGTH - 1] != zipCrypto.passwordVerification) { - throw new Error(ERR_INVALID_PASSWORD); - } - chunk = chunk.subarray(HEADER_LENGTH); - } - if (checkPasswordOnly) { - controller.error(new Error(ERR_ABORT_CHECK_PASSWORD)); - } else { - controller.enqueue(decrypt(zipCrypto, chunk)); - } - } - }); - } -} - -class ZipCryptoEncryptionStream extends TransformStream { - - constructor({ password, passwordVerification }) { - super({ - start() { - Object.assign(this, { - password, - passwordVerification - }); - createKeys(this, password); - }, - transform(chunk, controller) { - const zipCrypto = this; - let output; - let offset; - if (zipCrypto.password) { - zipCrypto.password = null; - const header = getRandomValues(new Uint8Array(HEADER_LENGTH)); - header[HEADER_LENGTH - 1] = zipCrypto.passwordVerification; - output = new Uint8Array(chunk.length + header.length); - output.set(encrypt(zipCrypto, header), 0); - offset = HEADER_LENGTH; - } else { - output = new Uint8Array(chunk.length); - offset = 0; - } - output.set(encrypt(zipCrypto, chunk), offset); - controller.enqueue(output); - } - }); - } -} - -function decrypt(target, input) { - const output = new Uint8Array(input.length); - for (let index = 0; index < input.length; index++) { - output[index] = getByte(target) ^ input[index]; - updateKeys(target, output[index]); - } - return output; -} - -function encrypt(target, input) { - const output = new Uint8Array(input.length); - for (let index = 0; index < input.length; index++) { - output[index] = getByte(target) ^ input[index]; - updateKeys(target, input[index]); - } - return output; -} - -function createKeys(target, password) { - const keys = [0x12345678, 0x23456789, 0x34567890]; - Object.assign(target, { - keys, - crcKey0: new Crc32(keys[0]), - crcKey2: new Crc32(keys[2]), - }); - for (let index = 0; index < password.length; index++) { - updateKeys(target, password.charCodeAt(index)); - } -} - -function updateKeys(target, byte) { - let [key0, key1, key2] = target.keys; - target.crcKey0.append([byte]); - key0 = ~target.crcKey0.get(); - key1 = getInt32(Math.imul(getInt32(key1 + getInt8(key0)), 134775813) + 1); - target.crcKey2.append([key1 >>> 24]); - key2 = ~target.crcKey2.get(); - target.keys = [key0, key1, key2]; -} - -function getByte(target) { - const temp = target.keys[2] | 2; - return getInt8(Math.imul(temp, (temp ^ 1)) >>> 8); -} - -function getInt8(number) { - return number & 0xFF; -} - -function getInt32(number) { - return number & 0xFFFFFFFF; -} - -/* - Copyright (c) 2022 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. - */ - -const COMPRESSION_FORMAT = "deflate-raw"; - -class DeflateStream extends TransformStream { - - constructor(options, { chunkSize, CompressionStream, CompressionStreamNative }) { - super({}); - const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level } = options; - const stream = this; - let crc32Stream, encryptionStream; - let readable = filterEmptyChunks(super.readable); - if ((!encrypted || zipCrypto) && signed) { - [readable, crc32Stream] = readable.tee(); - crc32Stream = pipeThrough(crc32Stream, new Crc32Stream()); - } - if (compressed) { - readable = pipeThroughCommpressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStreamNative, CompressionStream); - } - if (encrypted) { - if (zipCrypto) { - readable = pipeThrough(readable, new ZipCryptoEncryptionStream(options)); - } else { - encryptionStream = new AESEncryptionStream(options); - readable = pipeThrough(readable, encryptionStream); - } - } - setReadable(stream, readable, async () => { - let signature; - if (encrypted && !zipCrypto) { - signature = encryptionStream.signature; - } - if ((!encrypted || zipCrypto) && signed) { - signature = await crc32Stream.getReader().read(); - signature = new DataView(signature.value.buffer).getUint32(0); - } - stream.signature = signature; - }); - } -} - -class InflateStream extends TransformStream { - - constructor(options, { chunkSize, DecompressionStream, DecompressionStreamNative }) { - super({}); - const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream } = options; - let crc32Stream, decryptionStream; - let readable = filterEmptyChunks(super.readable); - if (encrypted) { - if (zipCrypto) { - readable = pipeThrough(readable, new ZipCryptoDecryptionStream(options)); - } else { - decryptionStream = new AESDecryptionStream(options); - readable = pipeThrough(readable, decryptionStream); - } - } - if (compressed) { - readable = pipeThroughCommpressionStream(readable, useCompressionStream, { chunkSize }, DecompressionStreamNative, DecompressionStream); - } - if ((!encrypted || zipCrypto) && signed) { - [readable, crc32Stream] = readable.tee(); - crc32Stream = pipeThrough(crc32Stream, new Crc32Stream()); - } - setReadable(this, readable, async () => { - if ((!encrypted || zipCrypto) && signed) { - const streamSignature = await crc32Stream.getReader().read(); - const dataViewSignature = new DataView(streamSignature.value.buffer); - if (signature != dataViewSignature.getUint32(0, false)) { - throw new Error(ERR_INVALID_SIGNATURE); - } - } - }); - } -} - -function filterEmptyChunks(readable) { - return pipeThrough(readable, new TransformStream({ - transform(chunk, controller) { - if (chunk && chunk.length) { - controller.enqueue(chunk); - } - } - })); -} - -function setReadable(stream, readable, flush) { - readable = pipeThrough(readable, new TransformStream({ flush })); - Object.defineProperty(stream, "readable", { - get() { - return readable; - } - }); -} - -function pipeThroughCommpressionStream(readable, useCompressionStream, options, CodecStreamNative, CodecStream) { - try { - const CompressionStream = useCompressionStream && CodecStreamNative ? CodecStreamNative : CodecStream; - readable = pipeThrough(readable, new CompressionStream(COMPRESSION_FORMAT, options)); - } catch (error) { - if (useCompressionStream) { - readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options)); - } else { - throw error; - } - } - return readable; -} - -function pipeThrough(readable, transformStream) { - return readable.pipeThrough(transformStream); -} - -/* - Copyright (c) 2022 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. - */ - -const MESSAGE_EVENT_TYPE = "message"; -const MESSAGE_START = "start"; -const MESSAGE_PULL = "pull"; -const MESSAGE_DATA = "data"; -const MESSAGE_ACK_DATA = "ack"; -const MESSAGE_CLOSE = "close"; -const CODEC_DEFLATE = "deflate"; -const CODEC_INFLATE = "inflate"; - -class CodecStream extends TransformStream { - - constructor(options, config) { - super({}); - const codec = this; - const { codecType } = options; - let Stream; - if (codecType.startsWith(CODEC_DEFLATE)) { - Stream = DeflateStream; - } else if (codecType.startsWith(CODEC_INFLATE)) { - Stream = InflateStream; - } - let size = 0; - const stream = new Stream(options, config); - const readable = super.readable; - const transformStream = new TransformStream({ - transform(chunk, controller) { - if (chunk && chunk.length) { - size += chunk.length; - controller.enqueue(chunk); - } - }, - flush() { - const { signature } = stream; - Object.assign(codec, { - signature, - size - }); - } - }); - Object.defineProperty(codec, "readable", { - get() { - return readable.pipeThrough(stream).pipeThrough(transformStream); - } - }); - } -} - -/* - Copyright (c) 2022 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. - */ - -// deno-lint-ignore valid-typeof -const WEB_WORKERS_SUPPORTED = typeof Worker != UNDEFINED_TYPE$1; - -class CodecWorker { - - constructor(workerData, { readable, writable }, { options, config, streamOptions, useWebWorkers, transferStreams, scripts }, onTaskFinished) { - const { signal } = streamOptions; - Object.assign(workerData, { - busy: true, - readable: readable.pipeThrough(new ProgressWatcherStream(readable, streamOptions, config), { signal }), - writable, - options: Object.assign({}, options), - scripts, - transferStreams, - terminate() { - const { worker, busy } = workerData; - if (worker && !busy) { - worker.terminate(); - workerData.interface = null; - } - }, - onTaskFinished() { - workerData.busy = false; - onTaskFinished(workerData); - } - }); - return (useWebWorkers && WEB_WORKERS_SUPPORTED ? createWebWorkerInterface : createWorkerInterface)(workerData, config); - } -} - -class ProgressWatcherStream extends TransformStream { - - constructor(readableSource, { onstart, onprogress, size, onend }, { chunkSize }) { - let chunkOffset = 0; - super({ - start() { - if (onstart) { - callHandler(onstart, size); - } - }, - async transform(chunk, controller) { - chunkOffset += chunk.length; - if (onprogress) { - await callHandler(onprogress, chunkOffset, size); - } - controller.enqueue(chunk); - }, - flush() { - readableSource.size = chunkOffset; - if (onend) { - callHandler(onend, chunkOffset); - } - } - }, { highWaterMark: 1, size: () => chunkSize }); - } -} - -async function callHandler(handler, ...parameters) { - try { - await handler(...parameters); - } catch (_error) { - // ignored - } -} - -function createWorkerInterface(workerData, config) { - return { - run: () => runWorker$1(workerData, config) - }; -} - -function createWebWorkerInterface(workerData, { baseURL, chunkSize }) { - if (!workerData.interface) { - Object.assign(workerData, { - worker: getWebWorker(workerData.scripts[0], baseURL, workerData), - interface: { - run: () => runWebWorker(workerData, { chunkSize }) - } - }); - } - return workerData.interface; -} - -async function runWorker$1({ options, readable, writable, onTaskFinished }, config) { - const codecStream = new CodecStream(options, config); - try { - await readable.pipeThrough(codecStream).pipeTo(writable, { preventClose: true, preventAbort: true }); - const { - signature, - size - } = codecStream; - return { - signature, - size - }; - } finally { - onTaskFinished(); - } -} - -async function runWebWorker(workerData, config) { - let resolveResult, rejectResult; - const result = new Promise((resolve, reject) => { - resolveResult = resolve; - rejectResult = reject; - }); - Object.assign(workerData, { - reader: null, - writer: null, - resolveResult, - rejectResult, - result - }); - const { readable, options, scripts } = workerData; - const { writable, closed } = watchClosedStream(workerData.writable); - const streamsTransferred = sendMessage({ - type: MESSAGE_START, - scripts: scripts.slice(1), - options, - config, - readable, - writable - }, workerData); - if (!streamsTransferred) { - Object.assign(workerData, { - reader: readable.getReader(), - writer: writable.getWriter() - }); - } - const resultValue = await result; - try { - await writable.close(); - } catch (_error) { - // ignored - } - await closed; - return resultValue; -} - -function watchClosedStream(writableSource) { - const writer = writableSource.getWriter(); - let resolveStreamClosed; - const closed = new Promise(resolve => resolveStreamClosed = resolve); - const writable = new WritableStream({ - async write(chunk) { - await writer.ready; - await writer.write(chunk); - }, - close() { - writer.releaseLock(); - resolveStreamClosed(); - }, - abort(reason) { - return writer.abort(reason); - } - }); - return { writable, closed }; -} - -let classicWorkersSupported = true; -let transferStreamsSupported = true; - -function getWebWorker(url, baseURL, workerData) { - const workerOptions = { type: "module" }; - let scriptUrl, worker; - // deno-lint-ignore valid-typeof - if (typeof url == FUNCTION_TYPE$1) { - url = url(); - } - try { - scriptUrl = new URL(url, baseURL); - } catch (_error) { - scriptUrl = url; - } - if (classicWorkersSupported) { - try { - worker = new Worker(scriptUrl); - } catch (_error) { - classicWorkersSupported = false; - worker = new Worker(scriptUrl, workerOptions); - } - } else { - worker = new Worker(scriptUrl, workerOptions); - } - worker.addEventListener(MESSAGE_EVENT_TYPE, event => onMessage(event, workerData)); - return worker; -} - -function sendMessage(message, { worker, writer, onTaskFinished, transferStreams }) { - try { - let { value, readable, writable } = message; - const transferables = []; - if (value) { - const { buffer, length } = value; - if (length != buffer.byteLength) { - value = new Uint8Array(value); - } - message.value = value.buffer; - transferables.push(message.value); - } - if (transferStreams && transferStreamsSupported) { - if (readable) { - transferables.push(readable); - } - if (writable) { - transferables.push(writable); - } - } else { - message.readable = message.writable = null; - } - if (transferables.length) { - try { - worker.postMessage(message, transferables); - return true; - } catch (_error) { - transferStreamsSupported = false; - message.readable = message.writable = null; - worker.postMessage(message); - } - } else { - worker.postMessage(message); - } - } catch (error) { - if (writer) { - writer.releaseLock(); - } - onTaskFinished(); - throw error; - } -} - -async function onMessage({ data }, workerData) { - const { type, value, messageId, result, error } = data; - const { reader, writer, resolveResult, rejectResult, onTaskFinished } = workerData; - try { - if (error) { - const { message, stack, code, name } = error; - const responseError = new Error(message); - Object.assign(responseError, { stack, code, name }); - close(responseError); - } else { - if (type == MESSAGE_PULL) { - const { value, done } = await reader.read(); - sendMessage({ type: MESSAGE_DATA, value, done, messageId }, workerData); - } - if (type == MESSAGE_DATA) { - await writer.ready; - await writer.write(new Uint8Array(value)); - sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData); - } - if (type == MESSAGE_CLOSE) { - close(null, result); - } - } - } catch (error) { - close(error); - } - - function close(error, result) { - if (error) { - rejectResult(error); - } else { - resolveResult(result); - } - if (writer) { - writer.releaseLock(); - } - onTaskFinished(); - } -} - -/* - Copyright (c) 2022 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. - */ - -let pool = []; -const pendingRequests = []; - -let indexWorker = 0; - -async function runWorker(stream, workerOptions) { - const { options, config } = workerOptions; - const { transferStreams, useWebWorkers, useCompressionStream, codecType, compressed, signed, encrypted } = options; - const { workerScripts, maxWorkers, terminateWorkerTimeout } = config; - workerOptions.transferStreams = transferStreams || transferStreams === UNDEFINED_VALUE; - const streamCopy = !compressed && !signed && !encrypted && !workerOptions.transferStreams; - workerOptions.useWebWorkers = !streamCopy && (useWebWorkers || (useWebWorkers === UNDEFINED_VALUE && config.useWebWorkers)); - workerOptions.scripts = workerOptions.useWebWorkers && workerScripts ? workerScripts[codecType] : []; - options.useCompressionStream = useCompressionStream || (useCompressionStream === UNDEFINED_VALUE && config.useCompressionStream); - let worker; - const workerData = pool.find(workerData => !workerData.busy); - if (workerData) { - clearTerminateTimeout(workerData); - worker = new CodecWorker(workerData, stream, workerOptions, onTaskFinished); - } else if (pool.length < maxWorkers) { - const workerData = { indexWorker }; - indexWorker++; - pool.push(workerData); - worker = new CodecWorker(workerData, stream, workerOptions, onTaskFinished); - } else { - worker = await new Promise(resolve => pendingRequests.push({ resolve, stream, workerOptions })); - } - return worker.run(); - - function onTaskFinished(workerData) { - if (pendingRequests.length) { - const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1); - resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished)); - } else if (workerData.worker) { - clearTerminateTimeout(workerData); - if (Number.isFinite(terminateWorkerTimeout) && terminateWorkerTimeout >= 0) { - workerData.terminateTimeout = setTimeout(() => { - pool = pool.filter(data => data != workerData); - workerData.terminate(); - }, terminateWorkerTimeout); - } - } else { - pool = pool.filter(data => data != workerData); - } - } -} - -function clearTerminateTimeout(workerData) { - const { terminateTimeout } = workerData; - if (terminateTimeout) { - clearTimeout(terminateTimeout); - workerData.terminateTimeout = null; - } -} - -function e(e){const t=()=>URL.createObjectURL(new Blob(['const{Array:e,Object:t,Number:n,Math:r,Error:s,Uint8Array:i,Uint16Array:o,Uint32Array:c,Int32Array:f,Map:a,DataView:l,Promise:u,TextEncoder:w,crypto:h,postMessage:d,TransformStream:p,ReadableStream:y,WritableStream:m,CompressionStream:b,DecompressionStream:g}=self;class k{constructor(e){return class extends p{constructor(t,n){const r=new e(n);super({transform(e,t){t.enqueue(r.append(e))},flush(e){const t=r.flush();t&&e.enqueue(t)}})}}}}const v=[];for(let e=0;256>e;e++){let t=e;for(let e=0;8>e;e++)1&t?t=t>>>1^3988292384:t>>>=1;v[e]=t}class S{constructor(e){this.t=e||-1}append(e){let t=0|this.t;for(let n=0,r=0|e.length;r>n;n++)t=t>>>8^v[255&(t^e[n])];this.t=t}get(){return~this.t}}class z extends p{constructor(){const e=new S;super({transform(t){e.append(t)},flush(t){const n=new i(4);new l(n.buffer).setUint32(0,e.get()),t.enqueue(n)}})}}const C={concat(e,t){if(0===e.length||0===t.length)return e.concat(t);const n=e[e.length-1],r=C.i(n);return 32===r?e.concat(t):C.o(t,r,0|n,e.slice(0,e.length-1))},l(e){const t=e.length;if(0===t)return 0;const n=e[t-1];return 32*(t-1)+C.i(n)},u(e,t){if(32*e.length0&&t&&(e[n-1]=C.h(t,e[n-1]&2147483648>>t-1,1)),e},h:(e,t,n)=>32===e?t:(n?0|t:t<<32-e)+1099511627776*e,i:e=>r.round(e/1099511627776)||32,o(e,t,n,r){for(void 0===r&&(r=[]);t>=32;t-=32)r.push(n),n=0;if(0===t)return r.concat(e);for(let s=0;s>>t),n=e[s]<<32-t;const s=e.length?e[e.length-1]:0,i=C.i(s);return r.push(C.h(t+i&31,t+i>32?n:r.pop(),1)),r}},x={p:{m(e){const t=C.l(e)/8,n=new i(t);let r;for(let s=0;t>s;s++)0==(3&s)&&(r=e[s/4]),n[s]=r>>>24,r<<=8;return n},g(e){const t=[];let n,r=0;for(n=0;n9007199254740991)throw new s("Cannot hash more than 2^53 - 1 bits");const o=new c(n);let f=0;for(let e=t.blockSize+r-(t.blockSize+r&t.blockSize-1);i>=e;e+=t.blockSize)t.I(o.subarray(16*f,16*(f+1))),f+=1;return n.splice(0,16*f),t}D(){const e=this;let t=e.C;const n=e.S;t=C.concat(t,[C.h(1,1)]);for(let e=t.length+2;15&e;e++)t.push(0);for(t.push(r.floor(e._/4294967296)),t.push(0|e._);t.length;)e.I(t.splice(0,16));return e.reset(),n}V(e,t,n,r){return e>19?e>39?e>59?e>79?void 0:t^n^r:t&n|t&r|n&r:t^n^r:t&n|~t&r}P(e,t){return t<>>32-e}I(t){const n=this,s=n.S,i=e(80);for(let e=0;16>e;e++)i[e]=t[e];let o=s[0],c=s[1],f=s[2],a=s[3],l=s[4];for(let e=0;79>=e;e++){16>e||(i[e]=n.P(1,i[e-3]^i[e-8]^i[e-14]^i[e-16]));const t=n.P(5,o)+n.V(e,c,f,a)+l+i[e]+n.v[r.floor(e/20)]|0;l=a,a=f,f=n.P(30,c),c=o,o=t}s[0]=s[0]+o|0,s[1]=s[1]+c|0,s[2]=s[2]+f|0,s[3]=s[3]+a|0,s[4]=s[4]+l|0}},A={getRandomValues(e){const t=new c(e.buffer),n=e=>{let t=987654321;const n=4294967295;return()=>(t=36969*(65535&t)+(t>>16)&n,(((t<<16)+(e=18e3*(65535&e)+(e>>16)&n)&n)/4294967296+.5)*(r.random()>.5?1:-1))};for(let s,i=0;inew I.R(x.p.g(e)),B(e,t,n,r){if(n=n||1e4,0>r||0>n)throw new s("invalid params to pbkdf2");const i=1+(r>>5)<<2;let o,c,f,a,u;const w=new ArrayBuffer(i),h=new l(w);let d=0;const p=C;for(t=x.p.g(t),u=1;(i||1)>d;u++){for(o=c=e.encrypt(p.concat(t,[u])),f=1;n>f;f++)for(c=e.encrypt(c),a=0;ad&&fs&&(e=(new n).update(e).D());for(let t=0;s>t;t++)r[0][t]=909522486^e[t],r[1][t]=1549556828^e[t];t.K[0].update(r[0]),t.K[1].update(r[1]),t.U=new n(t.K[0])}reset(){const e=this;e.U=new e.M(e.K[0]),e.N=!1}update(e){this.N=!0,this.U.update(e)}digest(){const e=this,t=e.U.D(),n=new e.M(e.K[1]).update(t).D();return e.reset(),n}encrypt(e){if(this.N)throw new s("encrypt on already updated hmac called!");return this.update(e),this.digest(e)}}},D=void 0!==h&&"function"==typeof h.getRandomValues,V="Invalid password",P="Invalid signature",R="zipjs-abort-check-password";function B(e){return D?h.getRandomValues(e):A.getRandomValues(e)}const E=16,M={name:"PBKDF2"},K=t.assign({hash:{name:"HMAC"}},M),U=t.assign({iterations:1e3,hash:{name:"SHA-1"}},M),N=["deriveBits"],O=[8,12,16],T=[16,24,32],W=10,j=[0,0,0,0],H="undefined",L="function",F=typeof h!=H,q=F&&h.subtle,G=F&&typeof q!=H,J=x.p,Q=class{constructor(e){const t=this;t.O=[[[],[],[],[],[]],[[],[],[],[],[]]],t.O[0][0][0]||t.T();const n=t.O[0][4],r=t.O[1],i=e.length;let o,c,f,a=1;if(4!==i&&6!==i&&8!==i)throw new s("invalid aes key size");for(t.v=[c=e.slice(0),f=[]],o=i;4*i+28>o;o++){let e=c[o-1];(o%i==0||8===i&&o%i==4)&&(e=n[e>>>24]<<24^n[e>>16&255]<<16^n[e>>8&255]<<8^n[255&e],o%i==0&&(e=e<<8^e>>>24^a<<24,a=a<<1^283*(a>>7))),c[o]=c[o-i]^e}for(let e=0;o;e++,o--){const t=c[3&e?o:o-4];f[e]=4>=o||4>e?t:r[0][n[t>>>24]]^r[1][n[t>>16&255]]^r[2][n[t>>8&255]]^r[3][n[255&t]]}}encrypt(e){return this.W(e,0)}decrypt(e){return this.W(e,1)}T(){const e=this.O[0],t=this.O[1],n=e[4],r=t[4],s=[],i=[];let o,c,f,a;for(let e=0;256>e;e++)i[(s[e]=e<<1^283*(e>>7))^e]=e;for(let l=o=0;!n[l];l^=c||1,o=i[o]||1){let i=o^o<<1^o<<2^o<<3^o<<4;i=i>>8^255&i^99,n[l]=i,r[i]=l,a=s[f=s[c=s[l]]];let u=16843009*a^65537*f^257*c^16843008*l,w=257*s[i]^16843008*i;for(let n=0;4>n;n++)e[n][l]=w=w<<24^w>>>8,t[n][i]=u=u<<24^u>>>8}for(let n=0;5>n;n++)e[n]=e[n].slice(0),t[n]=t[n].slice(0)}W(e,t){if(4!==e.length)throw new s("invalid aes block size");const n=this.v[t],r=n.length/4-2,i=[0,0,0,0],o=this.O[t],c=o[0],f=o[1],a=o[2],l=o[3],u=o[4];let w,h,d,p=e[0]^n[0],y=e[t?3:1]^n[1],m=e[2]^n[2],b=e[t?1:3]^n[3],g=4;for(let e=0;r>e;e++)w=c[p>>>24]^f[y>>16&255]^a[m>>8&255]^l[255&b]^n[g],h=c[y>>>24]^f[m>>16&255]^a[b>>8&255]^l[255&p]^n[g+1],d=c[m>>>24]^f[b>>16&255]^a[p>>8&255]^l[255&y]^n[g+2],b=c[b>>>24]^f[p>>16&255]^a[y>>8&255]^l[255&m]^n[g+3],g+=4,p=w,y=h,m=d;for(let e=0;4>e;e++)i[t?3&-e:e]=u[p>>>24]<<24^u[y>>16&255]<<16^u[m>>8&255]<<8^u[255&b]^n[g++],w=p,p=y,y=m,m=b,b=w;return i}},X=class{constructor(e,t){this.j=e,this.H=t,this.L=t}reset(){this.L=this.H}update(e){return this.F(this.j,e,this.L)}q(e){if(255==(e>>24&255)){let t=e>>16&255,n=e>>8&255,r=255&e;255===t?(t=0,255===n?(n=0,255===r?r=0:++r):++n):++t,e=0,e+=t<<16,e+=n<<8,e+=r}else e+=1<<24;return e}G(e){0===(e[0]=this.q(e[0]))&&(e[1]=this.q(e[1]))}F(e,t,n){let r;if(!(r=t.length))return[];const s=C.l(t);for(let s=0;r>s;s+=4){this.G(n);const r=e.encrypt(n);t[s]^=r[0],t[s+1]^=r[1],t[s+2]^=r[2],t[s+3]^=r[3]}return C.u(t,s)}},Y=I.R;let Z=F&&G&&typeof q.importKey==L,$=F&&G&&typeof q.deriveBits==L;class ee extends p{constructor({password:e,signed:n,encryptionStrength:r,checkPasswordOnly:o}){super({start(){t.assign(this,{ready:new u((e=>this.J=e)),password:e,signed:n,X:r-1,pending:new i})},async transform(e,t){const n=this,{password:r,X:c,J:f,ready:a}=n;r?(await(async(e,t,n,r)=>{const i=await re(e,t,n,ie(r,0,O[t])),o=ie(r,O[t]);if(i[0]!=o[0]||i[1]!=o[1])throw new s(V)})(n,c,r,ie(e,0,O[c]+2)),e=ie(e,O[c]+2),o?t.error(new s(R)):f()):await a;const l=new i(e.length-W-(e.length-W)%E);t.enqueue(ne(n,e,l,0,W,!0))},async flush(e){const{signed:t,Y:n,Z:r,pending:o,ready:c}=this;await c;const f=ie(o,0,o.length-W),a=ie(o,o.length-W);let l=new i;if(f.length){const e=ce(J,f);r.update(e);const t=n.update(e);l=oe(J,t)}if(t){const e=ie(oe(J,r.digest()),0,W);for(let t=0;W>t;t++)if(e[t]!=a[t])throw new s(P)}e.enqueue(l)}})}}class te extends p{constructor({password:e,encryptionStrength:n}){let r;super({start(){t.assign(this,{ready:new u((e=>this.J=e)),password:e,X:n-1,pending:new i})},async transform(e,t){const n=this,{password:r,X:s,J:o,ready:c}=n;let f=new i;r?(f=await(async(e,t,n)=>{const r=B(new i(O[t]));return se(r,await re(e,t,n,r))})(n,s,r),o()):await c;const a=new i(f.length+e.length-e.length%E);a.set(f,0),t.enqueue(ne(n,e,a,f.length,0))},async flush(e){const{Y:t,Z:n,pending:s,ready:o}=this;await o;let c=new i;if(s.length){const e=t.update(ce(J,s));n.update(e),c=oe(J,e)}r.signature=oe(J,n.digest()).slice(0,W),e.enqueue(se(c,r.signature))}}),r=this}}function ne(e,t,n,r,s,o){const{Y:c,Z:f,pending:a}=e,l=t.length-s;let u;for(a.length&&(t=se(a,t),n=((e,t)=>{if(t&&t>e.length){const n=e;(e=new i(t)).set(n,0)}return e})(n,l-l%E)),u=0;l-E>=u;u+=E){const e=ce(J,ie(t,u,u+E));o&&f.update(e);const s=c.update(e);o||f.update(s),n.set(oe(J,s),u+r)}return e.pending=ie(t,u),n}async function re(n,r,s,o){n.password=null;const c=(e=>{if(void 0===w){const t=new i((e=unescape(encodeURIComponent(e))).length);for(let n=0;n{if(!Z)return I.importKey(t);try{return await q.importKey("raw",t,n,!1,s)}catch(e){return Z=!1,I.importKey(t)}})(0,c,K,0,N),a=await(async(e,t,n)=>{if(!$)return I.B(t,e.salt,U.iterations,n);try{return await q.deriveBits(e,t,n)}catch(r){return $=!1,I.B(t,e.salt,U.iterations,n)}})(t.assign({salt:o},U),f,8*(2*T[r]+2)),l=new i(a),u=ce(J,ie(l,0,T[r])),h=ce(J,ie(l,T[r],2*T[r])),d=ie(l,2*T[r]);return t.assign(n,{keys:{key:u,$:h,passwordVerification:d},Y:new X(new Q(u),e.from(j)),Z:new Y(h)}),d}function se(e,t){let n=e;return e.length+t.length&&(n=new i(e.length+t.length),n.set(e,0),n.set(t,e.length)),n}function ie(e,t,n){return e.subarray(t,n)}function oe(e,t){return e.m(t)}function ce(e,t){return e.g(t)}class fe extends p{constructor({password:e,passwordVerification:n,checkPasswordOnly:r}){super({start(){t.assign(this,{password:e,passwordVerification:n}),we(this,e)},transform(e,t){const n=this;if(n.password){const t=le(n,e.subarray(0,12));if(n.password=null,t[11]!=n.passwordVerification)throw new s(V);e=e.subarray(12)}r?t.error(new s(R)):t.enqueue(le(n,e))}})}}class ae extends p{constructor({password:e,passwordVerification:n}){super({start(){t.assign(this,{password:e,passwordVerification:n}),we(this,e)},transform(e,t){const n=this;let r,s;if(n.password){n.password=null;const t=B(new i(12));t[11]=n.passwordVerification,r=new i(e.length+t.length),r.set(ue(n,t),0),s=12}else r=new i(e.length),s=0;r.set(ue(n,e),s),t.enqueue(r)}})}}function le(e,t){const n=new i(t.length);for(let r=0;r>>24]),i=~e.te.get(),e.keys=[n,s,i]}function de(e){const t=2|e.keys[2];return pe(r.imul(t,1^t)>>>8)}function pe(e){return 255&e}function ye(e){return 4294967295&e}const me="deflate-raw";class be extends p{constructor(e,{chunkSize:t,CompressionStream:n,CompressionStreamNative:r}){super({});const{compressed:s,encrypted:i,useCompressionStream:o,zipCrypto:c,signed:f,level:a}=e,u=this;let w,h,d=ke(super.readable);i&&!c||!f||([d,w]=d.tee(),w=ze(w,new z)),s&&(d=Se(d,o,{level:a,chunkSize:t},r,n)),i&&(c?d=ze(d,new ae(e)):(h=new te(e),d=ze(d,h))),ve(u,d,(async()=>{let e;i&&!c&&(e=h.signature),i&&!c||!f||(e=await w.getReader().read(),e=new l(e.value.buffer).getUint32(0)),u.signature=e}))}}class ge extends p{constructor(e,{chunkSize:t,DecompressionStream:n,DecompressionStreamNative:r}){super({});const{zipCrypto:i,encrypted:o,signed:c,signature:f,compressed:a,useCompressionStream:u}=e;let w,h,d=ke(super.readable);o&&(i?d=ze(d,new fe(e)):(h=new ee(e),d=ze(d,h))),a&&(d=Se(d,u,{chunkSize:t},r,n)),o&&!i||!c||([d,w]=d.tee(),w=ze(w,new z)),ve(this,d,(async()=>{if((!o||i)&&c){const e=await w.getReader().read(),t=new l(e.value.buffer);if(f!=t.getUint32(0,!1))throw new s(P)}}))}}function ke(e){return ze(e,new p({transform(e,t){e&&e.length&&t.enqueue(e)}}))}function ve(e,n,r){n=ze(n,new p({flush:r})),t.defineProperty(e,"readable",{get:()=>n})}function Se(e,t,n,r,s){try{e=ze(e,new(t&&r?r:s)(me,n))}catch(r){if(!t)throw r;e=ze(e,new s(me,n))}return e}function ze(e,t){return e.pipeThrough(t)}const Ce="data";class xe extends p{constructor(e,n){super({});const r=this,{codecType:s}=e;let i;s.startsWith("deflate")?i=be:s.startsWith("inflate")&&(i=ge);let o=0;const c=new i(e,n),f=super.readable,a=new p({transform(e,t){e&&e.length&&(o+=e.length,t.enqueue(e))},flush(){const{signature:e}=c;t.assign(r,{signature:e,size:o})}});t.defineProperty(r,"readable",{get:()=>f.pipeThrough(c).pipeThrough(a)})}}const _e=new a,Ae=new a;let Ie=0;async function De(e){try{const{options:t,scripts:r,config:s}=e;r&&r.length&&importScripts.apply(void 0,r),self.initCodec&&self.initCodec(),s.CompressionStreamNative=self.CompressionStream,s.DecompressionStreamNative=self.DecompressionStream,self.Deflate&&(s.CompressionStream=new k(self.Deflate)),self.Inflate&&(s.DecompressionStream=new k(self.Inflate));const i={highWaterMark:1,size:()=>s.chunkSize},o=e.readable||new y({async pull(e){const t=new u((e=>_e.set(Ie,e)));Ve({type:"pull",messageId:Ie}),Ie=(Ie+1)%n.MAX_SAFE_INTEGER;const{value:r,done:s}=await t;e.enqueue(r),s&&e.close()}},i),c=e.writable||new m({async write(e){let t;const r=new u((e=>t=e));Ae.set(Ie,t),Ve({type:Ce,value:e,messageId:Ie}),Ie=(Ie+1)%n.MAX_SAFE_INTEGER,await r}},i),f=new xe(t,s);await o.pipeThrough(f).pipeTo(c,{preventClose:!0,preventAbort:!0});try{await c.close()}catch(e){}const{signature:a,size:l}=f;Ve({type:"close",result:{signature:a,size:l}})}catch(e){Pe(e)}}function Ve(e){let{value:t}=e;if(t)if(t.length)try{t=new i(t),e.value=t.buffer,d(e,[e.value])}catch(t){d(e)}else d(e);else d(e)}function Pe(e){const{message:t,stack:n,code:r,name:s}=e;d({error:{message:t,stack:n,code:r,name:s}})}addEventListener("message",(({data:e})=>{const{type:t,messageId:n,value:r,done:s}=e;try{if("start"==t&&De(e),t==Ce){const e=_e.get(n);_e.delete(n),e({value:new i(r),done:s})}if("ack"==t){const e=Ae.get(n);Ae.delete(n),e()}}catch(e){Pe(e)}}));const Re=-2;function Be(t){return Ee(t.map((([t,n])=>new e(t).fill(n,0,t))))}function Ee(t){return t.reduce(((t,n)=>t.concat(e.isArray(n)?Ee(n):n)),[])}const Me=[0,1,2,3].concat(...Be([[2,4],[2,5],[4,6],[4,7],[8,8],[8,9],[16,10],[16,11],[32,12],[32,13],[64,14],[64,15],[2,0],[1,16],[1,17],[2,18],[2,19],[4,20],[4,21],[8,22],[8,23],[16,24],[16,25],[32,26],[32,27],[64,28],[64,29]]));function Ke(){const e=this;function t(e,t){let n=0;do{n|=1&e,e>>>=1,n<<=1}while(--t>0);return n>>>1}e.ne=n=>{const s=e.re,i=e.ie.se,o=e.ie.oe;let c,f,a,l=-1;for(n.ce=0,n.fe=573,c=0;o>c;c++)0!==s[2*c]?(n.ae[++n.ce]=l=c,n.le[c]=0):s[2*c+1]=0;for(;2>n.ce;)a=n.ae[++n.ce]=2>l?++l:0,s[2*a]=1,n.le[a]=0,n.ue--,i&&(n.we-=i[2*a+1]);for(e.he=l,c=r.floor(n.ce/2);c>=1;c--)n.de(s,c);a=o;do{c=n.ae[1],n.ae[1]=n.ae[n.ce--],n.de(s,1),f=n.ae[1],n.ae[--n.fe]=c,n.ae[--n.fe]=f,s[2*a]=s[2*c]+s[2*f],n.le[a]=r.max(n.le[c],n.le[f])+1,s[2*c+1]=s[2*f+1]=a,n.ae[1]=a++,n.de(s,1)}while(n.ce>=2);n.ae[--n.fe]=n.ae[1],(t=>{const n=e.re,r=e.ie.se,s=e.ie.pe,i=e.ie.ye,o=e.ie.me;let c,f,a,l,u,w,h=0;for(l=0;15>=l;l++)t.be[l]=0;for(n[2*t.ae[t.fe]+1]=0,c=t.fe+1;573>c;c++)f=t.ae[c],l=n[2*n[2*f+1]+1]+1,l>o&&(l=o,h++),n[2*f+1]=l,f>e.he||(t.be[l]++,u=0,i>f||(u=s[f-i]),w=n[2*f],t.ue+=w*(l+u),r&&(t.we+=w*(r[2*f+1]+u)));if(0!==h){do{for(l=o-1;0===t.be[l];)l--;t.be[l]--,t.be[l+1]+=2,t.be[o]--,h-=2}while(h>0);for(l=o;0!==l;l--)for(f=t.be[l];0!==f;)a=t.ae[--c],a>e.he||(n[2*a+1]!=l&&(t.ue+=(l-n[2*a+1])*n[2*a],n[2*a+1]=l),f--)}})(n),((e,n,r)=>{const s=[];let i,o,c,f=0;for(i=1;15>=i;i++)s[i]=f=f+r[i-1]<<1;for(o=0;n>=o;o++)c=e[2*o+1],0!==c&&(e[2*o]=t(s[c]++,c))})(s,e.he,n.be)}}function Ue(e,t,n,r,s){const i=this;i.se=e,i.pe=t,i.ye=n,i.oe=r,i.me=s}Ke.ge=[0,1,2,3,4,5,6,7].concat(...Be([[2,8],[2,9],[2,10],[2,11],[4,12],[4,13],[4,14],[4,15],[8,16],[8,17],[8,18],[8,19],[16,20],[16,21],[16,22],[16,23],[32,24],[32,25],[32,26],[31,27],[1,28]])),Ke.ke=[0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224,0],Ke.ve=[0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576],Ke.Se=e=>256>e?Me[e]:Me[256+(e>>>7)],Ke.ze=[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],Ke.Ce=[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],Ke.xe=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],Ke._e=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];const Ne=Be([[144,8],[112,9],[24,7],[8,8]]);Ue.Ae=Ee([12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,19,275,147,403,83,339,211,467,51,307,179,435,115,371,243,499,11,267,139,395,75,331,203,459,43,299,171,427,107,363,235,491,27,283,155,411,91,347,219,475,59,315,187,443,123,379,251,507,7,263,135,391,71,327,199,455,39,295,167,423,103,359,231,487,23,279,151,407,87,343,215,471,55,311,183,439,119,375,247,503,15,271,143,399,79,335,207,463,47,303,175,431,111,367,239,495,31,287,159,415,95,351,223,479,63,319,191,447,127,383,255,511,0,64,32,96,16,80,48,112,8,72,40,104,24,88,56,120,4,68,36,100,20,84,52,116,3,131,67,195,35,163,99,227].map(((e,t)=>[e,Ne[t]])));const Oe=Be([[30,5]]);function Te(e,t,n,r,s){const i=this;i.Ie=e,i.De=t,i.Ve=n,i.Pe=r,i.Re=s}Ue.Be=Ee([0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,1,17,9,25,5,21,13,29,3,19,11,27,7,23].map(((e,t)=>[e,Oe[t]]))),Ue.Ee=new Ue(Ue.Ae,Ke.ze,257,286,15),Ue.Me=new Ue(Ue.Be,Ke.Ce,0,30,15),Ue.Ke=new Ue(null,Ke.xe,0,19,7);const We=[new Te(0,0,0,0,0),new Te(4,4,8,4,1),new Te(4,5,16,8,1),new Te(4,6,32,32,1),new Te(4,4,16,16,2),new Te(8,16,32,32,2),new Te(8,16,128,128,2),new Te(8,32,128,256,2),new Te(32,128,258,1024,2),new Te(32,258,258,4096,2)],je=["need dictionary","stream end","","","stream error","data error","","buffer error","",""],He=113,Le=666,Fe=262;function qe(e,t,n,r){const s=e[2*t],i=e[2*n];return i>s||s==i&&r[t]<=r[n]}function Ge(){const e=this;let t,n,s,c,f,a,l,u,w,h,d,p,y,m,b,g,k,v,S,z,C,x,_,A,I,D,V,P,R,B,E,M,K;const U=new Ke,N=new Ke,O=new Ke;let T,W,j,H,L,F;function q(){let t;for(t=0;286>t;t++)E[2*t]=0;for(t=0;30>t;t++)M[2*t]=0;for(t=0;19>t;t++)K[2*t]=0;E[512]=1,e.ue=e.we=0,W=j=0}function G(e,t){let n,r=-1,s=e[1],i=0,o=7,c=4;0===s&&(o=138,c=3),e[2*(t+1)+1]=65535;for(let f=0;t>=f;f++)n=s,s=e[2*(f+1)+1],++ii?K[2*n]+=i:0!==n?(n!=r&&K[2*n]++,K[32]++):i>10?K[36]++:K[34]++,i=0,r=n,0===s?(o=138,c=3):n==s?(o=6,c=3):(o=7,c=4))}function J(t){e.Ue[e.pending++]=t}function Q(e){J(255&e),J(e>>>8&255)}function X(e,t){let n;const r=t;F>16-r?(n=e,L|=n<>>16-F,F+=r-16):(L|=e<=n;n++)if(r=i,i=e[2*(n+1)+1],++o>=c||r!=i){if(f>o)do{Y(r,K)}while(0!=--o);else 0!==r?(r!=s&&(Y(r,K),o--),Y(16,K),X(o-3,2)):o>10?(Y(18,K),X(o-11,7)):(Y(17,K),X(o-3,3));o=0,s=r,0===i?(c=138,f=3):r==i?(c=6,f=3):(c=7,f=4)}}function $(){16==F?(Q(L),L=0,F=0):8>F||(J(255&L),L>>>=8,F-=8)}function ee(t,n){let s,i,o;if(e.Ne[W]=t,e.Oe[W]=255&n,W++,0===t?E[2*n]++:(j++,t--,E[2*(Ke.ge[n]+256+1)]++,M[2*Ke.Se(t)]++),0==(8191&W)&&V>2){for(s=8*W,i=C-k,o=0;30>o;o++)s+=M[2*o]*(5+Ke.Ce[o]);if(s>>>=3,jc);Y(256,t),H=t[513]}function ne(){F>8?Q(L):F>0&&J(255&L),L=0,F=0}function re(t,n,r){X(0+(r?1:0),3),((t,n)=>{ne(),H=8,Q(n),Q(~n),e.Ue.set(u.subarray(t,t+n),e.pending),e.pending+=n})(t,n)}function se(n){((t,n,r)=>{let s,i,o=0;V>0?(U.ne(e),N.ne(e),o=(()=>{let t;for(G(E,U.he),G(M,N.he),O.ne(e),t=18;t>=3&&0===K[2*Ke._e[t]+1];t--);return e.ue+=14+3*(t+1),t})(),s=e.ue+3+7>>>3,i=e.we+3+7>>>3,i>s||(s=i)):s=i=n+5,n+4>s||-1==t?i==s?(X(2+(r?1:0),3),te(Ue.Ae,Ue.Be)):(X(4+(r?1:0),3),((e,t,n)=>{let r;for(X(e-257,5),X(t-1,5),X(n-4,4),r=0;n>r;r++)X(K[2*Ke._e[r]+1],3);Z(E,e-1),Z(M,t-1)})(U.he+1,N.he+1,o+1),te(E,M)):re(t,n,r),q(),r&&ne()})(0>k?-1:k,C-k,n),k=C,t.Te()}function ie(){let e,n,r,s;do{if(s=w-_-C,0===s&&0===C&&0===_)s=f;else if(-1==s)s--;else if(C>=f+f-Fe){u.set(u.subarray(f,f+f),0),x-=f,C-=f,k-=f,e=y,r=e;do{n=65535&d[--r],d[r]=f>n?0:n-f}while(0!=--e);e=f,r=e;do{n=65535&h[--r],h[r]=f>n?0:n-f}while(0!=--e);s+=f}if(0===t.We)return;e=t.je(u,C+_,s),_+=e,3>_||(p=255&u[C],p=(p<_&&0!==t.We)}function oe(e){let t,n,r=I,s=C,i=A;const o=C>f-Fe?C-(f-Fe):0;let c=B;const a=l,w=C+258;let d=u[s+i-1],p=u[s+i];R>A||(r>>=2),c>_&&(c=_);do{if(t=e,u[t+i]==p&&u[t+i-1]==d&&u[t]==u[s]&&u[++t]==u[s+1]){s+=2,t++;do{}while(u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&u[++s]==u[++t]&&w>s);if(n=258-(w-s),s=w-258,n>i){if(x=e,i=n,n>=c)break;d=u[s+i-1],p=u[s+i]}}}while((e=65535&h[e&a])>o&&0!=--r);return i>_?_:i}e.le=[],e.be=[],e.ae=[],E=[],M=[],K=[],e.de=(t,n)=>{const r=e.ae,s=r[n];let i=n<<1;for(;i<=e.ce&&(i(W||(W=8),j||(j=8),G||(G=0),t.Le=null,-1==S&&(S=6),1>j||j>9||8!=W||9>x||x>15||0>S||S>9||0>G||G>2?Re:(t.Fe=e,a=x,f=1<(t.qe=t.Ge=0,t.Le=null,e.pending=0,e.Je=0,n=He,c=0,U.re=E,U.ie=Ue.Ee,N.re=M,N.ie=Ue.Me,O.re=K,O.ie=Ue.Ke,L=0,F=0,H=8,q(),(()=>{w=2*f,d[y-1]=0;for(let e=0;y-1>e;e++)d[e]=0;D=We[V].De,R=We[V].Ie,B=We[V].Ve,I=We[V].Pe,C=0,k=0,_=0,v=A=2,z=0,p=0})(),0))(t))),e.Qe=()=>42!=n&&n!=He&&n!=Le?Re:(e.Oe=null,e.Ne=null,e.Ue=null,d=null,h=null,u=null,e.Fe=null,n==He?-3:0),e.Xe=(e,t,n)=>{let r=0;return-1==t&&(t=6),0>t||t>9||0>n||n>2?Re:(We[V].Re!=We[t].Re&&0!==e.qe&&(r=e.Ye(1)),V!=t&&(V=t,D=We[V].De,R=We[V].Ie,B=We[V].Ve,I=We[V].Pe),P=n,r)},e.Ze=(e,t,r)=>{let s,i=r,o=0;if(!t||42!=n)return Re;if(3>i)return 0;for(i>f-Fe&&(i=f-Fe,o=r-i),u.set(t.subarray(o,o+i),0),C=i,k=i,p=255&u[0],p=(p<=s;s++)p=(p<{let o,w,m,I,R;if(i>4||0>i)return Re;if(!r.$e||!r.et&&0!==r.We||n==Le&&4!=i)return r.Le=je[4],Re;if(0===r.tt)return r.Le=je[7],-5;var B;if(t=r,I=c,c=i,42==n&&(w=8+(a-8<<4)<<8,m=(V-1&255)>>1,m>3&&(m=3),w|=m<<6,0!==C&&(w|=32),w+=31-w%31,n=He,J((B=w)>>8&255),J(255&B)),0!==e.pending){if(t.Te(),0===t.tt)return c=-1,0}else if(0===t.We&&I>=i&&4!=i)return t.Le=je[7],-5;if(n==Le&&0!==t.We)return r.Le=je[7],-5;if(0!==t.We||0!==_||0!=i&&n!=Le){switch(R=-1,We[V].Re){case 0:R=(e=>{let n,r=65535;for(r>s-5&&(r=s-5);;){if(1>=_){if(ie(),0===_&&0==e)return 0;if(0===_)break}if(C+=_,_=0,n=k+r,(0===C||C>=n)&&(_=C-n,C=n,se(!1),0===t.tt))return 0;if(C-k>=f-Fe&&(se(!1),0===t.tt))return 0}return se(4==e),0===t.tt?4==e?2:0:4==e?3:1})(i);break;case 1:R=(e=>{let n,r=0;for(;;){if(Fe>_){if(ie(),Fe>_&&0==e)return 0;if(0===_)break}if(3>_||(p=(p<f-Fe||2!=P&&(v=oe(r)),3>v)n=ee(0,255&u[C]),_--,C++;else if(n=ee(C-x,v-3),_-=v,v>D||3>_)C+=v,v=0,p=255&u[C],p=(p<{let n,r,s=0;for(;;){if(Fe>_){if(ie(),Fe>_&&0==e)return 0;if(0===_)break}if(3>_||(p=(p<A&&f-Fe>=(C-s&65535)&&(2!=P&&(v=oe(s)),5>=v&&(1==P||3==v&&C-x>4096)&&(v=2)),3>A||v>A)if(0!==z){if(n=ee(0,255&u[C-1]),n&&se(!1),C++,_--,0===t.tt)return 0}else z=1,C++,_--;else{r=C+_-3,n=ee(C-1-S,A-3),_-=A-1,A-=2;do{++C>r||(p=(p<1+H+10-F&&(X(2,3),Y(256,Ue.Ae),$()),H=7;else if(re(0,0,!1),3==i)for(o=0;y>o;o++)d[o]=0;if(t.Te(),0===t.tt)return c=-1,0}}return 4!=i?0:1}}function Je(){const e=this;e.nt=0,e.rt=0,e.We=0,e.qe=0,e.tt=0,e.Ge=0}function Qe(e){const t=new Je,n=(o=e&&e.chunkSize?e.chunkSize:65536)+5*(r.floor(o/16383)+1);var o;const c=new i(n);let f=e?e.level:-1;void 0===f&&(f=-1),t.He(f),t.$e=c,this.append=(e,r)=>{let o,f,a=0,l=0,u=0;const w=[];if(e.length){t.nt=0,t.et=e,t.We=e.length;do{if(t.rt=0,t.tt=n,o=t.Ye(0),0!=o)throw new s("deflating: "+t.Le);t.rt&&(t.rt==n?w.push(new i(c)):w.push(c.slice(0,t.rt))),u+=t.rt,r&&t.nt>0&&t.nt!=a&&(r(t.nt),a=t.nt)}while(t.We>0||0===t.tt);return w.length>1?(f=new i(u),w.forEach((e=>{f.set(e,l),l+=e.length}))):f=w[0]||new i,f}},this.flush=()=>{let e,r,o=0,f=0;const a=[];do{if(t.rt=0,t.tt=n,e=t.Ye(4),1!=e&&0!=e)throw new s("deflating: "+t.Le);n-t.tt>0&&a.push(c.slice(0,t.rt)),f+=t.rt}while(t.We>0||0===t.tt);return t.Qe(),r=new i(f),a.forEach((e=>{r.set(e,o),o+=e.length})),r}}Je.prototype={He(e,t){const n=this;return n.Fe=new Ge,t||(t=15),n.Fe.He(n,e,t)},Ye(e){const t=this;return t.Fe?t.Fe.Ye(t,e):Re},Qe(){const e=this;if(!e.Fe)return Re;const t=e.Fe.Qe();return e.Fe=null,t},Xe(e,t){const n=this;return n.Fe?n.Fe.Xe(n,e,t):Re},Ze(e,t){const n=this;return n.Fe?n.Fe.Ze(n,e,t):Re},je(e,t,n){const r=this;let s=r.We;return s>n&&(s=n),0===s?0:(r.We-=s,e.set(r.et.subarray(r.nt,r.nt+s),t),r.nt+=s,r.qe+=s,s)},Te(){const e=this;let t=e.Fe.pending;t>e.tt&&(t=e.tt),0!==t&&(e.$e.set(e.Fe.Ue.subarray(e.Fe.Je,e.Fe.Je+t),e.rt),e.rt+=t,e.Fe.Je+=t,e.Ge+=t,e.tt-=t,e.Fe.pending-=t,0===e.Fe.pending&&(e.Fe.Je=0))}};const Xe=-2,Ye=-3,Ze=-5,$e=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],et=[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],tt=[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],nt=[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],rt=[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],st=[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],it=[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];function ot(){let e,t,n,r,s,i;function o(e,t,o,c,f,a,l,u,w,h,d){let p,y,m,b,g,k,v,S,z,C,x,_,A,I,D;C=0,g=o;do{n[e[t+C]]++,C++,g--}while(0!==g);if(n[0]==o)return l[0]=-1,u[0]=0,0;for(S=u[0],k=1;15>=k&&0===n[k];k++);for(v=k,k>S&&(S=k),g=15;0!==g&&0===n[g];g--);for(m=g,S>g&&(S=g),u[0]=S,I=1<k;k++,I<<=1)if(0>(I-=n[k]))return Ye;if(0>(I-=n[g]))return Ye;for(n[g]+=I,i[1]=k=0,C=1,A=2;0!=--g;)i[A]=k+=n[C],A++,C++;g=0,C=0;do{0!==(k=e[t+C])&&(d[i[k]++]=g),C++}while(++g=v;v++)for(p=n[v];0!=p--;){for(;v>_+S;){if(b++,_+=S,D=m-_,D=D>S?S:D,(y=1<<(k=v-_))>p+1&&(y-=p+1,A=v,D>k))for(;++kn[++A];)y-=n[A];if(D=1<1440)return Ye;s[b]=x=h[0],h[0]+=D,0!==b?(i[b]=g,r[0]=k,r[1]=S,k=g>>>_-S,r[2]=x-s[b-1]-k,w.set(r,3*(s[b-1]+k))):l[0]=x}for(r[1]=v-_,o>C?d[C]d[C]?0:96,r[2]=d[C++]):(r[0]=a[d[C]-c]+16+64,r[2]=f[d[C++]-c]):r[0]=192,y=1<>>_;D>k;k+=y)w.set(r,3*(x+k));for(k=1<>>=1)g^=k;for(g^=k,z=(1<<_)-1;(g&z)!=i[b];)b--,_-=S,z=(1<<_)-1}return 0!==I&&1!=m?Ze:0}function c(o){let c;for(e||(e=[],t=[],n=new f(16),r=[],s=new f(15),i=new f(16)),t.lengthc;c++)t[c]=0;for(c=0;16>c;c++)n[c]=0;for(c=0;3>c;c++)r[c]=0;s.set(n.subarray(0,15),0),i.set(n.subarray(0,16),0)}this.st=(n,r,s,i,f)=>{let a;return c(19),e[0]=0,a=o(n,0,19,19,null,null,s,r,i,e,t),a==Ye?f.Le="oversubscribed dynamic bit lengths tree":a!=Ze&&0!==r[0]||(f.Le="incomplete dynamic bit lengths tree",a=Ye),a},this.it=(n,r,s,i,f,a,l,u,w)=>{let h;return c(288),e[0]=0,h=o(s,0,n,257,nt,rt,a,i,u,e,t),0!=h||0===i[0]?(h==Ye?w.Le="oversubscribed literal/length tree":-4!=h&&(w.Le="incomplete literal/length tree",h=Ye),h):(c(288),h=o(s,n,r,0,st,it,l,f,u,e,t),0!=h||0===f[0]&&n>257?(h==Ye?w.Le="oversubscribed distance tree":h==Ze?(w.Le="incomplete distance tree",h=Ye):-4!=h&&(w.Le="empty distance tree with lengths",h=Ye),h):0)}}function ct(){const e=this;let t,n,r,s,i=0,o=0,c=0,f=0,a=0,l=0,u=0,w=0,h=0,d=0;function p(e,t,n,r,s,i,o,c){let f,a,l,u,w,h,d,p,y,m,b,g,k,v,S,z;d=c.nt,p=c.We,w=o.ot,h=o.ct,y=o.write,m=yh;)p--,w|=(255&c.ft(d++))<>=a[z+1],h-=a[z+1],0!=(16&u)){for(u&=15,k=a[z+2]+(w&$e[u]),w>>=u,h-=u;15>h;)p--,w|=(255&c.ft(d++))<>=a[z+1],h-=a[z+1],0!=(16&u)){for(u&=15;u>h;)p--,w|=(255&c.ft(d++))<>=u,h-=u,m-=k,v>y){S=y-v;do{S+=o.end}while(0>S);if(u=o.end-S,k>u){if(k-=u,y-S>0&&u>y-S)do{o.lt[y++]=o.lt[S++]}while(0!=--u);else o.lt.set(o.lt.subarray(S,S+u),y),y+=u,S+=u,u=0;S=0}}else S=y-v,y-S>0&&2>y-S?(o.lt[y++]=o.lt[S++],o.lt[y++]=o.lt[S++],k-=2):(o.lt.set(o.lt.subarray(S,S+2),y),y+=2,S+=2,k-=2);if(y-S>0&&k>y-S)do{o.lt[y++]=o.lt[S++]}while(0!=--k);else o.lt.set(o.lt.subarray(S,S+k),y),y+=k,S+=k,k=0;break}if(0!=(64&u))return c.Le="invalid distance code",k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,Ye;f+=a[z+2],f+=w&$e[u],z=3*(l+f),u=a[z]}break}if(0!=(64&u))return 0!=(32&u)?(k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,1):(c.Le="invalid literal/length code",k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,Ye);if(f+=a[z+2],f+=w&$e[u],z=3*(l+f),0===(u=a[z])){w>>=a[z+1],h-=a[z+1],o.lt[y++]=a[z+2],m--;break}}else w>>=a[z+1],h-=a[z+1],o.lt[y++]=a[z+2],m--}while(m>=258&&p>=10);return k=c.We-p,k=k>h>>3?h>>3:k,p+=k,d-=k,h-=k<<3,o.ot=w,o.ct=h,c.We=p,c.qe+=d-c.nt,c.nt=d,o.write=y,0}e.init=(e,i,o,c,f,a)=>{t=0,u=e,w=i,r=o,h=c,s=f,d=a,n=null},e.ut=(e,y,m)=>{let b,g,k,v,S,z,C,x=0,_=0,A=0;for(A=y.nt,v=y.We,x=e.ot,_=e.ct,S=e.write,z=S=258&&v>=10&&(e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,m=p(u,w,r,h,s,d,e,y),A=y.nt,v=y.We,x=e.ot,_=e.ct,S=e.write,z=S_;){if(0===v)return e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(A++))<<_,_+=8}if(g=3*(o+(x&$e[b])),x>>>=n[g+1],_-=n[g+1],k=n[g],0===k){f=n[g+2],t=6;break}if(0!=(16&k)){a=15&k,i=n[g+2],t=2;break}if(0==(64&k)){c=k,o=g/3+n[g+2];break}if(0!=(32&k)){t=7;break}return t=9,y.Le="invalid literal/length code",m=Ye,e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,e.wt(y,m);case 2:for(b=a;b>_;){if(0===v)return e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(A++))<<_,_+=8}i+=x&$e[b],x>>=b,_-=b,c=w,n=s,o=d,t=3;case 3:for(b=c;b>_;){if(0===v)return e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(A++))<<_,_+=8}if(g=3*(o+(x&$e[b])),x>>=n[g+1],_-=n[g+1],k=n[g],0!=(16&k)){a=15&k,l=n[g+2],t=4;break}if(0==(64&k)){c=k,o=g/3+n[g+2];break}return t=9,y.Le="invalid distance code",m=Ye,e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,e.wt(y,m);case 4:for(b=a;b>_;){if(0===v)return e.ot=x,e.ct=_,y.We=v,y.qe+=A-y.nt,y.nt=A,e.write=S,e.wt(y,m);m=0,v--,x|=(255&y.ft(A++))<<_,_+=8}l+=x&$e[b],x>>=b,_-=b,t=5;case 5:for(C=S-l;0>C;)C+=e.end;for(;0!==i;){if(0===z&&(S==e.end&&0!==e.read&&(S=0,z=S7&&(_-=8,v++,A--),e.write=S,m=e.wt(y,m),S=e.write,z=S{}}ot.dt=(e,t,n,r)=>(e[0]=9,t[0]=5,n[0]=et,r[0]=tt,0);const ft=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];function at(e,t){const n=this;let r,s=0,o=0,c=0,a=0;const l=[0],u=[0],w=new ct;let h=0,d=new f(4320);const p=new ot;n.ct=0,n.ot=0,n.lt=new i(t),n.end=t,n.read=0,n.write=0,n.reset=(e,t)=>{t&&(t[0]=0),6==s&&w.ht(e),s=0,n.ct=0,n.ot=0,n.read=n.write=0},n.reset(e,null),n.wt=(e,t)=>{let r,s,i;return s=e.rt,i=n.read,r=(i>n.write?n.end:n.write)-i,r>e.tt&&(r=e.tt),0!==r&&t==Ze&&(t=0),e.tt-=r,e.Ge+=r,e.$e.set(n.lt.subarray(i,i+r),s),s+=r,i+=r,i==n.end&&(i=0,n.write==n.end&&(n.write=0),r=n.write-i,r>e.tt&&(r=e.tt),0!==r&&t==Ze&&(t=0),e.tt-=r,e.Ge+=r,e.$e.set(n.lt.subarray(i,i+r),s),s+=r,i+=r),e.rt=s,n.read=i,t},n.ut=(e,t)=>{let i,f,y,m,b,g,k,v;for(m=e.nt,b=e.We,f=n.ot,y=n.ct,g=n.write,k=gy;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<>>1){case 0:f>>>=3,y-=3,i=7&y,f>>>=i,y-=i,s=1;break;case 1:S=[],z=[],C=[[]],x=[[]],ot.dt(S,z,C,x),w.init(S[0],z[0],C[0],0,x[0],0),f>>>=3,y-=3,s=6;break;case 2:f>>>=3,y-=3,s=3;break;case 3:return f>>>=3,y-=3,s=9,e.Le="invalid block type",t=Ye,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t)}break;case 1:for(;32>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<>>16&65535)!=(65535&f))return s=9,e.Le="invalid stored block lengths",t=Ye,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);o=65535&f,f=y=0,s=0!==o?2:0!==h?7:0;break;case 2:if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);if(0===k&&(g==n.end&&0!==n.read&&(g=0,k=gb&&(i=b),i>k&&(i=k),n.lt.set(e.je(m,i),g),m+=i,b-=i,g+=i,k-=i,0!=(o-=i))break;s=0!==h?7:0;break;case 3:for(;14>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<29||(i>>5&31)>29)return s=9,e.Le="too many length or distance symbols",t=Ye,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);if(i=258+(31&i)+(i>>5&31),!r||r.lengthv;v++)r[v]=0;f>>>=14,y-=14,a=0,s=4;case 4:for(;4+(c>>>10)>a;){for(;3>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<>>=3,y-=3}for(;19>a;)r[ft[a++]]=0;if(l[0]=7,i=p.st(r,l,u,d,e),0!=i)return(t=i)==Ye&&(r=null,s=9),n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);a=0,s=5;case 5:for(;i=c,258+(31&i)+(i>>5&31)>a;){let o,w;for(i=l[0];i>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<w)f>>>=i,y-=i,r[a++]=w;else{for(v=18==w?7:w-14,o=18==w?11:3;i+v>y;){if(0===b)return n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);t=0,b--,f|=(255&e.ft(m++))<>>=i,y-=i,o+=f&$e[v],f>>>=v,y-=v,v=a,i=c,v+o>258+(31&i)+(i>>5&31)||16==w&&1>v)return r=null,s=9,e.Le="invalid bit length repeat",t=Ye,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);w=16==w?r[v-1]:0;do{r[v++]=w}while(0!=--o);a=v}}if(u[0]=-1,_=[],A=[],I=[],D=[],_[0]=9,A[0]=6,i=c,i=p.it(257+(31&i),1+(i>>5&31),r,_,A,I,D,d,e),0!=i)return i==Ye&&(r=null,s=9),t=i,n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,n.wt(e,t);w.init(_[0],A[0],d,I[0],d,D[0]),s=6;case 6:if(n.ot=f,n.ct=y,e.We=b,e.qe+=m-e.nt,e.nt=m,n.write=g,1!=(t=w.ut(n,e,t)))return n.wt(e,t);if(t=0,w.ht(e),m=e.nt,b=e.We,f=n.ot,y=n.ct,g=n.write,k=g{n.reset(e,null),n.lt=null,d=null},n.yt=(e,t,r)=>{n.lt.set(e.subarray(t,t+r),0),n.read=n.write=r},n.bt=()=>1==s?1:0}const lt=13,ut=[0,0,255,255];function wt(){const e=this;function t(e){return e&&e.gt?(e.qe=e.Ge=0,e.Le=null,e.gt.mode=7,e.gt.kt.reset(e,null),0):Xe}e.mode=0,e.method=0,e.vt=[0],e.St=0,e.marker=0,e.zt=0,e.Ct=t=>(e.kt&&e.kt.ht(t),e.kt=null,0),e.xt=(n,r)=>(n.Le=null,e.kt=null,8>r||r>15?(e.Ct(n),Xe):(e.zt=r,n.gt.kt=new at(n,1<{let n,r;if(!e||!e.gt||!e.et)return Xe;const s=e.gt;for(t=4==t?Ze:0,n=Ze;;)switch(s.mode){case 0:if(0===e.We)return n;if(n=t,e.We--,e.qe++,8!=(15&(s.method=e.ft(e.nt++)))){s.mode=lt,e.Le="unknown compression method",s.marker=5;break}if(8+(s.method>>4)>s.zt){s.mode=lt,e.Le="invalid win size",s.marker=5;break}s.mode=1;case 1:if(0===e.We)return n;if(n=t,e.We--,e.qe++,r=255&e.ft(e.nt++),((s.method<<8)+r)%31!=0){s.mode=lt,e.Le="incorrect header check",s.marker=5;break}if(0==(32&r)){s.mode=7;break}s.mode=2;case 2:if(0===e.We)return n;n=t,e.We--,e.qe++,s.St=(255&e.ft(e.nt++))<<24&4278190080,s.mode=3;case 3:if(0===e.We)return n;n=t,e.We--,e.qe++,s.St+=(255&e.ft(e.nt++))<<16&16711680,s.mode=4;case 4:if(0===e.We)return n;n=t,e.We--,e.qe++,s.St+=(255&e.ft(e.nt++))<<8&65280,s.mode=5;case 5:return 0===e.We?n:(n=t,e.We--,e.qe++,s.St+=255&e.ft(e.nt++),s.mode=6,2);case 6:return s.mode=lt,e.Le="need dictionary",s.marker=0,Xe;case 7:if(n=s.kt.ut(e,n),n==Ye){s.mode=lt,s.marker=0;break}if(0==n&&(n=t),1!=n)return n;n=t,s.kt.reset(e,s.vt),s.mode=12;case 12:return e.We=0,1;case lt:return Ye;default:return Xe}},e.At=(e,t,n)=>{let r=0,s=n;if(!e||!e.gt||6!=e.gt.mode)return Xe;const i=e.gt;return s<1<{let n,r,s,i,o;if(!e||!e.gt)return Xe;const c=e.gt;if(c.mode!=lt&&(c.mode=lt,c.marker=0),0===(n=e.We))return Ze;for(r=e.nt,s=c.marker;0!==n&&4>s;)e.ft(r)==ut[s]?s++:s=0!==e.ft(r)?0:4-s,r++,n--;return e.qe+=r-e.nt,e.nt=r,e.We=n,c.marker=s,4!=s?Ye:(i=e.qe,o=e.Ge,t(e),e.qe=i,e.Ge=o,c.mode=7,0)},e.Dt=e=>e&&e.gt&&e.gt.kt?e.gt.kt.bt():Xe}function ht(){}function dt(e){const t=new ht,n=e&&e.chunkSize?r.floor(2*e.chunkSize):131072,o=new i(n);let c=!1;t.xt(),t.$e=o,this.append=(e,r)=>{const f=[];let a,l,u=0,w=0,h=0;if(0!==e.length){t.nt=0,t.et=e,t.We=e.length;do{if(t.rt=0,t.tt=n,0!==t.We||c||(t.nt=0,c=!0),a=t._t(0),c&&a===Ze){if(0!==t.We)throw new s("inflating: bad input")}else if(0!==a&&1!==a)throw new s("inflating: "+t.Le);if((c||1===a)&&t.We===e.length)throw new s("inflating: bad input");t.rt&&(t.rt===n?f.push(new i(o)):f.push(o.slice(0,t.rt))),h+=t.rt,r&&t.nt>0&&t.nt!=u&&(r(t.nt),u=t.nt)}while(t.We>0||0===t.tt);return f.length>1?(l=new i(h),f.forEach((e=>{l.set(e,w),w+=e.length}))):l=f[0]||new i,l}},this.flush=()=>{t.Ct()}}ht.prototype={xt(e){const t=this;return t.gt=new wt,e||(e=15),t.gt.xt(t,e)},_t(e){const t=this;return t.gt?t.gt._t(t,e):Xe},Ct(){const e=this;if(!e.gt)return Xe;const t=e.gt.Ct(e);return e.gt=null,t},It(){const e=this;return e.gt?e.gt.It(e):Xe},At(e,t){const n=this;return n.gt?n.gt.At(n,e,t):Xe},ft(e){return this.et[e]},je(e,t){return this.et.subarray(e,e+t)}},self.initCodec=()=>{self.Deflate=Qe,self.Inflate=dt};\n'],{type:"text/javascript"}));e({workerScripts:{inflate:[t],deflate:[t]}});} - -/* - Copyright (c) 2022 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. - */ -const ERR_ITERATOR_COMPLETED_TOO_SOON = "Writer iterator completed too soon"; -const HTTP_HEADER_CONTENT_TYPE = "Content-Type"; -const DEFAULT_CHUNK_SIZE = 64 * 1024; - -const PROPERTY_NAME_WRITABLE = "writable"; - -class Stream { - - constructor() { - this.size = 0; - } - - init() { - this.initialized = true; - } -} - -class Reader extends Stream { - - get readable() { - const reader = this; - const { chunkSize = DEFAULT_CHUNK_SIZE } = reader; - const readable = new ReadableStream({ - start() { - this.chunkOffset = 0; - }, - async pull(controller) { - const { offset = 0, size, diskNumberStart } = readable; - const { chunkOffset } = this; - controller.enqueue(await readUint8Array(reader, offset + chunkOffset, Math.min(chunkSize, size - chunkOffset), diskNumberStart)); - if (chunkOffset + chunkSize > size) { - controller.close(); - } else { - this.chunkOffset += chunkSize; - } - } - }); - return readable; - } -} - -class BlobReader extends Reader { - - constructor(blob) { - super(); - Object.assign(this, { - blob, - size: blob.size - }); - } - - async readUint8Array(offset, length) { - const reader = this; - const offsetEnd = offset + length; - const blob = offset || offsetEnd < reader.size ? reader.blob.slice(offset, offsetEnd) : reader.blob; - return new Uint8Array(await blob.arrayBuffer()); - } -} - -class BlobWriter extends Stream { - - constructor(contentType) { - super(); - const writer = this; - const transformStream = new TransformStream(); - const headers = []; - if (contentType) { - headers.push([HTTP_HEADER_CONTENT_TYPE, contentType]); - } - Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, { - get() { - return transformStream.writable; - } - }); - writer.blob = new Response(transformStream.readable, { headers }).blob(); - } - - getData() { - return this.blob; - } -} - -class TextWriter extends BlobWriter { - - constructor(encoding) { - super(encoding); - Object.assign(this, { - encoding, - utf8: !encoding || encoding.toLowerCase() == "utf-8" - }); - } - - async getData() { - const { - encoding, - utf8 - } = this; - const blob = await super.getData(); - if (blob.text && utf8) { - return blob.text(); - } else { - const reader = new FileReader(); - return new Promise((resolve, reject) => { - Object.assign(reader, { - onload: ({ target }) => resolve(target.result), - onerror: () => reject(reader.error) - }); - reader.readAsText(blob, encoding); - }); - } - } -} - -class SplitDataReader extends Reader { - - constructor(readers) { - super(); - this.readers = readers; - } - - async init() { - const reader = this; - const { readers } = reader; - reader.lastDiskNumber = 0; - await Promise.all(readers.map(async diskReader => { - await diskReader.init(); - reader.size += diskReader.size; - })); - super.init(); - } - - async readUint8Array(offset, length, diskNumber = 0) { - const reader = this; - const { readers } = this; - let result; - let currentDiskNumber = diskNumber; - if (currentDiskNumber == -1) { - currentDiskNumber = readers.length - 1; - } - let currentReaderOffset = offset; - while (currentReaderOffset >= readers[currentDiskNumber].size) { - currentReaderOffset -= readers[currentDiskNumber].size; - currentDiskNumber++; - } - const currentReader = readers[currentDiskNumber]; - const currentReaderSize = currentReader.size; - if (currentReaderOffset + length <= currentReaderSize) { - result = await readUint8Array(currentReader, currentReaderOffset, length); - } else { - const chunkLength = currentReaderSize - currentReaderOffset; - result = new Uint8Array(length); - result.set(await readUint8Array(currentReader, currentReaderOffset, chunkLength)); - result.set(await reader.readUint8Array(offset + chunkLength, length - chunkLength, diskNumber), chunkLength); - } - reader.lastDiskNumber = Math.max(currentDiskNumber, reader.lastDiskNumber); - return result; - } -} - -class SplitDataWriter extends Stream { - - constructor(writerGenerator, maxSize = 4294967295) { - super(); - const zipWriter = this; - Object.assign(zipWriter, { - diskNumber: 0, - diskOffset: 0, - size: 0, - maxSize, - availableSize: maxSize - }); - let diskSourceWriter, diskWritable, diskWriter; - const writable = new WritableStream({ - async write(chunk) { - const { availableSize } = zipWriter; - if (!diskWriter) { - const { value, done } = await writerGenerator.next(); - if (done && !value) { - throw new Error(ERR_ITERATOR_COMPLETED_TOO_SOON); - } else { - diskSourceWriter = value; - diskSourceWriter.size = 0; - if (diskSourceWriter.maxSize) { - zipWriter.maxSize = diskSourceWriter.maxSize; - } - zipWriter.availableSize = zipWriter.maxSize; - await initStream(diskSourceWriter); - diskWritable = value.writable; - diskWriter = diskWritable.getWriter(); - } - await this.write(chunk); - } else if (chunk.length >= availableSize) { - await writeChunk(chunk.slice(0, availableSize)); - await closeDisk(); - zipWriter.diskOffset += diskSourceWriter.size; - zipWriter.diskNumber++; - diskWriter = null; - await this.write(chunk.slice(availableSize)); - } else { - await writeChunk(chunk); - } - }, - async close() { - await diskWriter.ready; - await closeDisk(); - } - }); - Object.defineProperty(zipWriter, PROPERTY_NAME_WRITABLE, { - get() { - return writable; - } - }); - - async function writeChunk(chunk) { - const chunkLength = chunk.length; - if (chunkLength) { - await diskWriter.ready; - await diskWriter.write(chunk); - diskSourceWriter.size += chunkLength; - zipWriter.size += chunkLength; - zipWriter.availableSize -= chunkLength; - } - } - - async function closeDisk() { - diskWritable.size = diskSourceWriter.size; - await diskWriter.close(); - } - } -} - -async function initStream(stream, initSize) { - if (stream.init && !stream.initialized) { - await stream.init(initSize); - } -} - -function initReader(reader) { - if (Array.isArray(reader)) { - reader = new SplitDataReader(reader); - } - if (reader instanceof ReadableStream) { - reader = { - readable: reader - }; - } - return reader; -} - -function initWriter(writer) { - if (writer.writable === UNDEFINED_VALUE && typeof writer.next == FUNCTION_TYPE$1) { - writer = new SplitDataWriter(writer); - } - if (writer instanceof WritableStream) { - writer = { - writable: writer - }; - } - const { writable } = writer; - if (writable.size === UNDEFINED_VALUE) { - writable.size = 0; - } - const splitZipFile = writer instanceof SplitDataWriter; - if (!splitZipFile) { - Object.assign(writer, { - diskNumber: 0, - diskOffset: 0, - availableSize: Infinity, - maxSize: Infinity - }); - } - return writer; -} - -function readUint8Array(reader, offset, size, diskNumber) { - return reader.readUint8Array(offset, size, diskNumber); -} - -/* - Copyright (c) 2022 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. - */ - -/* global TextDecoder */ - -const CP437 = "\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ".split(""); -const VALID_CP437 = CP437.length == 256; - -function decodeCP437(stringValue) { - if (VALID_CP437) { - let result = ""; - for (let indexCharacter = 0; indexCharacter < stringValue.length; indexCharacter++) { - result += CP437[stringValue[indexCharacter]]; - } - return result; - } else { - return new TextDecoder().decode(stringValue); - } -} - -/* - Copyright (c) 2022 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 decodeText(value, encoding) { - if (encoding && encoding.trim().toLowerCase() == "cp437") { - return decodeCP437(value); - } else { - return new TextDecoder(encoding).decode(value); - } -} - -/* - Copyright (c) 2022 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. - */ - -const PROPERTY_NAME_FILENAME = "filename"; -const PROPERTY_NAME_RAW_FILENAME = "rawFilename"; -const PROPERTY_NAME_COMMENT = "comment"; -const PROPERTY_NAME_RAW_COMMENT = "rawComment"; -const PROPERTY_NAME_UNCOMPPRESSED_SIZE = "uncompressedSize"; -const PROPERTY_NAME_COMPPRESSED_SIZE = "compressedSize"; -const PROPERTY_NAME_OFFSET = "offset"; -const PROPERTY_NAME_DISK_NUMBER_START = "diskNumberStart"; -const PROPERTY_NAME_LAST_MODIFICATION_DATE = "lastModDate"; -const PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE = "rawLastModDate"; -const PROPERTY_NAME_LAST_ACCESS_DATE = "lastAccessDate"; -const PROPERTY_NAME_RAW_LAST_ACCESS_DATE = "rawLastAccessDate"; -const PROPERTY_NAME_CREATION_DATE = "creationDate"; -const PROPERTY_NAME_RAW_CREATION_DATE = "rawCreationDate"; -const PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE = "internalFileAttribute"; -const PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE = "externalFileAttribute"; -const PROPERTY_NAME_MS_DOS_COMPATIBLE = "msDosCompatible"; -const PROPERTY_NAME_ZIP64 = "zip64"; - -const PROPERTY_NAMES = [ - PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, PROPERTY_NAME_COMPPRESSED_SIZE, PROPERTY_NAME_UNCOMPPRESSED_SIZE, - PROPERTY_NAME_LAST_MODIFICATION_DATE, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE, PROPERTY_NAME_COMMENT, PROPERTY_NAME_RAW_COMMENT, - PROPERTY_NAME_LAST_ACCESS_DATE, PROPERTY_NAME_CREATION_DATE, PROPERTY_NAME_OFFSET, PROPERTY_NAME_DISK_NUMBER_START, - PROPERTY_NAME_DISK_NUMBER_START, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, - PROPERTY_NAME_MS_DOS_COMPATIBLE, PROPERTY_NAME_ZIP64, - "directory", "bitFlag", "encrypted", "signature", "filenameUTF8", "commentUTF8", "compressionMethod", "version", "versionMadeBy", - "extraField", "rawExtraField", "extraFieldZip64", "extraFieldUnicodePath", "extraFieldUnicodeComment", "extraFieldAES", "extraFieldNTFS", - "extraFieldExtendedTimestamp"]; - -class Entry { - - constructor(data) { - PROPERTY_NAMES.forEach(name => this[name] = data[name]); - } - -} - -/* - Copyright (c) 2022 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. - */ - -const ERR_BAD_FORMAT = "File format is not recognized"; -const ERR_EOCDR_NOT_FOUND = "End of central directory not found"; -const ERR_EOCDR_ZIP64_NOT_FOUND = "End of Zip64 central directory not found"; -const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found"; -const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found"; -const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found"; -const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found"; -const ERR_ENCRYPTED = "File contains encrypted entry"; -const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported"; -const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported"; -const ERR_SPLIT_ZIP_FILE = "Split zip file"; -const CHARSET_UTF8 = "utf-8"; -const CHARSET_CP437 = "cp437"; -const ZIP64_PROPERTIES = [ - [PROPERTY_NAME_UNCOMPPRESSED_SIZE, MAX_32_BITS], - [PROPERTY_NAME_COMPPRESSED_SIZE, MAX_32_BITS], - [PROPERTY_NAME_OFFSET, MAX_32_BITS], - [PROPERTY_NAME_DISK_NUMBER_START, MAX_16_BITS] -]; -const ZIP64_EXTRACTION = { - [MAX_16_BITS]: { - getValue: getUint32, - bytes: 4 - }, - [MAX_32_BITS]: { - getValue: getBigUint64, - bytes: 8 - } -}; - -class ZipReader { - - constructor(reader, options = {}) { - Object.assign(this, { - reader: initReader(reader), - options, - config: getConfiguration() - }); - } - - async* getEntriesGenerator(options = {}) { - const zipReader = this; - let { reader } = zipReader; - const { config } = zipReader; - await initStream(reader); - if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) { - reader = new BlobReader(await new Response(reader.readable).blob()); - await initStream(reader); - } - if (reader.size < END_OF_CENTRAL_DIR_LENGTH) { - throw new Error(ERR_BAD_FORMAT); - } - reader.chunkSize = getChunkSize(config); - const endOfDirectoryInfo = await seekSignature(reader, END_OF_CENTRAL_DIR_SIGNATURE, reader.size, END_OF_CENTRAL_DIR_LENGTH, MAX_16_BITS * 16); - if (!endOfDirectoryInfo) { - const signatureArray = await readUint8Array(reader, 0, 4); - const signatureView = getDataView(signatureArray); - if (getUint32(signatureView) == SPLIT_ZIP_FILE_SIGNATURE) { - throw new Error(ERR_SPLIT_ZIP_FILE); - } else { - throw new Error(ERR_EOCDR_NOT_FOUND); - } - } - const endOfDirectoryView = getDataView(endOfDirectoryInfo); - let directoryDataLength = getUint32(endOfDirectoryView, 12); - let directoryDataOffset = getUint32(endOfDirectoryView, 16); - const commentOffset = endOfDirectoryInfo.offset; - const commentLength = getUint16(endOfDirectoryView, 20); - const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength; - let lastDiskNumber = getUint16(endOfDirectoryView, 4); - const expectedLastDiskNumber = reader.lastDiskNumber || 0; - let diskNumber = getUint16(endOfDirectoryView, 6); - let filesLength = getUint16(endOfDirectoryView, 8); - let prependedDataLength = 0; - let startOffset = 0; - if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || filesLength == MAX_16_BITS || diskNumber == MAX_16_BITS) { - const endOfDirectoryLocatorArray = await readUint8Array(reader, endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH); - const endOfDirectoryLocatorView = getDataView(endOfDirectoryLocatorArray); - if (getUint32(endOfDirectoryLocatorView, 0) != ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE) { - throw new Error(ERR_EOCDR_ZIP64_NOT_FOUND); - } - directoryDataOffset = getBigUint64(endOfDirectoryLocatorView, 8); - let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1); - let endOfDirectoryView = getDataView(endOfDirectoryArray); - const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH; - if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) { - const originalDirectoryDataOffset = directoryDataOffset; - directoryDataOffset = expectedDirectoryDataOffset; - prependedDataLength = directoryDataOffset - originalDirectoryDataOffset; - endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1); - endOfDirectoryView = getDataView(endOfDirectoryArray); - } - if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) { - throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND); - } - if (lastDiskNumber == MAX_16_BITS) { - lastDiskNumber = getUint32(endOfDirectoryView, 16); - } - if (diskNumber == MAX_16_BITS) { - diskNumber = getUint32(endOfDirectoryView, 20); - } - if (filesLength == MAX_16_BITS) { - filesLength = getBigUint64(endOfDirectoryView, 32); - } - if (directoryDataLength == MAX_32_BITS) { - directoryDataLength = getBigUint64(endOfDirectoryView, 40); - } - directoryDataOffset -= directoryDataLength; - } - if (expectedLastDiskNumber != lastDiskNumber) { - throw new Error(ERR_SPLIT_ZIP_FILE); - } - if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) { - throw new Error(ERR_BAD_FORMAT); - } - let offset = 0; - let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber); - let directoryView = getDataView(directoryArray); - if (directoryDataLength) { - const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - directoryDataLength; - if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) { - const originalDirectoryDataOffset = directoryDataOffset; - directoryDataOffset = expectedDirectoryDataOffset; - prependedDataLength = directoryDataOffset - originalDirectoryDataOffset; - directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber); - directoryView = getDataView(directoryArray); - } - } - if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) { - throw new Error(ERR_BAD_FORMAT); - } - const filenameEncoding = getOptionValue(zipReader, options, "filenameEncoding"); - const commentEncoding = getOptionValue(zipReader, options, "commentEncoding"); - for (let indexFile = 0; indexFile < filesLength; indexFile++) { - const fileEntry = new ZipEntry(reader, config, zipReader.options); - if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) { - throw new Error(ERR_CENTRAL_DIRECTORY_NOT_FOUND); - } - readCommonHeader(fileEntry, directoryView, offset + 6); - const languageEncodingFlag = Boolean(fileEntry.bitFlag.languageEncodingFlag); - const filenameOffset = offset + 46; - const extraFieldOffset = filenameOffset + fileEntry.filenameLength; - const commentOffset = extraFieldOffset + fileEntry.extraFieldLength; - const versionMadeBy = getUint16(directoryView, offset + 4); - const msDosCompatible = (versionMadeBy & 0) == 0; - const rawFilename = directoryArray.subarray(filenameOffset, extraFieldOffset); - const commentLength = getUint16(directoryView, offset + 32); - const endOffset = commentOffset + commentLength; - const rawComment = directoryArray.subarray(commentOffset, endOffset); - const filenameUTF8 = languageEncodingFlag; - const commentUTF8 = languageEncodingFlag; - const directory = msDosCompatible && ((getUint8(directoryView, offset + 38) & FILE_ATTR_MSDOS_DIR_MASK) == FILE_ATTR_MSDOS_DIR_MASK); - const offsetFileEntry = getUint32(directoryView, offset + 42) + prependedDataLength; - Object.assign(fileEntry, { - versionMadeBy, - msDosCompatible, - compressedSize: 0, - uncompressedSize: 0, - commentLength, - directory, - offset: offsetFileEntry, - diskNumberStart: getUint16(directoryView, offset + 34), - internalFileAttribute: getUint16(directoryView, offset + 36), - externalFileAttribute: getUint32(directoryView, offset + 38), - rawFilename, - filenameUTF8, - commentUTF8, - rawExtraField: directoryArray.subarray(extraFieldOffset, commentOffset) - }); - const [filename, comment] = await Promise.all([ - decodeText(rawFilename, filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437), - decodeText(rawComment, commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437) - ]); - Object.assign(fileEntry, { - rawComment, - filename, - comment, - directory: directory || filename.endsWith(DIRECTORY_SIGNATURE) - }); - startOffset = Math.max(offsetFileEntry, startOffset); - await readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6); - const entry = new Entry(fileEntry); - entry.getData = (writer, options) => fileEntry.getData(writer, entry, options); - offset = endOffset; - const { onprogress } = options; - if (onprogress) { - try { - await onprogress(indexFile + 1, filesLength, new Entry(fileEntry)); - } catch (_error) { - // ignored - } - } - yield entry; - } - const extractPrependedData = getOptionValue(zipReader, options, "extractPrependedData"); - const extractAppendedData = getOptionValue(zipReader, options, "extractAppendedData"); - if (extractPrependedData) { - zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : new Uint8Array(); - } - zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) : new Uint8Array(); - if (extractAppendedData) { - zipReader.appendedData = appendedDataOffset < reader.size ? await readUint8Array(reader, appendedDataOffset, reader.size - appendedDataOffset) : new Uint8Array(); - } - return true; - } - - async getEntries(options = {}) { - const entries = []; - for await (const entry of this.getEntriesGenerator(options)) { - entries.push(entry); - } - return entries; - } - - async close() { - } -} - -class ZipEntry { - - constructor(reader, config, options) { - Object.assign(this, { - reader, - config, - options - }); - } - - async getData(writer, fileEntry, options = {}) { - const zipEntry = this; - const { - reader, - offset, - diskNumberStart, - extraFieldAES, - compressionMethod, - config, - bitFlag, - signature, - rawLastModDate, - uncompressedSize, - compressedSize - } = zipEntry; - const localDirectory = zipEntry.localDirectory = {}; - const dataArray = await readUint8Array(reader, offset, 30, diskNumberStart); - const dataView = getDataView(dataArray); - let password = getOptionValue(zipEntry, options, "password"); - password = password && password.length && password; - if (extraFieldAES) { - if (extraFieldAES.originalCompressionMethod != COMPRESSION_METHOD_AES) { - throw new Error(ERR_UNSUPPORTED_COMPRESSION); - } - } - if (compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE) { - throw new Error(ERR_UNSUPPORTED_COMPRESSION); - } - if (getUint32(dataView, 0) != LOCAL_FILE_HEADER_SIGNATURE) { - throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND); - } - readCommonHeader(localDirectory, dataView, 4); - localDirectory.rawExtraField = localDirectory.extraFieldLength ? - await readUint8Array(reader, offset + 30 + localDirectory.filenameLength, localDirectory.extraFieldLength, diskNumberStart) : - new Uint8Array(); - await readCommonFooter(zipEntry, localDirectory, dataView, 4); - Object.assign(fileEntry, { - lastAccessDate: localDirectory.lastAccessDate, - creationDate: localDirectory.creationDate - }); - const encrypted = zipEntry.encrypted && localDirectory.encrypted; - const zipCrypto = encrypted && !extraFieldAES; - if (encrypted) { - if (!zipCrypto && extraFieldAES.strength === UNDEFINED_VALUE) { - throw new Error(ERR_UNSUPPORTED_ENCRYPTION); - } else if (!password) { - throw new Error(ERR_ENCRYPTED); - } - } - const dataOffset = offset + 30 + localDirectory.filenameLength + localDirectory.extraFieldLength; - const readable = reader.readable; - readable.diskNumberStart = diskNumberStart; - readable.offset = dataOffset; - let size = readable.size = compressedSize; - const signal = getOptionValue(zipEntry, options, "signal"); - const checkPasswordOnly = getOptionValue(zipEntry, options, "checkPasswordOnly"); - if (checkPasswordOnly) { - writer = new WritableStream(); - } - writer = initWriter(writer); - await initStream(writer, uncompressedSize); - const { writable } = writer; - const { onstart, onprogress, onend } = options; - const workerOptions = { - options: { - codecType: CODEC_INFLATE, - password, - zipCrypto, - encryptionStrength: extraFieldAES && extraFieldAES.strength, - signed: getOptionValue(zipEntry, options, "checkSignature"), - passwordVerification: zipCrypto && (bitFlag.dataDescriptor ? ((rawLastModDate >>> 8) & 0xFF) : ((signature >>> 24) & 0xFF)), - signature, - compressed: compressionMethod != 0, - encrypted, - useWebWorkers: getOptionValue(zipEntry, options, "useWebWorkers"), - useCompressionStream: getOptionValue(zipEntry, options, "useCompressionStream"), - transferStreams: getOptionValue(zipEntry, options, "transferStreams"), - checkPasswordOnly - }, - config, - streamOptions: { signal, size, onstart, onprogress, onend } - }; - let outputSize = 0; - try { - ({ outputSize } = (await runWorker({ readable, writable }, workerOptions))); - } catch (error) { - if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) { - throw error; - } - } finally { - const preventClose = getOptionValue(zipEntry, options, "preventClose"); - writable.size += outputSize; - if (!preventClose && !writable.locked) { - await writable.close(); - } - } - return checkPasswordOnly ? undefined : writer.getData ? writer.getData() : writable; - } -} - -function readCommonHeader(directory, dataView, offset) { - const rawBitFlag = directory.rawBitFlag = getUint16(dataView, offset + 2); - const encrypted = (rawBitFlag & BITFLAG_ENCRYPTED) == BITFLAG_ENCRYPTED; - const rawLastModDate = getUint32(dataView, offset + 6); - Object.assign(directory, { - encrypted, - version: getUint16(dataView, offset), - bitFlag: { - level: (rawBitFlag & BITFLAG_LEVEL) >> 1, - dataDescriptor: (rawBitFlag & BITFLAG_DATA_DESCRIPTOR) == BITFLAG_DATA_DESCRIPTOR, - languageEncodingFlag: (rawBitFlag & BITFLAG_LANG_ENCODING_FLAG) == BITFLAG_LANG_ENCODING_FLAG - }, - rawLastModDate, - lastModDate: getDate(rawLastModDate), - filenameLength: getUint16(dataView, offset + 22), - extraFieldLength: getUint16(dataView, offset + 24) - }); -} - -async function readCommonFooter(fileEntry, directory, dataView, offset) { - const { rawExtraField } = directory; - const extraField = directory.extraField = new Map(); - const rawExtraFieldView = getDataView(new Uint8Array(rawExtraField)); - let offsetExtraField = 0; - try { - while (offsetExtraField < rawExtraField.length) { - const type = getUint16(rawExtraFieldView, offsetExtraField); - const size = getUint16(rawExtraFieldView, offsetExtraField + 2); - extraField.set(type, { - type, - data: rawExtraField.slice(offsetExtraField + 4, offsetExtraField + 4 + size) - }); - offsetExtraField += 4 + size; - } - } catch (_error) { - // ignored - } - const compressionMethod = getUint16(dataView, offset + 4); - Object.assign(directory, { - signature: getUint32(dataView, offset + 10), - uncompressedSize: getUint32(dataView, offset + 18), - compressedSize: getUint32(dataView, offset + 14) - }); - const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64); - if (extraFieldZip64) { - readExtraFieldZip64(extraFieldZip64, directory); - directory.extraFieldZip64 = extraFieldZip64; - } - const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH); - if (extraFieldUnicodePath) { - await readExtraFieldUnicode(extraFieldUnicodePath, PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, directory, fileEntry); - directory.extraFieldUnicodePath = extraFieldUnicodePath; - } - const extraFieldUnicodeComment = extraField.get(EXTRAFIELD_TYPE_UNICODE_COMMENT); - if (extraFieldUnicodeComment) { - await readExtraFieldUnicode(extraFieldUnicodeComment, PROPERTY_NAME_COMMENT, PROPERTY_NAME_RAW_COMMENT, directory, fileEntry); - directory.extraFieldUnicodeComment = extraFieldUnicodeComment; - } - const extraFieldAES = extraField.get(EXTRAFIELD_TYPE_AES); - if (extraFieldAES) { - readExtraFieldAES(extraFieldAES, directory, compressionMethod); - directory.extraFieldAES = extraFieldAES; - } else { - directory.compressionMethod = compressionMethod; - } - const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS); - if (extraFieldNTFS) { - readExtraFieldNTFS(extraFieldNTFS, directory); - directory.extraFieldNTFS = extraFieldNTFS; - } - const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP); - if (extraFieldExtendedTimestamp) { - readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory); - directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp; - } -} - -function readExtraFieldZip64(extraFieldZip64, directory) { - directory.zip64 = true; - const extraFieldView = getDataView(extraFieldZip64.data); - const missingProperties = ZIP64_PROPERTIES.filter(([propertyName, max]) => directory[propertyName] == max); - for (let indexMissingProperty = 0, offset = 0; indexMissingProperty < missingProperties.length; indexMissingProperty++) { - const [propertyName, max] = missingProperties[indexMissingProperty]; - if (directory[propertyName] == max) { - const extraction = ZIP64_EXTRACTION[max]; - directory[propertyName] = extraFieldZip64[propertyName] = extraction.getValue(extraFieldView, offset); - offset += extraction.bytes; - } else if (extraFieldZip64[propertyName]) { - throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND); - } - } -} - -async function readExtraFieldUnicode(extraFieldUnicode, propertyName, rawPropertyName, directory, fileEntry) { - const extraFieldView = getDataView(extraFieldUnicode.data); - const crc32 = new Crc32(); - crc32.append(fileEntry[rawPropertyName]); - const dataViewSignature = getDataView(new Uint8Array(4)); - dataViewSignature.setUint32(0, crc32.get(), true); - Object.assign(extraFieldUnicode, { - version: getUint8(extraFieldView, 0), - signature: getUint32(extraFieldView, 1), - [propertyName]: await decodeText(extraFieldUnicode.data.subarray(5)), - valid: !fileEntry.bitFlag.languageEncodingFlag && extraFieldUnicode.signature == getUint32(dataViewSignature, 0) - }); - if (extraFieldUnicode.valid) { - directory[propertyName] = extraFieldUnicode[propertyName]; - directory[propertyName + "UTF8"] = true; - } -} - -function readExtraFieldAES(extraFieldAES, directory, compressionMethod) { - const extraFieldView = getDataView(extraFieldAES.data); - const strength = getUint8(extraFieldView, 4); - Object.assign(extraFieldAES, { - vendorVersion: getUint8(extraFieldView, 0), - vendorId: getUint8(extraFieldView, 2), - strength, - originalCompressionMethod: compressionMethod, - compressionMethod: getUint16(extraFieldView, 5) - }); - directory.compressionMethod = extraFieldAES.compressionMethod; -} - -function readExtraFieldNTFS(extraFieldNTFS, directory) { - const extraFieldView = getDataView(extraFieldNTFS.data); - let offsetExtraField = 4; - let tag1Data; - try { - while (offsetExtraField < extraFieldNTFS.data.length && !tag1Data) { - const tagValue = getUint16(extraFieldView, offsetExtraField); - const attributeSize = getUint16(extraFieldView, offsetExtraField + 2); - if (tagValue == EXTRAFIELD_TYPE_NTFS_TAG1) { - tag1Data = extraFieldNTFS.data.slice(offsetExtraField + 4, offsetExtraField + 4 + attributeSize); - } - offsetExtraField += 4 + attributeSize; - } - } catch (_error) { - // ignored - } - try { - if (tag1Data && tag1Data.length == 24) { - const tag1View = getDataView(tag1Data); - const rawLastModDate = tag1View.getBigUint64(0, true); - const rawLastAccessDate = tag1View.getBigUint64(8, true); - const rawCreationDate = tag1View.getBigUint64(16, true); - Object.assign(extraFieldNTFS, { - rawLastModDate, - rawLastAccessDate, - rawCreationDate - }); - const lastModDate = getDateNTFS(rawLastModDate); - const lastAccessDate = getDateNTFS(rawLastAccessDate); - const creationDate = getDateNTFS(rawCreationDate); - const extraFieldData = { lastModDate, lastAccessDate, creationDate }; - Object.assign(extraFieldNTFS, extraFieldData); - Object.assign(directory, extraFieldData); - } - } catch (_error) { - // ignored - } -} - -function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory) { - const extraFieldView = getDataView(extraFieldExtendedTimestamp.data); - const flags = getUint8(extraFieldView, 0); - const timeProperties = []; - const timeRawProperties = []; - if ((flags & 0x1) == 0x1) { - timeProperties.push(PROPERTY_NAME_LAST_MODIFICATION_DATE); - timeRawProperties.push(PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE); - } - if ((flags & 0x2) == 0x2) { - timeProperties.push(PROPERTY_NAME_LAST_ACCESS_DATE); - timeRawProperties.push(PROPERTY_NAME_RAW_LAST_ACCESS_DATE); - } - if ((flags & 0x4) == 0x4) { - timeProperties.push(PROPERTY_NAME_CREATION_DATE); - timeRawProperties.push(PROPERTY_NAME_RAW_CREATION_DATE); - } - let offset = 1; - timeProperties.forEach((propertyName, indexProperty) => { - if (extraFieldExtendedTimestamp.data.length >= offset + 4) { - const time = getUint32(extraFieldView, offset); - directory[propertyName] = extraFieldExtendedTimestamp[propertyName] = new Date(time * 1000); - const rawPropertyName = timeRawProperties[indexProperty]; - extraFieldExtendedTimestamp[rawPropertyName] = time; - } - offset += 4; - }); -} - -async function seekSignature(reader, signature, startOffset, minimumBytes, maximumLength) { - const signatureArray = new Uint8Array(4); - const signatureView = getDataView(signatureArray); - setUint32(signatureView, 0, signature); - const maximumBytes = minimumBytes + maximumLength; - return (await seek(minimumBytes)) || await seek(Math.min(maximumBytes, startOffset)); - - async function seek(length) { - const offset = startOffset - length; - const bytes = await readUint8Array(reader, offset, length); - for (let indexByte = bytes.length - minimumBytes; indexByte >= 0; indexByte--) { - if (bytes[indexByte] == signatureArray[0] && bytes[indexByte + 1] == signatureArray[1] && - bytes[indexByte + 2] == signatureArray[2] && bytes[indexByte + 3] == signatureArray[3]) { - return { - offset: offset + indexByte, - buffer: bytes.slice(indexByte, indexByte + minimumBytes).buffer - }; - } - } - } -} - -function getOptionValue(zipReader, options, name) { - return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name]; -} - -function getDate(timeRaw) { - const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & 0x0000ffff; - try { - return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0); - } catch (_error) { - // ignored - } -} - -function getDateNTFS(timeRaw) { - return new Date((Number((timeRaw / BigInt(10000)) - BigInt(11644473600000)))); -} - -function getUint8(view, offset) { - return view.getUint8(offset); -} - -function getUint16(view, offset) { - return view.getUint16(offset, true); -} - -function getUint32(view, offset) { - return view.getUint32(offset, true); -} - -function getBigUint64(view, offset) { - return Number(view.getBigUint64(offset, true)); -} - -function setUint32(view, offset, value) { - view.setUint32(offset, value, true); -} - -function getDataView(array) { - return new DataView(array.buffer); -} - -/* - Copyright (c) 2022 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. - */ - -let baseURL; -try { - baseURL = import.meta.url; -} catch (_error) { - // ignored -} -configure({ baseURL }); -e(configure); - -/// - -configure({ Deflate: ZipDeflate, Inflate: ZipInflate }); - -function parseIndex(index, size) { - return index < 0 ? - Math.max(index + size, 0) : - Math.min(index, size); -} -class BlobEntryReaderImpl extends Reader { - constructor(blob, entryMetadata) { - super(blob); - this.blob = blob; - this.offset = entryMetadata.offset + entryMetadata.headerSize; - this.size = entryMetadata.compressedSize; - } - async readUint8Array(index, length) { - const start = parseIndex(index, this.size) + this.offset; - const end = parseIndex(index + length, this.size) + this.offset; - const blob = this.blob.slice(start, end); - return new Uint8Array(await blob.arrayBuffer()); - } -} -/** - * Represents a {@link Reader} instance used to read data of an entry in a zip - * file provided as a {@link Blob}. It directly reads data if it is uncompressed. - */ -class BlobEntryReader extends Reader { - /** - * @param blob - The blob to read data from, usually the outer zip file. - * @param entry - The entry to read data of, usually the inner zip file. - * @param mimeString - The MIME type of the data. - * @param options - Represents options passed to {@link Entry#getData}. - */ - constructor(blob, entry, mimeString, options) { - super(); - this.blob = blob; - this.entry = entry; - this.mimeString = mimeString; - this.options = options; - } - async init() { - const entryMetadata = await getEntryMetadata(this.blob, this.entry); - if (entryMetadata.compressionMethod !== 0) { - const entryBlob = await zipGetData(this.entry, new BlobWriter(this.mimeString), this.options); - this.reader = new BlobReader(entryBlob); - } - else { - this.reader = new BlobEntryReaderImpl(this.blob, entryMetadata); - } - this.size = this.reader.size; - } - async readUint8Array(index, length) { - return this.reader.readUint8Array(index, length); - } -} - -// Images needed for fastbootd -const BOOT_CRITICAL_IMAGES = [ - "boot", - "dt", - "dtbo", - "init_boot", - "pvmfw", - "recovery", - "vbmeta_system", - "vbmeta_vendor", - "vbmeta", - "vendor_boot", - "vendor_kernel_boot", -]; -// Less critical images to flash after boot-critical ones -const SYSTEM_IMAGES = [ - "odm", - "odm_dlkm", - "product", - "system_dlkm", - "system_ext", - "system", - "vendor_dlkm", - "vendor", -]; -/** - * User-friendly action strings for factory image flashing progress. - * This can be indexed by the action argument in FactoryFlashCallback. - */ -const USER_ACTION_MAP = { - load: "Loading", - unpack: "Unpacking", - flash: "Writing", - wipe: "Wiping", - reboot: "Restarting", -}; -const BOOTLOADER_REBOOT_TIME = 4000; // ms -const FASTBOOTD_REBOOT_TIME = 16000; // ms -const USERDATA_ERASE_TIME = 1000; // ms -async function flashEntryBlob(device, entry, onProgress, partition) { - const blob = await zipGetData(entry, new BlobWriter("application/octet-stream"), { - onstart(total) { - logDebug(`Unpacking ${partition} (${total} bytes)`); - onProgress("unpack", partition, 0.0); - - }, - onprogress(progress, total) { - onProgress("unpack", partition, progress / total); - - } - }); - logDebug(`Flashing ${partition}`); - onProgress("flash", partition, 0.0); - await device.flashBlob(partition, blob, (progress) => { - onProgress("flash", partition, progress); - }); -} -async function tryFlashImages(device, entries, onProgress, imageNames) { - for (let imageName of imageNames) { - let pattern = new RegExp(`${imageName}(?:-.+)?\\.img$`); - let entry = entries.find((entry) => entry.filename.match(pattern)); - if (entry !== undefined) { - if (imageName == "bootloader") { - let current_slot = await device.getVariable("current-slot"); - if (current_slot == "a") { - await flashEntryBlob(device, entry, onProgress, (imageName + "_b")); - await device.runCommand("set_active:b"); - } - else if (current_slot == "b") { - await flashEntryBlob(device, entry, onProgress, (imageName + "_a")); - await device.runCommand("set_active:a"); - } - else { - throw new FastbootError("FAIL", `Invalid slot given by bootloader.`); - } - } - else { - await flashEntryBlob(device, entry, onProgress, imageName); - } - } - } -} -async function checkRequirements(device, androidInfo) { - // Deal with CRLF just in case - for (let line of androidInfo.replace("\r", "").split("\n")) { - let match = line.match(/^require\s+(.+?)=(.+)$/); - if (!match) { - continue; - } - let variable = match[1]; - // Historical mismatch that we still need to deal with - if (variable === "board") { - variable = "product"; - } - let expectValue = match[2]; - let expectValues = expectValue.split("|"); - // Special case: not a real variable at all - if (variable === "partition-exists") { - // Check whether the partition exists on the device: - // has-slot = undefined || FAIL => doesn't exist - // has-slot = yes || no => exists - let hasSlot = await device.getVariable(`has-slot:${expectValue}`); - if (hasSlot !== "yes" && hasSlot !== "no") { - throw new FastbootError("FAIL", `Requirement ${variable}=${expectValue} failed, device lacks partition`); - } - // Check whether we recognize the partition - if (!BOOT_CRITICAL_IMAGES.includes(expectValue) && - !SYSTEM_IMAGES.includes(expectValue)) { - throw new FastbootError("FAIL", `Requirement ${variable}=${expectValue} failed, unrecognized partition`); - } - } - else { - let realValue = await device.getVariable(variable); - if (expectValues.includes(realValue)) { - logDebug(`Requirement ${variable}=${expectValue} passed`); - } - else { - let msg = `Requirement ${variable}=${expectValue} failed, value = ${realValue}`; - logDebug(msg); - throw new FastbootError("FAIL", msg); - } - } - } -} -async function tryReboot(device, target, onReconnect) { - try { - await device.reboot(target, false); - } - catch (e) { - /* Failed = device rebooted by itself */ - } - await device.waitForConnect(onReconnect); -} -async function flashRecovery(device, partition, blob, onReconnect, onProgress = (_progress) => { }) { - await device.flashBlob(partition, blob, (progress) => { - onProgress(progress); - }); -} -async function flashZip(device, blob, wipe, onReconnect, onProgress = (_action, _item, _progress) => { }) { - onProgress("load", "package", 0.0); - let reader = new ZipReader(new BlobReader(blob)); - let entries = await reader.getEntries(); - // Bootloader and radio packs can only be flashed in the bare-metal bootloader - if ((await device.getVariable("is-userspace")) === "yes") { - await device.reboot("bootloader", true, onReconnect); - } - // 1. Bootloader pack (repeated for slot A and B) - await tryFlashImages(device, entries, onProgress, ["bootloader"]); - await runWithTimedProgress(onProgress, "reboot", "device", BOOTLOADER_REBOOT_TIME, tryReboot(device, "bootloader", onReconnect)); - await tryFlashImages(device, entries, onProgress, ["bootloader"]); - await runWithTimedProgress(onProgress, "reboot", "device", BOOTLOADER_REBOOT_TIME, tryReboot(device, "bootloader", onReconnect)); - // 2. Radio pack - await tryFlashImages(device, entries, onProgress, ["radio"]); - await runWithTimedProgress(onProgress, "reboot", "device", BOOTLOADER_REBOOT_TIME, tryReboot(device, "bootloader", onReconnect)); - // Cancel snapshot update if in progress - let snapshotStatus = await device.getVariable("snapshot-update-status"); - if (snapshotStatus !== null && snapshotStatus !== "none") { - await device.runCommand("snapshot-update:cancel"); - } - // Load nested images for the following steps - let entry = entries.find((e) => e.filename.match(/image-.+\.zip$/)); - const imageReader = new ZipReader(new BlobEntryReader(blob, entry, "application/zip", { - onstart(total) { - logDebug(`Loading nested images from zip (${total} bytes)`); - onProgress("unpack", "images", 0.0); - - }, - onprogress(progress, total) { - onProgress("unpack", "images", progress / total); - - } - })); - const imageEntries = await imageReader.getEntries(); - // 3. Check requirements - entry = imageEntries.find((e) => e.filename === "android-info.txt"); - if (entry !== undefined) { - const reqText = await zipGetData(entry, new TextWriter()); - await checkRequirements(device, reqText); - } - // 4. Boot-critical images - await tryFlashImages(device, imageEntries, onProgress, BOOT_CRITICAL_IMAGES); - // 5. Super partition template - // This is also where we reboot to fastbootd. - entry = imageEntries.find((e) => e.filename === "super_empty.img"); - if (entry !== undefined) { - await runWithTimedProgress(onProgress, "reboot", "device", FASTBOOTD_REBOOT_TIME, device.reboot("fastboot", true, onReconnect)); - let superName = await device.getVariable("super-partition-name"); - if (!superName) { - superName = "super"; - } - let superAction = wipe ? "wipe" : "flash"; - onProgress(superAction, "super", 0.0); - const superBlob = await zipGetData(entry, new BlobWriter("application/octet-stream")); - await device.upload(superName, await readBlobAsBuffer(superBlob), (progress) => { - onProgress(superAction, "super", progress); - }); - await device.runCommand(`update-super:${superName}${wipe ? ":wipe" : ""}`); - } - // 6. Remaining system images - await tryFlashImages(device, imageEntries, onProgress, SYSTEM_IMAGES); - // We unconditionally reboot back to the bootloader here if we're in fastbootd, - // even when there's no custom AVB key, because common follow-up actions like - // locking the bootloader and wiping data need to be done in the bootloader. - if ((await device.getVariable("is-userspace")) === "yes") { - await runWithTimedProgress(onProgress, "reboot", "device", BOOTLOADER_REBOOT_TIME, device.reboot("bootloader", true, onReconnect)); - } - // 7. Custom AVB key - entry = entries.find((e) => e.filename.endsWith("avb_pkmd.bin")); - if (entry !== undefined) { - await device.runCommand("erase:avb_custom_key"); - await flashEntryBlob(device, entry, onProgress, "avb_custom_key"); - } - // 8. Wipe userdata - if (wipe) { - await runWithTimedProgress(onProgress, "wipe", "data", USERDATA_ERASE_TIME, device.runCommand("erase:userdata")); - } -} - -const FASTBOOT_USB_CLASS = 0xff; -const FASTBOOT_USB_SUBCLASS = 0x42; -const FASTBOOT_USB_PROTOCOL = 0x03; -const BULK_TRANSFER_SIZE = 16384; -const DEFAULT_DOWNLOAD_SIZE = 512 * 1024 * 1024; // 512 MiB -// To conserve RAM and work around Chromium's ~2 GiB size limit, we limit the -// max download size even if the bootloader can accept more data. -const MAX_DOWNLOAD_SIZE = 1024 * 1024 * 1024; // 1 GiB -const GETVAR_TIMEOUT = 10000; // ms -/** - * Exception class for USB errors not directly thrown by WebUSB. - */ -class UsbError extends Error { - constructor(message) { - super(message); - this.name = "UsbError"; - } -} -/** - * Exception class for errors returned by the bootloader, as well as high-level - * fastboot errors resulting from bootloader responses. - */ -class FastbootError extends Error { - constructor(status, message) { - super(`Bootloader replied with ${status}: ${message}`); - this.status = status; - this.bootloaderMessage = message; - this.name = "FastbootError"; - } -} -/** - * This class is a client for executing fastboot commands and operations on a - * device connected over USB. - */ -class FastbootDevice { - /** - * Create a new fastboot device instance. This doesn't actually connect to - * any USB devices; call {@link connect} to do so. - */ - constructor() { - this.device = null; - this.epIn = null; - this.epOut = null; - this._registeredUsbListeners = false; - this._connectResolve = null; - this._connectReject = null; - this._disconnectResolve = null; - } - /** - * Returns whether a USB device is connected and ready for use. - */ - get isConnected() { - return (this.device !== null && - this.device.opened && - this.device.configurations[0].interfaces[0].claimed); - } - /** - * Validate the current USB device's details and connect to it. - * - * @private - */ - async _validateAndConnectDevice() { - if (this.device === null) { - throw new UsbError("Attempted to connect to null device"); - } - // Validate device - let ife = this.device.configurations[0].interfaces[0].alternates[0]; - if (ife.endpoints.length !== 2) { - throw new UsbError("Interface has wrong number of endpoints"); - } - this.epIn = null; - this.epOut = null; - for (let endpoint of ife.endpoints) { - logVerbose("Checking endpoint:", endpoint); - if (endpoint.type !== "bulk") { - throw new UsbError("Interface endpoint is not bulk"); - } - if (endpoint.direction === "in") { - if (this.epIn === null) { - this.epIn = endpoint.endpointNumber; - } - else { - throw new UsbError("Interface has multiple IN endpoints"); - } - } - else if (endpoint.direction === "out") { - if (this.epOut === null) { - this.epOut = endpoint.endpointNumber; - } - else { - throw new UsbError("Interface has multiple OUT endpoints"); - } - } - } - logVerbose("Endpoints: in =", this.epIn, ", out =", this.epOut); - try { - await this.device.open(); - // Opportunistically reset to fix issues on some platforms - try { - await this.device.reset(); - } - catch (error) { - /* Failed = doesn't support reset */ - } - await this.device.selectConfiguration(1); - await this.device.claimInterface(0); // fastboot - } - catch (error) { - // Propagate exception from waitForConnect() - if (this._connectReject !== null) { - this._connectReject(error); - this._connectResolve = null; - this._connectReject = null; - } - throw error; - } - // Return from waitForConnect() - if (this._connectResolve !== null) { - this._connectResolve(undefined); - this._connectResolve = null; - this._connectReject = null; - } - } - /** - * Wait for the current USB device to disconnect, if it's still connected. - * Returns immediately if no device is connected. - */ - async waitForDisconnect() { - if (this.device === null) { - return; - } - return await new Promise((resolve, _reject) => { - this._disconnectResolve = resolve; - }); - } - /** - * Wait for the USB device to connect. Returns at the next connection, - * regardless of whether the connected USB device matches the previous one. - * - * @param {ReconnectCallback} onReconnect - Callback to request device reconnection on Android. - */ - async waitForConnect(onReconnect = () => { }) { - // On Android, we need to request the user to reconnect the device manually - // because there is no support for automatic reconnection. - if (navigator.userAgent.includes("Android")) { - await this.waitForDisconnect(); - onReconnect(); - } - return await new Promise((resolve, reject) => { - this._connectResolve = resolve; - this._connectReject = reject; - }); - } - - handleUSBDisconnect = async (event) => { - if (event.device === this.device) { - logDebug("USB device disconnected"); - if (this._disconnectResolve !== null) { - this._disconnectResolve(undefined); - this._disconnectResolve = null; - } - await window.navigator.usb.removeEventListener("connect", this.handleUSBConnect); - await window.navigator.usb.removeEventListener("disconnect", this.handleUSBDisconnect); - } - }; - - handleUSBConnect = async (event) => { - logDebug("USB device connected"); - this.device = event.device; - - let hasPromiseReject = this._connectReject !== null; - - try { - await this._validateAndConnectDevice(); - } catch (error) { - if (!hasPromiseReject) { - throw error; - } - } - }; - - /** - * Request the user to select a USB device and connect to it using the - * fastboot protocol. - * - * @throws {UsbError} - */ - async connect() { - let devices = await navigator.usb.getDevices(); - logDebug("Found paired USB devices:", devices); - if (devices.length === 1) { - this.device = devices[0]; - } - else { - // If multiple paired devices are connected, request the user to - // select a specific one to reduce ambiguity. This is also necessary - // if no devices are already paired, i.e. first use. - logDebug("No or multiple paired devices are connected, requesting one"); - this.device = await navigator.usb.requestDevice({ - filters: [ - { - classCode: FASTBOOT_USB_CLASS, - subclassCode: FASTBOOT_USB_SUBCLASS, - protocolCode: FASTBOOT_USB_PROTOCOL, - }, - ], - }); - } - logDebug("Using USB device:", this.device); - if (!this._registeredUsbListeners) { - navigator.usb.addEventListener("disconnect", this.handleUSBDisconnect); - navigator.usb.addEventListener("connect", this.handleUSBConnect); - this._registeredUsbListeners = true; - } - await this._validateAndConnectDevice(); - } - /** - * Read a raw command response from the bootloader. - * - * @private - * @returns {Promise} Object containing response text and data size, if any. - * @throws {FastbootError} - */ - async _readResponse() { - let respData = { - text: "", - }; - let respStatus; - do { - let respPacket = await this.device.transferIn(this.epIn, 64); - let response = new TextDecoder().decode(respPacket.data); - respStatus = response.substring(0, 4); - let respMessage = response.substring(4); - logDebug(`Response: ${respStatus} ${respMessage}`); - if (respStatus === "OKAY") { - // OKAY = end of response for this command - respData.text += respMessage; - } - else if (respStatus === "INFO") { - // INFO = additional info line - respData.text += respMessage + "\n"; - } - else if (respStatus === "DATA") { - // DATA = hex string, but it's returned separately for safety - respData.dataSize = respMessage; - } - else { - // Assume FAIL or garbage data - throw new FastbootError(respStatus, respMessage); - } - // INFO = more packets are coming - } while (respStatus === "INFO"); - return respData; - } - /** - * Send a textual command to the bootloader and read the response. - * This is in raw fastboot format, not AOSP fastboot syntax. - * - * @param {string} command - The command to send. - * @returns {Promise} Object containing response text and data size, if any. - * @throws {FastbootError} - */ - async runCommand(command) { - // Command and response length is always 64 bytes regardless of protocol - if (command.length > 64) { - throw new RangeError(); - } - // Send raw UTF-8 command - let cmdPacket = new TextEncoder().encode(command); - await this.device.transferOut(this.epOut, cmdPacket); - logDebug("Command:", command); - return this._readResponse(); - } - /** - * Read the value of a bootloader variable. Returns undefined if the variable - * does not exist. - * - * @param {string} varName - The name of the variable to get. - * @returns {Promise} Textual content of the variable. - * @throws {FastbootError} - */ - async getVariable(varName) { - let resp; - try { - resp = (await runWithTimeout(this.runCommand(`getvar:${varName}`), GETVAR_TIMEOUT)).text; - } - catch (error) { - // Some bootloaders return FAIL instead of empty responses, despite - // what the spec says. Normalize it here. - console.log(error) - if (error instanceof FastbootError && error.status == "FAIL") { - resp = null; - } - else { - throw error; - } - } - // Some bootloaders send whitespace around some variables. - // According to the spec, non-existent variables should return empty - // responses - return resp ? resp.trim() : null; - } - /** - * Get the maximum download size for a single payload, in bytes. - * - * @private - * @returns {Promise} - * @throws {FastbootError} - */ - async _getDownloadSize() { - try { - let resp = (await this.getVariable("max-download-size")).toLowerCase(); - if (resp) { - // AOSP fastboot requires hex - return Math.min(parseInt(resp, 16), MAX_DOWNLOAD_SIZE); - } - } - catch (error) { - /* Failed = no value, fallthrough */ - } - // FAIL or empty variable means no max, set a reasonable limit to conserve memory - return DEFAULT_DOWNLOAD_SIZE; - } - /** - * Send a raw data payload to the bootloader. - * - * @private - */ - async _sendRawPayload(buffer, onProgress) { - let i = 0; - let remainingBytes = buffer.byteLength; - while (remainingBytes > 0) { - let chunk = buffer.slice(i * BULK_TRANSFER_SIZE, (i + 1) * BULK_TRANSFER_SIZE); - if (i % 1000 === 0) { - logVerbose(` Sending ${chunk.byteLength} bytes to endpoint, ${remainingBytes} remaining, i=${i}`); - } - if (i % 10 === 0) { - onProgress((buffer.byteLength - remainingBytes) / buffer.byteLength); - } - await this.device.transferOut(this.epOut, chunk); - remainingBytes -= chunk.byteLength; - i += 1; - } - onProgress(1.0); - } - /** - * Upload a payload to the bootloader for later use, e.g. flashing. - * Does not handle raw images, flashing, or splitting. - * - * @param {string} partition - Name of the partition the payload is intended for. - * @param {ArrayBuffer} buffer - Buffer containing the data to upload. - * @param {FlashProgressCallback} onProgress - Callback for upload progress updates. - * @throws {FastbootError} - */ - async upload(partition, buffer, onProgress = (_progress) => { }) { - // Bootloader requires an 8-digit hex number - let xferHex = buffer.byteLength.toString(16).padStart(8, "0"); - if (xferHex.length !== 8) { - throw new FastbootError("FAIL", `Transfer size overflow: ${xferHex} is more than 8 digits`); - } - // Check with the device and make sure size matches - let downloadResp = await this.runCommand(`download:${xferHex}`); - if (downloadResp.dataSize === undefined) { - throw new FastbootError("FAIL", `Unexpected response to download command: ${downloadResp.text}`); - } - let downloadSize = parseInt(downloadResp.dataSize, 16); - if (downloadSize !== buffer.byteLength) { - throw new FastbootError("FAIL", `Bootloader wants ${buffer.byteLength} bytes, requested to send ${buffer.byteLength} bytes`); - } - await this._sendRawPayload(buffer, onProgress); - await this._readResponse(); - } - /** - * Reboot to the given target, and optionally wait for the device to - * reconnect. - * - * @param {string} target - Where to reboot to, i.e. fastboot or bootloader. - * @param {boolean} wait - Whether to wait for the device to reconnect. - * @param {ReconnectCallback} onReconnect - Callback to request device reconnection, if wait is enabled. - */ - async reboot(target = "", wait = false, onReconnect = () => { }) { - if (target.length > 0) { - await this.runCommand(`reboot-${target}`); - } - else { - await this.runCommand("reboot"); - } - if (wait) { - await this.waitForConnect(onReconnect); - } - } - /** - * Flash the given Blob to the given partition on the device. Any image - * format supported by the bootloader is allowed, e.g. sparse or raw images. - * Large raw images will be converted to sparse images automatically, and - * large sparse images will be split and flashed in multiple passes - * depending on the bootloader's payload size limit. - * - * @param {string} partition - The name of the partition to flash. - * @param {Blob} blob - The Blob to retrieve data from. - * @param {FlashProgressCallback} onProgress - Callback for flashing progress updates. - * @throws {FastbootError} - */ - async flashBlob(partition, blob, onProgress = (_progress) => { }) { - // Use current slot if partition is A/B - if ((await this.getVariable(`has-slot:${partition}`)) === "yes") { - partition += "_" + (await this.getVariable("current-slot")); - } - let maxDlSize = (await this._getDownloadSize() /4) * 3; - - let fileHeader = await readBlobAsBuffer(blob.slice(0, FILE_HEADER_SIZE)); - let totalBytes = blob.size; - let isSparse = false; - try { - let sparseHeader = parseFileHeader(fileHeader); - if (sparseHeader !== null) { - totalBytes = sparseHeader.blocks * sparseHeader.blockSize; - isSparse = true; - } - } - catch (error) { - console.log(error) - // ImageError = invalid, so keep blob.size - } - // Logical partitions need to be resized before flashing because they're - // sized perfectly to the payload. - if ((await this.getVariable(`is-logical:${partition}`)) === "yes") { - // As per AOSP fastboot, we reset the partition to 0 bytes first - // to optimize extent allocation. - await this.runCommand(`resize-logical-partition:${partition}:0`); - // Set the actual size - await this.runCommand(`resize-logical-partition:${partition}:${totalBytes}`); - } - // Convert image to sparse (for splitting) if it exceeds the size limit - if (blob.size > maxDlSize && !isSparse) { - logDebug(`${partition} image is raw, converting to sparse`); - blob = await fromRaw(blob); - } - logDebug(`Flashing ${blob.size} bytes to ${partition}, ${maxDlSize} bytes per split`); - let splits = 0; - let sentBytes = 0; - for await (let split of splitBlob(blob, maxDlSize)) { - await this.upload(partition, split.data, (progress) => { - onProgress((sentBytes + progress * split.bytes) / totalBytes); - }); - logDebug("Flashing payload..."); - await this.runCommand(`flash:${partition}`); - splits += 1; - sentBytes += split.bytes; - } - logDebug(`Flashed ${partition} with ${splits} split(s)`); - } - /** - * Flash Removery using the given Blob on the device. - * Equivalent to `fastboot flash vendor[_boot] recovery.img`. - * - * @param {Blob} blob - The Blob to retrieve data from. - * @param {string} name - The recovery name with extension. - * @param {FlashProgressCallback} onProgress - Callback for flashing progress updates. - * @throws {FastbootError} - */ - async flashRecoveryBlob(blob, name, onReconnect, onProgress = (_progress) => { }) { - logDebug(`Flashing ${name} ${blob.size} bytes image`); - await readBlobAsBuffer(blob); - await flashRecovery(this, name, blob, onReconnect, onProgress); //device, entry, onProgress, imageName); - logDebug(`Flashed ${name} ${blob.size} bytes image`); - } - /** - * Boot the given Blob on the device. - * Equivalent to `fastboot boot boot.img`. - * - * @param {Blob} blob - The Blob to retrieve data from. - * @param {FlashProgressCallback} onProgress - Callback for flashing progress updates. - * @throws {FastbootError} - */ - async bootBlob(blob, onProgress = (_progress) => { }) { - logDebug(`Booting ${blob.size} bytes image`); - let data = await readBlobAsBuffer(blob); - await this.upload("boot.img", data, onProgress); - logDebug("Booting payload..."); - await this.runCommand("boot"); - logDebug(`Booted ${blob.size} bytes image`); - } - /** - * Flash the given factory images zip onto the device, with automatic handling - * of firmware, system, and logical partitions as AOSP fastboot and - * flash-all.sh would do. - * Equivalent to `fastboot update name.zip`. - * - * @param {Blob} blob - Blob containing the zip file to flash. - * @param {boolean} wipe - Whether to wipe super and userdata. Equivalent to `fastboot -w`. - * @param {ReconnectCallback} onReconnect - Callback to request device reconnection. - * @param {FactoryProgressCallback} onProgress - Progress callback for image flashing. - */ - async flashFactoryZip(blob, wipe, onReconnect, onProgress = (_progress) => { }) { - return await flashZip(this, blob, wipe, onReconnect, onProgress); - } -} - -export { FastbootDevice, FastbootError, TimeoutError, USER_ACTION_MAP, UsbError, configure as configureZip, setDebugLevel }; diff --git a/app/src/lib/fastboot/vendor/pako_inflate.min.js b/app/src/lib/fastboot/vendor/pako_inflate.min.js deleted file mode 100644 index 587fbdd..0000000 --- a/app/src/lib/fastboot/vendor/pako_inflate.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).pako={})}(this,(function(e){"use strict";var t=(e,t,i,n)=>{let a=65535&e|0,r=e>>>16&65535|0,o=0;for(;0!==i;){o=i>2e3?2e3:i,i-=o;do{a=a+t[n++]|0,r=r+a|0}while(--o);a%=65521,r%=65521}return a|r<<16|0};const i=new Uint32Array((()=>{let e,t=[];for(var i=0;i<256;i++){e=i;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[i]=e}return t})());var n=(e,t,n,a)=>{const r=i,o=a+n;e^=-1;for(let i=a;i>>8^r[255&(e^t[i])];return-1^e};const a=16209;var r=function(e,t){let i,n,r,o,s,l,d,f,c,h,u,w,b,m,k,_,g,p,v,x,y,E,R,A;const Z=e.state;i=e.next_in,R=e.input,n=i+(e.avail_in-5),r=e.next_out,A=e.output,o=r-(t-e.avail_out),s=r+(e.avail_out-257),l=Z.dmax,d=Z.wsize,f=Z.whave,c=Z.wnext,h=Z.window,u=Z.hold,w=Z.bits,b=Z.lencode,m=Z.distcode,k=(1<>>24,u>>>=p,w-=p,p=g>>>16&255,0===p)A[r++]=65535&g;else{if(!(16&p)){if(0==(64&p)){g=b[(65535&g)+(u&(1<>>=p,w-=p),w<15&&(u+=R[i++]<>>24,u>>>=p,w-=p,p=g>>>16&255,!(16&p)){if(0==(64&p)){g=m[(65535&g)+(u&(1<l){e.msg="invalid distance too far back",Z.mode=a;break e}if(u>>>=p,w-=p,p=r-o,x>p){if(p=x-p,p>f&&Z.sane){e.msg="invalid distance too far back",Z.mode=a;break e}if(y=0,E=h,0===c){if(y+=d-p,p2;)A[r++]=E[y++],A[r++]=E[y++],A[r++]=E[y++],v-=3;v&&(A[r++]=E[y++],v>1&&(A[r++]=E[y++]))}else{y=r-x;do{A[r++]=A[y++],A[r++]=A[y++],A[r++]=A[y++],v-=3}while(v>2);v&&(A[r++]=A[y++],v>1&&(A[r++]=A[y++]))}break}}break}}while(i>3,i-=v,w-=v<<3,u&=(1<{const u=h.bits;let w,b,m,k,_,g,p=0,v=0,x=0,y=0,E=0,R=0,A=0,Z=0,S=0,T=0,O=null;const U=new Uint16Array(16),D=new Uint16Array(16);let I,B,N,C=null;for(p=0;p<=o;p++)U[p]=0;for(v=0;v=1&&0===U[y];y--);if(E>y&&(E=y),0===y)return a[r++]=20971520,a[r++]=20971520,h.bits=1,0;for(x=1;x0&&(0===e||1!==y))return-1;for(D[1]=0,p=1;p852||2===e&&S>592)return 1;for(;;){I=p-A,c[v]+1=g?(B=C[c[v]-g],N=O[c[v]-g]):(B=96,N=0),w=1<>A)+b]=I<<24|B<<16|N|0}while(0!==b);for(w=1<>=1;if(0!==w?(T&=w-1,T+=w):T=0,v++,0==--U[p]){if(p===y)break;p=t[i+c[v]]}if(p>E&&(T&k)!==m){for(0===A&&(A=E),_+=x,R=p-A,Z=1<852||2===e&&S>592)return 1;m=T&k,a[m]=E<<24|R<<16|_-r|0}}return 0!==T&&(a[_+T]=p-A<<24|64<<16|0),h.bits=E,0},h={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_MEM_ERROR:-4,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8};const{Z_FINISH:u,Z_BLOCK:w,Z_TREES:b,Z_OK:m,Z_STREAM_END:k,Z_NEED_DICT:_,Z_STREAM_ERROR:g,Z_DATA_ERROR:p,Z_MEM_ERROR:v,Z_BUF_ERROR:x,Z_DEFLATED:y}=h,E=16180,R=16190,A=16191,Z=16192,S=16194,T=16199,O=16200,U=16206,D=16209,I=e=>(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24);function B(){this.strm=null,this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new Uint16Array(320),this.work=new Uint16Array(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}const N=e=>{if(!e)return 1;const t=e.state;return!t||t.strm!==e||t.mode16211?1:0},C=e=>{if(N(e))return g;const t=e.state;return e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=E,t.last=0,t.havedict=0,t.flags=-1,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new Int32Array(852),t.distcode=t.distdyn=new Int32Array(592),t.sane=1,t.back=-1,m},z=e=>{if(N(e))return g;const t=e.state;return t.wsize=0,t.whave=0,t.wnext=0,C(e)},F=(e,t)=>{let i;if(N(e))return g;const n=e.state;return t<0?(i=0,t=-t):(i=5+(t>>4),t<48&&(t&=15)),t&&(t<8||t>15)?g:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=i,n.wbits=t,z(e))},L=(e,t)=>{if(!e)return g;const i=new B;e.state=i,i.strm=e,i.window=null,i.mode=E;const n=F(e,t);return n!==m&&(e.state=null),n};let M,H,j=!0;const K=e=>{if(j){M=new Int32Array(512),H=new Int32Array(32);let t=0;for(;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(c(1,e.lens,0,288,M,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;c(2,e.lens,0,32,H,0,e.work,{bits:5}),j=!1}e.lencode=M,e.lenbits=9,e.distcode=H,e.distbits=5},P=(e,t,i,n)=>{let a;const r=e.state;return null===r.window&&(r.wsize=1<=r.wsize?(r.window.set(t.subarray(i-r.wsize,i),0),r.wnext=0,r.whave=r.wsize):(a=r.wsize-r.wnext,a>n&&(a=n),r.window.set(t.subarray(i-n,i-n+a),r.wnext),(n-=a)?(r.window.set(t.subarray(i-n,i),0),r.wnext=n,r.whave=r.wsize):(r.wnext+=a,r.wnext===r.wsize&&(r.wnext=0),r.whaveL(e,15),inflateInit2:L,inflate:(e,i)=>{let a,o,s,l,d,f,h,B,C,z,F,L,M,H,j,Y,G,X,W,q,J,Q,V=0;const $=new Uint8Array(4);let ee,te;const ie=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);if(N(e)||!e.output||!e.input&&0!==e.avail_in)return g;a=e.state,a.mode===A&&(a.mode=Z),d=e.next_out,s=e.output,h=e.avail_out,l=e.next_in,o=e.input,f=e.avail_in,B=a.hold,C=a.bits,z=f,F=h,Q=m;e:for(;;)switch(a.mode){case E:if(0===a.wrap){a.mode=Z;break}for(;C<16;){if(0===f)break e;f--,B+=o[l++]<>>8&255,a.check=n(a.check,$,2,0),B=0,C=0,a.mode=16181;break}if(a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&B)<<8)+(B>>8))%31){e.msg="incorrect header check",a.mode=D;break}if((15&B)!==y){e.msg="unknown compression method",a.mode=D;break}if(B>>>=4,C-=4,J=8+(15&B),0===a.wbits&&(a.wbits=J),J>15||J>a.wbits){e.msg="invalid window size",a.mode=D;break}a.dmax=1<>8&1),512&a.flags&&4&a.wrap&&($[0]=255&B,$[1]=B>>>8&255,a.check=n(a.check,$,2,0)),B=0,C=0,a.mode=16182;case 16182:for(;C<32;){if(0===f)break e;f--,B+=o[l++]<>>8&255,$[2]=B>>>16&255,$[3]=B>>>24&255,a.check=n(a.check,$,4,0)),B=0,C=0,a.mode=16183;case 16183:for(;C<16;){if(0===f)break e;f--,B+=o[l++]<>8),512&a.flags&&4&a.wrap&&($[0]=255&B,$[1]=B>>>8&255,a.check=n(a.check,$,2,0)),B=0,C=0,a.mode=16184;case 16184:if(1024&a.flags){for(;C<16;){if(0===f)break e;f--,B+=o[l++]<>>8&255,a.check=n(a.check,$,2,0)),B=0,C=0}else a.head&&(a.head.extra=null);a.mode=16185;case 16185:if(1024&a.flags&&(L=a.length,L>f&&(L=f),L&&(a.head&&(J=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Uint8Array(a.head.extra_len)),a.head.extra.set(o.subarray(l,l+L),J)),512&a.flags&&4&a.wrap&&(a.check=n(a.check,o,L,l)),f-=L,l+=L,a.length-=L),a.length))break e;a.length=0,a.mode=16186;case 16186:if(2048&a.flags){if(0===f)break e;L=0;do{J=o[l+L++],a.head&&J&&a.length<65536&&(a.head.name+=String.fromCharCode(J))}while(J&&L>9&1,a.head.done=!0),e.adler=a.check=0,a.mode=A;break;case 16189:for(;C<32;){if(0===f)break e;f--,B+=o[l++]<>>=7&C,C-=7&C,a.mode=U;break}for(;C<3;){if(0===f)break e;f--,B+=o[l++]<>>=1,C-=1,3&B){case 0:a.mode=16193;break;case 1:if(K(a),a.mode=T,i===b){B>>>=2,C-=2;break e}break;case 2:a.mode=16196;break;case 3:e.msg="invalid block type",a.mode=D}B>>>=2,C-=2;break;case 16193:for(B>>>=7&C,C-=7&C;C<32;){if(0===f)break e;f--,B+=o[l++]<>>16^65535)){e.msg="invalid stored block lengths",a.mode=D;break}if(a.length=65535&B,B=0,C=0,a.mode=S,i===b)break e;case S:a.mode=16195;case 16195:if(L=a.length,L){if(L>f&&(L=f),L>h&&(L=h),0===L)break e;s.set(o.subarray(l,l+L),d),f-=L,l+=L,h-=L,d+=L,a.length-=L;break}a.mode=A;break;case 16196:for(;C<14;){if(0===f)break e;f--,B+=o[l++]<>>=5,C-=5,a.ndist=1+(31&B),B>>>=5,C-=5,a.ncode=4+(15&B),B>>>=4,C-=4,a.nlen>286||a.ndist>30){e.msg="too many length or distance symbols",a.mode=D;break}a.have=0,a.mode=16197;case 16197:for(;a.have>>=3,C-=3}for(;a.have<19;)a.lens[ie[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,ee={bits:a.lenbits},Q=c(0,a.lens,0,19,a.lencode,0,a.work,ee),a.lenbits=ee.bits,Q){e.msg="invalid code lengths set",a.mode=D;break}a.have=0,a.mode=16198;case 16198:for(;a.have>>24,Y=V>>>16&255,G=65535&V,!(j<=C);){if(0===f)break e;f--,B+=o[l++]<>>=j,C-=j,a.lens[a.have++]=G;else{if(16===G){for(te=j+2;C>>=j,C-=j,0===a.have){e.msg="invalid bit length repeat",a.mode=D;break}J=a.lens[a.have-1],L=3+(3&B),B>>>=2,C-=2}else if(17===G){for(te=j+3;C>>=j,C-=j,J=0,L=3+(7&B),B>>>=3,C-=3}else{for(te=j+7;C>>=j,C-=j,J=0,L=11+(127&B),B>>>=7,C-=7}if(a.have+L>a.nlen+a.ndist){e.msg="invalid bit length repeat",a.mode=D;break}for(;L--;)a.lens[a.have++]=J}}if(a.mode===D)break;if(0===a.lens[256]){e.msg="invalid code -- missing end-of-block",a.mode=D;break}if(a.lenbits=9,ee={bits:a.lenbits},Q=c(1,a.lens,0,a.nlen,a.lencode,0,a.work,ee),a.lenbits=ee.bits,Q){e.msg="invalid literal/lengths set",a.mode=D;break}if(a.distbits=6,a.distcode=a.distdyn,ee={bits:a.distbits},Q=c(2,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,ee),a.distbits=ee.bits,Q){e.msg="invalid distances set",a.mode=D;break}if(a.mode=T,i===b)break e;case T:a.mode=O;case O:if(f>=6&&h>=258){e.next_out=d,e.avail_out=h,e.next_in=l,e.avail_in=f,a.hold=B,a.bits=C,r(e,F),d=e.next_out,s=e.output,h=e.avail_out,l=e.next_in,o=e.input,f=e.avail_in,B=a.hold,C=a.bits,a.mode===A&&(a.back=-1);break}for(a.back=0;V=a.lencode[B&(1<>>24,Y=V>>>16&255,G=65535&V,!(j<=C);){if(0===f)break e;f--,B+=o[l++]<>X)],j=V>>>24,Y=V>>>16&255,G=65535&V,!(X+j<=C);){if(0===f)break e;f--,B+=o[l++]<>>=X,C-=X,a.back+=X}if(B>>>=j,C-=j,a.back+=j,a.length=G,0===Y){a.mode=16205;break}if(32&Y){a.back=-1,a.mode=A;break}if(64&Y){e.msg="invalid literal/length code",a.mode=D;break}a.extra=15&Y,a.mode=16201;case 16201:if(a.extra){for(te=a.extra;C>>=a.extra,C-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=16202;case 16202:for(;V=a.distcode[B&(1<>>24,Y=V>>>16&255,G=65535&V,!(j<=C);){if(0===f)break e;f--,B+=o[l++]<>X)],j=V>>>24,Y=V>>>16&255,G=65535&V,!(X+j<=C);){if(0===f)break e;f--,B+=o[l++]<>>=X,C-=X,a.back+=X}if(B>>>=j,C-=j,a.back+=j,64&Y){e.msg="invalid distance code",a.mode=D;break}a.offset=G,a.extra=15&Y,a.mode=16203;case 16203:if(a.extra){for(te=a.extra;C>>=a.extra,C-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){e.msg="invalid distance too far back",a.mode=D;break}a.mode=16204;case 16204:if(0===h)break e;if(L=F-h,a.offset>L){if(L=a.offset-L,L>a.whave&&a.sane){e.msg="invalid distance too far back",a.mode=D;break}L>a.wnext?(L-=a.wnext,M=a.wsize-L):M=a.wnext-L,L>a.length&&(L=a.length),H=a.window}else H=s,M=d-a.offset,L=a.length;L>h&&(L=h),h-=L,a.length-=L;do{s[d++]=H[M++]}while(--L);0===a.length&&(a.mode=O);break;case 16205:if(0===h)break e;s[d++]=a.length,h--,a.mode=O;break;case U:if(a.wrap){for(;C<32;){if(0===f)break e;f--,B|=o[l++]<{if(N(e))return g;let t=e.state;return t.window&&(t.window=null),e.state=null,m},inflateGetHeader:(e,t)=>{if(N(e))return g;const i=e.state;return 0==(2&i.wrap)?g:(i.head=t,t.done=!1,m)},inflateSetDictionary:(e,i)=>{const n=i.length;let a,r,o;return N(e)?g:(a=e.state,0!==a.wrap&&a.mode!==R?g:a.mode===R&&(r=1,r=t(r,i,n,0),r!==a.check)?p:(o=P(e,i,n,n),o?(a.mode=16210,v):(a.havedict=1,m)))},inflateInfo:"pako inflate (from Nodeca project)"};const G=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var X=function(e){const t=Array.prototype.slice.call(arguments,1);for(;t.length;){const i=t.shift();if(i){if("object"!=typeof i)throw new TypeError(i+"must be non-object");for(const t in i)G(i,t)&&(e[t]=i[t])}}return e},W=e=>{let t=0;for(let i=0,n=e.length;i=252?6:e>=248?5:e>=240?4:e>=224?3:e>=192?2:1;J[254]=J[254]=1;var Q=e=>{if("function"==typeof TextEncoder&&TextEncoder.prototype.encode)return(new TextEncoder).encode(e);let t,i,n,a,r,o=e.length,s=0;for(a=0;a>>6,t[r++]=128|63&i):i<65536?(t[r++]=224|i>>>12,t[r++]=128|i>>>6&63,t[r++]=128|63&i):(t[r++]=240|i>>>18,t[r++]=128|i>>>12&63,t[r++]=128|i>>>6&63,t[r++]=128|63&i);return t},V=(e,t)=>{const i=t||e.length;if("function"==typeof TextDecoder&&TextDecoder.prototype.decode)return(new TextDecoder).decode(e.subarray(0,t));let n,a;const r=new Array(2*i);for(a=0,n=0;n4)r[a++]=65533,n+=o-1;else{for(t&=2===o?31:3===o?15:7;o>1&&n1?r[a++]=65533:t<65536?r[a++]=t:(t-=65536,r[a++]=55296|t>>10&1023,r[a++]=56320|1023&t)}}return((e,t)=>{if(t<65534&&e.subarray&&q)return String.fromCharCode.apply(null,e.length===t?e:e.subarray(0,t));let i="";for(let n=0;n{(t=t||e.length)>e.length&&(t=e.length);let i=t-1;for(;i>=0&&128==(192&e[i]);)i--;return i<0||0===i?t:i+J[e[i]]>t?i:t},ee={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"};var te=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0};var ie=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1};const ne=Object.prototype.toString,{Z_NO_FLUSH:ae,Z_FINISH:re,Z_OK:oe,Z_STREAM_END:se,Z_NEED_DICT:le,Z_STREAM_ERROR:de,Z_DATA_ERROR:fe,Z_MEM_ERROR:ce}=h;function he(e){this.options=X({chunkSize:65536,windowBits:15,to:""},e||{});const t=this.options;t.raw&&t.windowBits>=0&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(t.windowBits>=0&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),t.windowBits>15&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new te,this.strm.avail_out=0;let i=Y.inflateInit2(this.strm,t.windowBits);if(i!==oe)throw new Error(ee[i]);if(this.header=new ie,Y.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=Q(t.dictionary):"[object ArrayBuffer]"===ne.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(i=Y.inflateSetDictionary(this.strm,t.dictionary),i!==oe)))throw new Error(ee[i])}function ue(e,t){const i=new he(t);if(i.push(e),i.err)throw i.msg||ee[i.err];return i.result}he.prototype.push=function(e,t){const i=this.strm,n=this.options.chunkSize,a=this.options.dictionary;let r,o,s;if(this.ended)return!1;for(o=t===~~t?t:!0===t?re:ae,"[object ArrayBuffer]"===ne.call(e)?i.input=new Uint8Array(e):i.input=e,i.next_in=0,i.avail_in=i.input.length;;){for(0===i.avail_out&&(i.output=new Uint8Array(n),i.next_out=0,i.avail_out=n),r=Y.inflate(i,o),r===le&&a&&(r=Y.inflateSetDictionary(i,a),r===oe?r=Y.inflate(i,o):r===fe&&(r=le));i.avail_in>0&&r===se&&i.state.wrap>0&&0!==e[i.next_in];)Y.inflateReset(i),r=Y.inflate(i,o);switch(r){case de:case fe:case le:case ce:return this.onEnd(r),this.ended=!0,!1}if(s=i.avail_out,i.next_out&&(0===i.avail_out||r===se))if("string"===this.options.to){let e=$(i.output,i.next_out),t=i.next_out-e,a=V(i.output,e);i.next_out=t,i.avail_out=n-t,t&&i.output.set(i.output.subarray(e,e+t),0),this.onData(a)}else this.onData(i.output.length===i.next_out?i.output:i.output.subarray(0,i.next_out));if(r!==oe||0!==s){if(r===se)return r=Y.inflateEnd(this.strm),this.onEnd(r),this.ended=!0,!0;if(0===i.avail_in)break}}return!0},he.prototype.onData=function(e){this.chunks.push(e)},he.prototype.onEnd=function(e){e===oe&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=W(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg};var we=he,be=ue,me=function(e,t){return(t=t||{}).raw=!0,ue(e,t)},ke=ue,_e=h,ge={Inflate:we,inflate:be,inflateRaw:me,ungzip:ke,constants:_e};e.Inflate=we,e.constants=_e,e.default=ge,e.inflate=be,e.inflateRaw=me,e.ungzip=ke,Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/app/src/lib/fastboot/vendor/z-worker-pako.js b/app/src/lib/fastboot/vendor/z-worker-pako.js deleted file mode 100644 index 960967d..0000000 --- a/app/src/lib/fastboot/vendor/z-worker-pako.js +++ /dev/null @@ -1 +0,0 @@ -!function(){"use strict";const{Array:e,Object:t,Number:n,Math:s,Error:r,Uint8Array:a,Uint16Array:o,Uint32Array:i,Int32Array:c,Map:l,DataView:u,Promise:h,TextEncoder:f,crypto:p,postMessage:d,TransformStream:g,ReadableStream:w,WritableStream:y,CompressionStream:m,DecompressionStream:_}=self;class b{constructor(e){return class extends g{constructor(t,n){const s=new e(n);super({transform(e,t){t.enqueue(s.append(e))},flush(e){const t=s.flush();t&&e.enqueue(t)}})}}}}const v=[];for(let e=0;256>e;e++){let t=e;for(let e=0;8>e;e++)1&t?t=t>>>1^3988292384:t>>>=1;v[e]=t}class S{constructor(e){this.crc=e||-1}append(e){let t=0|this.crc;for(let n=0,s=0|e.length;s>n;n++)t=t>>>8^v[255&(t^e[n])];this.crc=t}get(){return~this.crc}}class k extends g{constructor(){const e=new S;super({transform(t){e.append(t)},flush(t){const n=new a(4);new u(n.buffer).setUint32(0,e.get()),t.enqueue(n)}})}}const z={concat(e,t){if(0===e.length||0===t.length)return e.concat(t);const n=e[e.length-1],s=z.getPartial(n);return 32===s?e.concat(t):z._shiftRight(t,s,0|n,e.slice(0,e.length-1))},bitLength(e){const t=e.length;if(0===t)return 0;const n=e[t-1];return 32*(t-1)+z.getPartial(n)},clamp(e,t){if(32*e.length0&&t&&(e[n-1]=z.partial(t,e[n-1]&2147483648>>t-1,1)),e},partial:(e,t,n)=>32===e?t:(n?0|t:t<<32-e)+1099511627776*e,getPartial:e=>s.round(e/1099511627776)||32,_shiftRight(e,t,n,s){for(void 0===s&&(s=[]);t>=32;t-=32)s.push(n),n=0;if(0===t)return s.concat(e);for(let r=0;r>>t),n=e[r]<<32-t;const r=e.length?e[e.length-1]:0,a=z.getPartial(r);return s.push(z.partial(t+a&31,t+a>32?n:s.pop(),1)),s}},D={bytes:{fromBits(e){const t=z.bitLength(e)/8,n=new a(t);let s;for(let r=0;t>r;r++)0==(3&r)&&(s=e[r/4]),n[r]=s>>>24,s<<=8;return n},toBits(e){const t=[];let n,s=0;for(n=0;n9007199254740991)throw new r("Cannot hash more than 2^53 - 1 bits");const o=new i(n);let c=0;for(let e=t.blockSize+s-(t.blockSize+s&t.blockSize-1);a>=e;e+=t.blockSize)t._block(o.subarray(16*c,16*(c+1))),c+=1;return n.splice(0,16*c),t}finalize(){const e=this;let t=e._buffer;const n=e._h;t=z.concat(t,[z.partial(1,1)]);for(let e=t.length+2;15&e;e++)t.push(0);for(t.push(s.floor(e._length/4294967296)),t.push(0|e._length);t.length;)e._block(t.splice(0,16));return e.reset(),n}_f(e,t,n,s){return e>19?e>39?e>59?e>79?void 0:t^n^s:t&n|t&s|n&s:t^n^s:t&n|~t&s}_S(e,t){return t<>>32-e}_block(t){const n=this,r=n._h,a=e(80);for(let e=0;16>e;e++)a[e]=t[e];let o=r[0],i=r[1],c=r[2],l=r[3],u=r[4];for(let e=0;79>=e;e++){16>e||(a[e]=n._S(1,a[e-3]^a[e-8]^a[e-14]^a[e-16]));const t=n._S(5,o)+n._f(e,i,c,l)+u+a[e]+n._key[s.floor(e/20)]|0;u=l,l=c,c=n._S(30,i),i=o,o=t}r[0]=r[0]+o|0,r[1]=r[1]+i|0,r[2]=r[2]+c|0,r[3]=r[3]+l|0,r[4]=r[4]+u|0}},I={getRandomValues(e){const t=new i(e.buffer),n=e=>{let t=987654321;const n=4294967295;return()=>(t=36969*(65535&t)+(t>>16)&n,(((t<<16)+(e=18e3*(65535&e)+(e>>16)&n)&n)/4294967296+.5)*(s.random()>.5?1:-1))};for(let r,a=0;anew R.hmacSha1(D.bytes.toBits(e)),pbkdf2(e,t,n,s){if(n=n||1e4,0>s||0>n)throw new r("invalid params to pbkdf2");const a=1+(s>>5)<<2;let o,i,c,l,h;const f=new ArrayBuffer(a),p=new u(f);let d=0;const g=z;for(t=D.bytes.toBits(t),h=1;(a||1)>d;h++){for(o=i=e.encrypt(g.concat(t,[h])),c=1;n>c;c++)for(i=e.encrypt(i),l=0;ld&&cr&&(e=(new n).update(e).finalize());for(let t=0;r>t;t++)s[0][t]=909522486^e[t],s[1][t]=1549556828^e[t];t._baseHash[0].update(s[0]),t._baseHash[1].update(s[1]),t._resultHash=new n(t._baseHash[0])}reset(){const e=this;e._resultHash=new e._hash(e._baseHash[0]),e._updated=!1}update(e){this._updated=!0,this._resultHash.update(e)}digest(){const e=this,t=e._resultHash.finalize(),n=new e._hash(e._baseHash[1]).update(t).finalize();return e.reset(),n}encrypt(e){if(this._updated)throw new r("encrypt on already updated hmac called!");return this.update(e),this.digest(e)}}},A=void 0!==p&&"function"==typeof p.getRandomValues,H="Invalid password",q="Invalid signature",B="zipjs-abort-check-password";function K(e){return A?p.getRandomValues(e):I.getRandomValues(e)}const V=16,x={name:"PBKDF2"},P=t.assign({hash:{name:"HMAC"}},x),T=t.assign({iterations:1e3,hash:{name:"SHA-1"}},x),E=["deriveBits"],M=[8,12,16],N=[16,24,32],U=10,W=[0,0,0,0],L="undefined",O="function",F=typeof p!=L,j=F&&p.subtle,G=F&&typeof j!=L,X=D.bytes,J=class{constructor(e){const t=this;t._tables=[[[],[],[],[],[]],[[],[],[],[],[]]],t._tables[0][0][0]||t._precompute();const n=t._tables[0][4],s=t._tables[1],a=e.length;let o,i,c,l=1;if(4!==a&&6!==a&&8!==a)throw new r("invalid aes key size");for(t._key=[i=e.slice(0),c=[]],o=a;4*a+28>o;o++){let e=i[o-1];(o%a==0||8===a&&o%a==4)&&(e=n[e>>>24]<<24^n[e>>16&255]<<16^n[e>>8&255]<<8^n[255&e],o%a==0&&(e=e<<8^e>>>24^l<<24,l=l<<1^283*(l>>7))),i[o]=i[o-a]^e}for(let e=0;o;e++,o--){const t=i[3&e?o:o-4];c[e]=4>=o||4>e?t:s[0][n[t>>>24]]^s[1][n[t>>16&255]]^s[2][n[t>>8&255]]^s[3][n[255&t]]}}encrypt(e){return this._crypt(e,0)}decrypt(e){return this._crypt(e,1)}_precompute(){const e=this._tables[0],t=this._tables[1],n=e[4],s=t[4],r=[],a=[];let o,i,c,l;for(let e=0;256>e;e++)a[(r[e]=e<<1^283*(e>>7))^e]=e;for(let u=o=0;!n[u];u^=i||1,o=a[o]||1){let a=o^o<<1^o<<2^o<<3^o<<4;a=a>>8^255&a^99,n[u]=a,s[a]=u,l=r[c=r[i=r[u]]];let h=16843009*l^65537*c^257*i^16843008*u,f=257*r[a]^16843008*a;for(let n=0;4>n;n++)e[n][u]=f=f<<24^f>>>8,t[n][a]=h=h<<24^h>>>8}for(let n=0;5>n;n++)e[n]=e[n].slice(0),t[n]=t[n].slice(0)}_crypt(e,t){if(4!==e.length)throw new r("invalid aes block size");const n=this._key[t],s=n.length/4-2,a=[0,0,0,0],o=this._tables[t],i=o[0],c=o[1],l=o[2],u=o[3],h=o[4];let f,p,d,g=e[0]^n[0],w=e[t?3:1]^n[1],y=e[2]^n[2],m=e[t?1:3]^n[3],_=4;for(let e=0;s>e;e++)f=i[g>>>24]^c[w>>16&255]^l[y>>8&255]^u[255&m]^n[_],p=i[w>>>24]^c[y>>16&255]^l[m>>8&255]^u[255&g]^n[_+1],d=i[y>>>24]^c[m>>16&255]^l[g>>8&255]^u[255&w]^n[_+2],m=i[m>>>24]^c[g>>16&255]^l[w>>8&255]^u[255&y]^n[_+3],_+=4,g=f,w=p,y=d;for(let e=0;4>e;e++)a[t?3&-e:e]=h[g>>>24]<<24^h[w>>16&255]<<16^h[y>>8&255]<<8^h[255&m]^n[_++],f=g,g=w,w=y,y=m,m=f;return a}},Q=class{constructor(e,t){this._prf=e,this._initIv=t,this._iv=t}reset(){this._iv=this._initIv}update(e){return this.calculate(this._prf,e,this._iv)}incWord(e){if(255==(e>>24&255)){let t=e>>16&255,n=e>>8&255,s=255&e;255===t?(t=0,255===n?(n=0,255===s?s=0:++s):++n):++t,e=0,e+=t<<16,e+=n<<8,e+=s}else e+=1<<24;return e}incCounter(e){0===(e[0]=this.incWord(e[0]))&&(e[1]=this.incWord(e[1]))}calculate(e,t,n){let s;if(!(s=t.length))return[];const r=z.bitLength(t);for(let r=0;s>r;r+=4){this.incCounter(n);const s=e.encrypt(n);t[r]^=s[0],t[r+1]^=s[1],t[r+2]^=s[2],t[r+3]^=s[3]}return z.clamp(t,r)}},Y=R.hmacSha1;let Z=F&&G&&typeof j.importKey==O,$=F&&G&&typeof j.deriveBits==O;class ee extends g{constructor({password:e,signed:n,encryptionStrength:s,checkPasswordOnly:o}){super({start(){t.assign(this,{ready:new h((e=>this.resolveReady=e)),password:e,signed:n,strength:s-1,pending:new a})},async transform(e,t){const n=this,{password:s,strength:i,resolveReady:c,ready:l}=n;s?(await(async(e,t,n,s)=>{const a=await se(e,t,n,ae(s,0,M[t])),o=ae(s,M[t]);if(a[0]!=o[0]||a[1]!=o[1])throw new r(H)})(n,i,s,ae(e,0,M[i]+2)),e=ae(e,M[i]+2),o?t.error(new r(B)):c()):await l;const u=new a(e.length-U-(e.length-U)%V);t.enqueue(ne(n,e,u,0,U,!0))},async flush(e){const{signed:t,ctr:n,hmac:s,pending:o,ready:i}=this;await i;const c=ae(o,0,o.length-U),l=ae(o,o.length-U);let u=new a;if(c.length){const e=ie(X,c);s.update(e);const t=n.update(e);u=oe(X,t)}if(t){const e=ae(oe(X,s.digest()),0,U);for(let t=0;U>t;t++)if(e[t]!=l[t])throw new r(q)}e.enqueue(u)}})}}class te extends g{constructor({password:e,encryptionStrength:n}){let s;super({start(){t.assign(this,{ready:new h((e=>this.resolveReady=e)),password:e,strength:n-1,pending:new a})},async transform(e,t){const n=this,{password:s,strength:r,resolveReady:o,ready:i}=n;let c=new a;s?(c=await(async(e,t,n)=>{const s=K(new a(M[t]));return re(s,await se(e,t,n,s))})(n,r,s),o()):await i;const l=new a(c.length+e.length-e.length%V);l.set(c,0),t.enqueue(ne(n,e,l,c.length,0))},async flush(e){const{ctr:t,hmac:n,pending:r,ready:o}=this;await o;let i=new a;if(r.length){const e=t.update(ie(X,r));n.update(e),i=oe(X,e)}s.signature=oe(X,n.digest()).slice(0,U),e.enqueue(re(i,s.signature))}}),s=this}}function ne(e,t,n,s,r,o){const{ctr:i,hmac:c,pending:l}=e,u=t.length-r;let h;for(l.length&&(t=re(l,t),n=((e,t)=>{if(t&&t>e.length){const n=e;(e=new a(t)).set(n,0)}return e})(n,u-u%V)),h=0;u-V>=h;h+=V){const e=ie(X,ae(t,h,h+V));o&&c.update(e);const r=i.update(e);o||c.update(r),n.set(oe(X,r),h+s)}return e.pending=ae(t,h),n}async function se(n,s,r,o){n.password=null;const i=(e=>{if(void 0===f){const t=new a((e=unescape(encodeURIComponent(e))).length);for(let n=0;n{if(!Z)return R.importKey(t);try{return await j.importKey("raw",t,n,!1,r)}catch(e){return Z=!1,R.importKey(t)}})(0,i,P,0,E),l=await(async(e,t,n)=>{if(!$)return R.pbkdf2(t,e.salt,T.iterations,n);try{return await j.deriveBits(e,t,n)}catch(s){return $=!1,R.pbkdf2(t,e.salt,T.iterations,n)}})(t.assign({salt:o},T),c,8*(2*N[s]+2)),u=new a(l),h=ie(X,ae(u,0,N[s])),p=ie(X,ae(u,N[s],2*N[s])),d=ae(u,2*N[s]);return t.assign(n,{keys:{key:h,authentication:p,passwordVerification:d},ctr:new Q(new J(h),e.from(W)),hmac:new Y(p)}),d}function re(e,t){let n=e;return e.length+t.length&&(n=new a(e.length+t.length),n.set(e,0),n.set(t,e.length)),n}function ae(e,t,n){return e.subarray(t,n)}function oe(e,t){return e.fromBits(t)}function ie(e,t){return e.toBits(t)}class ce extends g{constructor({password:e,passwordVerification:n,checkPasswordOnly:s}){super({start(){t.assign(this,{password:e,passwordVerification:n}),fe(this,e)},transform(e,t){const n=this;if(n.password){const t=ue(n,e.subarray(0,12));if(n.password=null,t[11]!=n.passwordVerification)throw new r(H);e=e.subarray(12)}s?t.error(new r(B)):t.enqueue(ue(n,e))}})}}class le extends g{constructor({password:e,passwordVerification:n}){super({start(){t.assign(this,{password:e,passwordVerification:n}),fe(this,e)},transform(e,t){const n=this;let s,r;if(n.password){n.password=null;const t=K(new a(12));t[11]=n.passwordVerification,s=new a(e.length+t.length),s.set(he(n,t),0),r=12}else s=new a(e.length),r=0;s.set(he(n,e),r),t.enqueue(s)}})}}function ue(e,t){const n=new a(t.length);for(let s=0;s>>24]),a=~e.crcKey2.get(),e.keys=[n,r,a]}function de(e){const t=2|e.keys[2];return ge(s.imul(t,1^t)>>>8)}function ge(e){return 255&e}function we(e){return 4294967295&e}const ye="deflate-raw";class me extends g{constructor(e,{chunkSize:t,CompressionStream:n,CompressionStreamNative:s}){super({});const{compressed:r,encrypted:a,useCompressionStream:o,zipCrypto:i,signed:c,level:l}=e,h=this;let f,p,d=be(super.readable);a&&!i||!c||([d,f]=d.tee(),f=ke(f,new k)),r&&(d=Se(d,o,{level:l,chunkSize:t},s,n)),a&&(i?d=ke(d,new le(e)):(p=new te(e),d=ke(d,p))),ve(h,d,(async()=>{let e;a&&!i&&(e=p.signature),a&&!i||!c||(e=await f.getReader().read(),e=new u(e.value.buffer).getUint32(0)),h.signature=e}))}}class _e extends g{constructor(e,{chunkSize:t,DecompressionStream:n,DecompressionStreamNative:s}){super({});const{zipCrypto:a,encrypted:o,signed:i,signature:c,compressed:l,useCompressionStream:h}=e;let f,p,d=be(super.readable);o&&(a?d=ke(d,new ce(e)):(p=new ee(e),d=ke(d,p))),l&&(d=Se(d,h,{chunkSize:t},s,n)),o&&!a||!i||([d,f]=d.tee(),f=ke(f,new k)),ve(this,d,(async()=>{if((!o||a)&&i){const e=await f.getReader().read(),t=new u(e.value.buffer);if(c!=t.getUint32(0,!1))throw new r(q)}}))}}function be(e){return ke(e,new g({transform(e,t){e&&e.length&&t.enqueue(e)}}))}function ve(e,n,s){n=ke(n,new g({flush:s})),t.defineProperty(e,"readable",{get:()=>n})}function Se(e,t,n,s,r){try{e=ke(e,new(t&&s?s:r)(ye,n))}catch(s){if(!t)throw s;e=ke(e,new r(ye,n))}return e}function ke(e,t){return e.pipeThrough(t)}const ze="data";class De extends g{constructor(e,n){super({});const s=this,{codecType:r}=e;let a;r.startsWith("deflate")?a=me:r.startsWith("inflate")&&(a=_e);let o=0;const i=new a(e,n),c=super.readable,l=new g({transform(e,t){e&&e.length&&(o+=e.length,t.enqueue(e))},flush(){const{signature:e}=i;t.assign(s,{signature:e,size:o})}});t.defineProperty(s,"readable",{get:()=>c.pipeThrough(i).pipeThrough(l)})}}const Ce=new l,Ie=new l;let Re=0;async function Ae(e){try{const{options:t,scripts:s,config:r}=e;s&&s.length&&importScripts.apply(void 0,s),self.initCodec&&self.initCodec(),r.CompressionStreamNative=self.CompressionStream,r.DecompressionStreamNative=self.DecompressionStream,self.Deflate&&(r.CompressionStream=new b(self.Deflate)),self.Inflate&&(r.DecompressionStream=new b(self.Inflate));const a={highWaterMark:1,size:()=>r.chunkSize},o=e.readable||new w({async pull(e){const t=new h((e=>Ce.set(Re,e)));He({type:"pull",messageId:Re}),Re=(Re+1)%n.MAX_SAFE_INTEGER;const{value:s,done:r}=await t;e.enqueue(s),r&&e.close()}},a),i=e.writable||new y({async write(e){let t;const s=new h((e=>t=e));Ie.set(Re,t),He({type:ze,value:e,messageId:Re}),Re=(Re+1)%n.MAX_SAFE_INTEGER,await s}},a),c=new De(t,r);await o.pipeThrough(c).pipeTo(i,{preventClose:!0,preventAbort:!0});try{await i.close()}catch(e){}const{signature:l,size:u}=c;He({type:"close",result:{signature:l,size:u}})}catch(e){qe(e)}}function He(e){let{value:t}=e;if(t)if(t.length)try{t=new a(t),e.value=t.buffer,d(e,[e.value])}catch(t){d(e)}else d(e);else d(e)}function qe(e){const{message:t,stack:n,code:s,name:r}=e;d({error:{message:t,stack:n,code:s,name:r}})}function Be(e,n,s){return class{constructor(r){const o=this;t.hasOwn(r,"level")&&void 0===r.level&&delete r.level,o.codec=new e(t.assign({},n,r)),s(o.codec,(e=>{if(o.pendingData){const t=o.pendingData;o.pendingData=new a(t.length+e.length);const{pendingData:n}=o;n.set(t,0),n.set(e,t.length)}else o.pendingData=new a(e)}))}append(e){return this.codec.push(e),r(this)}flush(){return this.codec.push(new a,!0),r(this)}};function r(e){if(e.pendingData){const t=e.pendingData;return e.pendingData=null,t}return new a}}addEventListener("message",(({data:e})=>{const{type:t,messageId:n,value:s,done:r}=e;try{if("start"==t&&Ae(e),t==ze){const e=Ce.get(n);Ce.delete(n),e({value:new a(s),done:r})}if("ack"==t){const e=Ie.get(n);Ie.delete(n),e()}}catch(e){qe(e)}})),self.initCodec=()=>{const{Deflate:e,Inflate:t}=((e,t={},n)=>({Deflate:Be(e.Deflate,t.deflate,n),Inflate:Be(e.Inflate,t.inflate,n)}))(pako,{deflate:{raw:!0},inflate:{raw:!0}},((e,t)=>e.onData=t));self.Deflate=e,self.Inflate=t}}(); -- GitLab From c679e076ece71a499a28227a41475d46d2ce7d00 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 15 Jan 2025 10:16:59 +0100 Subject: [PATCH 02/19] Add comment about MSWindows developpement. --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f91857..de44713 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,8 @@ ## Install +If you're developing under MSWindows. You need to work from WSL. + [nodeJS](https://nodejs.org/en/download) ## Launch the app -- GitLab From 45c48468c3023ce5fa29b6b32c277a1ce63f75d4 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 15 Jan 2025 10:55:49 +0100 Subject: [PATCH 03/19] Update fastboot library --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f124c07..b909344 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,6 @@ Using: - docker for packaging Libraries: -- fastboot.js (License: MIT): https://github.com/kdrag0n/fastboot.js/ +- fastboot.js (License: MIT): https://gitlab.e.foundation/e/tools/fastboot.js - ya-webadb (License: MIT): https://github.com/yume-chan/ya-webadb - and more see [package.json](https://gitlab.e.foundation/e/devices/eos-installer/-/blob/main/app/package.json) -- GitLab From 7929d4a5b7a61ccbd128a947681dcc31345b111c Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 15 Jan 2025 17:25:41 +0100 Subject: [PATCH 04/19] Use explicit Fastboot import --- app/src/controller/device/bootloader.class.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/controller/device/bootloader.class.js b/app/src/controller/device/bootloader.class.js index 8cc338a..6cd14bc 100644 --- a/app/src/controller/device/bootloader.class.js +++ b/app/src/controller/device/bootloader.class.js @@ -1,5 +1,10 @@ -import * as fastboot from "@e/fastboot"; -import { TimeoutError } from "@e/fastboot"; +import { + configureZip, + FastbootDevice, + setDebugLevel, + TimeoutError, + USER_ACTION_MAP +} from "@e/fastboot"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; @@ -8,18 +13,18 @@ import { WDebug } from "../../debug.js"; * */ export class Bootloader extends Device { constructor() { - super(new fastboot.FastbootDevice()); + super(new FastbootDevice()); } async init() { //await this.blobStore.init(); - fastboot.configureZip({ + configureZip({ workerScripts: { inflate: ["../dist/vendor/z-worker-pako.js", "pako_inflate.min.js"], }, }); // Enable verbose debug logging - fastboot.setDebugLevel(2); + setDebugLevel(2); } reboot(mode) { @@ -61,7 +66,7 @@ export class Bootloader extends Device { onReconnect, // Progress callback (action, item, progress) => { - let userAction = fastboot.USER_ACTION_MAP[action]; + let userAction = USER_ACTION_MAP[action]; onProgress(userAction, item, progress); }, ); -- GitLab From 2ebeb3c29d108554cafe18a2b1f6f2bdf870f15a Mon Sep 17 00:00:00 2001 From: Nicolas Gelot Date: Thu, 16 Jan 2025 20:54:17 +0100 Subject: [PATCH 05/19] Remove vendor from fastboot lib --- app/package-lock.json | 411 +++++++++++++++++- app/package.json | 5 +- app/src/controller/device/bootloader.class.js | 4 +- app/vite.config.js | 15 + 4 files changed, 426 insertions(+), 9 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 81f39b7..12c45fd 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,7 +9,7 @@ "version": "0.9", "license": "GPLv3", "dependencies": { - "@e/fastboot": "1.1.4", + "@e/fastboot": "1.1.4-dev", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" }, @@ -18,13 +18,14 @@ "eslint": "^9.17.0", "globals": "^15.14.0", "prettier": "3.4.2", - "vite": "^6.0.5" + "vite": "^6.0.5", + "vite-plugin-static-copy": "^2.2.0" } }, "node_modules/@e/fastboot": { - "version": "1.1.4", - "resolved": "https://gitlab.e.foundation/api/v4/projects/1751/packages/npm/@e/fastboot/-/@e/fastboot-1.1.4.tgz", - "integrity": "sha1-AXJIHYbdpLqAfM0jmq4pTF0oamE=", + "version": "1.1.4-dev", + "resolved": "https://gitlab.e.foundation/api/v4/projects/1751/packages/npm/@e/fastboot/-/@e/fastboot-1.1.4-dev.tgz", + "integrity": "sha1-CxY0vL0L/zbnFVbCslBdl+VOrPI=", "dependencies": { "@zip.js/zip.js": "^2.7.6", "pako": "^2.1.0" @@ -253,6 +254,44 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.29.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", @@ -362,6 +401,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -376,6 +429,19 @@ "dev": true, "license": "MIT" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -387,6 +453,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -414,6 +493,44 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -696,6 +813,36 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -710,6 +857,16 @@ "dev": true, "license": "MIT" }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -723,6 +880,19 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -761,6 +931,36 @@ "dev": true, "license": "ISC" }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -787,6 +987,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -834,6 +1041,19 @@ "node": ">=0.8.19" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -857,6 +1077,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -898,6 +1128,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -957,6 +1200,30 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1003,6 +1270,16 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1098,6 +1375,19 @@ "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", @@ -1163,6 +1453,40 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1173,6 +1497,17 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.29.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", @@ -1212,6 +1547,30 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1271,6 +1630,19 @@ "node": ">=8" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1284,6 +1656,16 @@ "node": ">= 0.8.0" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1366,6 +1748,25 @@ } } }, + "node_modules/vite-plugin-static-copy": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.2.0.tgz", + "integrity": "sha512-ytMrKdR9iWEYHbUxs6x53m+MRl4SJsOSoMu1U1+Pfg0DjPeMlsRVx3RR5jvoonineDquIue83Oq69JvNsFSU5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "fs-extra": "^11.1.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/app/package.json b/app/package.json index 45a4a65..7d532bb 100644 --- a/app/package.json +++ b/app/package.json @@ -15,10 +15,11 @@ "eslint": "^9.17.0", "globals": "^15.14.0", "prettier": "3.4.2", - "vite": "^6.0.5" + "vite": "^6.0.5", + "vite-plugin-static-copy": "^2.2.0" }, "dependencies": { - "@e/fastboot": "1.1.4", + "@e/fastboot": "1.1.4-dev", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" } diff --git a/app/src/controller/device/bootloader.class.js b/app/src/controller/device/bootloader.class.js index 6cd14bc..5c34fd3 100644 --- a/app/src/controller/device/bootloader.class.js +++ b/app/src/controller/device/bootloader.class.js @@ -3,7 +3,7 @@ import { FastbootDevice, setDebugLevel, TimeoutError, - USER_ACTION_MAP + USER_ACTION_MAP, } from "@e/fastboot"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; @@ -20,7 +20,7 @@ export class Bootloader extends Device { //await this.blobStore.init(); configureZip({ workerScripts: { - inflate: ["../dist/vendor/z-worker-pako.js", "pako_inflate.min.js"], + inflate: ["/vendor/z-worker-pako.js", "pako_inflate.min.js"], }, }); // Enable verbose debug logging diff --git a/app/vite.config.js b/app/vite.config.js index 16ef1ec..3cd15ca 100644 --- a/app/vite.config.js +++ b/app/vite.config.js @@ -1,5 +1,20 @@ import { defineConfig } from "vite"; +import { viteStaticCopy } from "vite-plugin-static-copy"; export default defineConfig({ base: "", + plugins: [ + viteStaticCopy({ + targets: [ + { + src: "node_modules/@zip.js/zip.js/dist/z-worker-pako.js", + dest: "vendor", + }, + { + src: "node_modules/pako/dist/pako_inflate.min.js", + dest: "vendor", + }, + ], + }), + ], }); -- GitLab From 41bb86832424102402912445d336a52696a5a681 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 23 Jan 2025 12:18:09 +0100 Subject: [PATCH 06/19] The original developments used an older version of the webadbjs lib in static. This commit covers the use of this library as a dependency, and the latest version. Removal of the static library and implementation of the modular version for mobile detection. --- app/index.html | 2 +- app/package-lock.json | 88 + app/package.json | 6 + app/src/controller/device/adb.class.js | 53 +- app/src/controller/device/recovery.class.js | 2 +- app/src/lib/webadb/_UNUSED_-webadb.js | 1051 ------- app/src/lib/webadb/adb.bundle.js | 2794 ------------------- 7 files changed, 132 insertions(+), 3864 deletions(-) delete mode 100644 app/src/lib/webadb/_UNUSED_-webadb.js delete mode 100644 app/src/lib/webadb/adb.bundle.js diff --git a/app/index.html b/app/index.html index 828095f..ca41976 100644 --- a/app/index.html +++ b/app/index.html @@ -10,7 +10,7 @@ /e/OS Installer - + diff --git a/app/package-lock.json b/app/package-lock.json index 12c45fd..b408bb5 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -10,11 +10,17 @@ "license": "GPLv3", "dependencies": { "@e/fastboot": "1.1.4-dev", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-credential-web": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" }, "devDependencies": { "@eslint/js": "^9.17.0", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-credential-web": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", "eslint": "^9.17.0", "globals": "^15.14.0", "prettier": "3.4.2", @@ -334,6 +340,88 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/w3c-web-usb": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz", + "integrity": "sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ==", + "dev": true + }, + "node_modules/@yume-chan/adb": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yume-chan/adb/-/adb-1.1.0.tgz", + "integrity": "sha512-AC2HhTtxvEPrAQfMP9qDC3FI5Uc6U8j4oH+WMOQ+PKqzI4eme1X3V7OXgPNkrLTQ9SUWgLRw+lgzpvyTvNYpng==", + "dev": true, + "dependencies": { + "@yume-chan/async": "^4.0.2", + "@yume-chan/event": "^1.0.0", + "@yume-chan/no-data-view": "^1.0.0", + "@yume-chan/stream-extra": "^1.0.0", + "@yume-chan/struct": "^1.0.0" + } + }, + "node_modules/@yume-chan/adb-credential-web": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yume-chan/adb-credential-web/-/adb-credential-web-1.1.0.tgz", + "integrity": "sha512-jdg0JTZ1Z82gPoxtc29511aPVKPQyXRx5Nf2uRy7UXRmg5oeH6dqO5a45Li1yRo1dwAxZHShxIt90RnP7zDH0g==", + "dev": true, + "dependencies": { + "@yume-chan/adb": "^1.1.0" + } + }, + "node_modules/@yume-chan/adb-daemon-webusb": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yume-chan/adb-daemon-webusb/-/adb-daemon-webusb-1.1.0.tgz", + "integrity": "sha512-Q0jkEX/V/PTMZov3udN0gR4uwxfJz0EmKBmRqdJl619rXGC8OfkqlnbrOI4aOjCebm2HCc6d3jVAmjo5sIB7OQ==", + "dev": true, + "dependencies": { + "@types/w3c-web-usb": "^1.0.10", + "@yume-chan/adb": "^1.1.0", + "@yume-chan/event": "^1.0.0", + "@yume-chan/stream-extra": "^1.0.0", + "@yume-chan/struct": "^1.0.0" + } + }, + "node_modules/@yume-chan/async": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@yume-chan/async/-/async-4.0.2.tgz", + "integrity": "sha512-YP5Hg4DZoq6CXzeTsiOu6rDNUaWw8SMiM4cB2rHam4zRTatgUHCWpSKMawQt0+nUro/+IeNTZLh2QpIFyxuGzg==", + "dev": true + }, + "node_modules/@yume-chan/event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/event/-/event-1.0.0.tgz", + "integrity": "sha512-tr4V34WQ5dz2UDMQl4ekj2zGLqwzmclOJpJL+9s2LJpURHw+Szy5g4gi4j86M+5epMFD8dpT9ym/wXHiUdtpsg==", + "dev": true, + "dependencies": { + "@yume-chan/async": "^4.0.2" + } + }, + "node_modules/@yume-chan/no-data-view": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/no-data-view/-/no-data-view-1.0.0.tgz", + "integrity": "sha512-KrkXhJJQiCFFXb/eeHB++HCfKuwwiI7RVzHR7X/0XiwjQouxBpNpRFjEO25458Q5p/EPGprGWQ7BsHrmV3mkZQ==", + "dev": true + }, + "node_modules/@yume-chan/stream-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/stream-extra/-/stream-extra-1.0.0.tgz", + "integrity": "sha512-xltJYD5txn63e0jm7bHExmULowJTgjbsC205DN0GCxfdfrZIl6adKVheQNh1yOuOKV5Ok5luWNVSBp7Y2OVffA==", + "dev": true, + "dependencies": { + "@yume-chan/async": "^4.0.2", + "@yume-chan/struct": "^1.0.0" + } + }, + "node_modules/@yume-chan/struct": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/struct/-/struct-1.0.0.tgz", + "integrity": "sha512-PQWUjgITlZstIkLD6ouRDwmR35Z9OJZ9daOQ6ZipzQ/mCnHTeoJf2v8x2+fmGyVrrHf9oaCWe8U/XW65onRlGg==", + "dev": true, + "dependencies": { + "@yume-chan/async": "^4.0.2", + "@yume-chan/no-data-view": "^1.0.0" + } + }, "node_modules/@zip.js/zip.js": { "version": "2.7.54", "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.54.tgz", diff --git a/app/package.json b/app/package.json index 7d532bb..7881996 100644 --- a/app/package.json +++ b/app/package.json @@ -12,6 +12,9 @@ }, "devDependencies": { "@eslint/js": "^9.17.0", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", + "@yume-chan/adb-credential-web" : "1.1.0", "eslint": "^9.17.0", "globals": "^15.14.0", "prettier": "3.4.2", @@ -20,6 +23,9 @@ }, "dependencies": { "@e/fastboot": "1.1.4-dev", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", + "@yume-chan/adb-credential-web" : "1.1.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" } diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index 86bd536..957c30c 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -1,6 +1,9 @@ import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -import { AdbWebBackend, Adb2 } from "../../lib/webadb/adb.bundle.js"; + +import { AdbDaemonWebUsbDeviceManager, AdbDaemonWebUsbDevice } from "@yume-chan/adb-daemon-webusb"; +import { Adb, AdbDaemonTransport } from "@yume-chan/adb"; +import AdbWebCredentialStore from "@yume-chan/adb-credential-web"; export class ADB extends Device { constructor(device) { @@ -26,24 +29,40 @@ export class ADB extends Device { async connect() { try { console.log("debug adb connect"); - let adbWebBackend = await AdbWebBackend.requestDevice(); - if (adbWebBackend) { - let adbDevice = new Adb2(adbWebBackend, null); //adb.bundle.js - await adbDevice.connect(); - this.device = adbWebBackend._device; - this.webusb = adbDevice; - WDebug.log("----------------------------------"); - WDebug.log("Model", adbDevice.model); - WDebug.log("product", adbDevice.product); - WDebug.log("Name", adbDevice.name); - WDebug.log(">Device (codename)", adbDevice.device); // codemane - WDebug.log("----------------------------------"); + const Manager = AdbDaemonWebUsbDeviceManager.BROWSER; + const devices = await Manager.getDevices(); + if (!devices.length) { + throw new Error("No device connected"); + } + + const adbDaemonWebUsbDevice = devices[0]; /*AdbDaemonWebUsbDevice*/ + const connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + const credentialStore = new AdbWebCredentialStore(); + const transport = await AdbDaemonTransport.authenticate({ + serial: connection.deserial, + connection, + credentialStore: credentialStore + }); + const adb = new Adb(transport); + + const version = await adb.getProp("ro.build.version.release"); + + this.device = adbDaemonWebUsbDevice; + this.webusb = adb; /*Adb*/ + + WDebug.log("----------------------------------"); + WDebug.log("Model", adb.transport.banner.model); + WDebug.log("product", adb.transport.banner.product); + WDebug.log("Name", adbDaemonWebUsbDevice.name); + WDebug.log(">Device (codename)", adb.transport.banner.device); // codemane + WDebug.log("----------------------------------"); + + + } catch (e) { + this.device = null; + throw new Error(`Cannot connect ADB ${e.message || e}`); } - } catch (e) { - this.device = null; - throw new Error(`Cannot connect ADB ${e.message || e}`); - } } getProductName() { diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index d786ba1..71133b8 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -2,7 +2,7 @@ import { MessageClass } from "../../lib/webadb/message.class.js"; import { MessageHeader } from "../../lib/webadb/message-header.class.js"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -import { AdbWebBackend3, Adb3 } from "../../lib/webadb/adb.bundle.js"; +//import { AdbWebBackend3, Adb3 } from "../../lib/webadb/adb.bundle.js"; export class Recovery extends Device { constructor(device) { diff --git a/app/src/lib/webadb/_UNUSED_-webadb.js b/app/src/lib/webadb/_UNUSED_-webadb.js deleted file mode 100644 index d28f295..0000000 --- a/app/src/lib/webadb/_UNUSED_-webadb.js +++ /dev/null @@ -1,1051 +0,0 @@ -// SPDX-License-Identifier: MIT - -(function(root, factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof exports === 'object') { - module.exports = factory(); - } else { - root.Adb = factory(); - } -}(this, function() { - 'use strict'; - - let Adb = {}; - - Adb.Opt = {}; - Adb.Opt.debug = false; - Adb.Opt.dump = false; - - Adb.Opt.key_size = 2048; - Adb.Opt.reuse_key = -1; - - // Set this to false for new devices (post Dec 2017) if - // autodetection doesn't handle it automatically. - Adb.Opt.use_checksum = true; - - let db = init_db(); - let keys = db.then(load_keys); - - Adb.open = function(transport) { - if (transport == "WebUSB") - return Adb.WebUSB.Transport.open(); - - throw new Error("Unsupported transport: " + transport); - }; - - Adb.WebUSB = {}; - - Adb.WebUSB.Transport = function(device) { - this.device = device; - - if (Adb.Opt.debug) - console.log(this); - }; - - Adb.WebUSB.Transport.open = function() { - let filters = [ - { classCode: 255, subclassCode: 66, protocolCode: 1 }, - { classCode: 255, subclassCode: 66, protocolCode: 3 } - ]; - - return navigator.usb.requestDevice({ filters: filters }) - .then(device => device.open() - .then(() => new Adb.WebUSB.Transport(device))); - }; - - Adb.WebUSB.Transport.prototype.close = function() { - this.device.close(); - }; - - Adb.WebUSB.Transport.prototype.reset = function() { - this.device.reset(); - }; - - Adb.WebUSB.Transport.prototype.send = function(ep, data) { - if (Adb.Opt.dump) - hexdump(new DataView(data), "" + ep + "==> "); - - return this.device.transferOut(ep, data); - }; - - Adb.WebUSB.Transport.prototype.receive = function(ep, len) { - return this.device.transferIn(ep, len) - .then(response => { - if (Adb.Opt.dump) - hexdump(response.data, "<==" + ep + " "); - - return response.data; - }); - }; - - Adb.WebUSB.Transport.prototype.find = function(filter) { - for (let i in this.device.configurations) { - let conf = this.device.configurations[i]; - for (let j in conf.interfaces) { - let intf = conf.interfaces[j]; - for (let k in intf.alternates) { - let alt = intf.alternates[k]; - if (filter.classCode == alt.interfaceClass && - filter.subclassCode == alt.interfaceSubclass && - filter.protocolCode == alt.interfaceProtocol) { - return { conf: conf, intf: intf, alt: alt }; - } - } - } - } - - return null; - } - - Adb.WebUSB.Transport.prototype.isAdb = function() { - let match = this.find({ classCode: 255, subclassCode: 66, protocolCode: 1 }); - return match != null; - }; - - Adb.WebUSB.Transport.prototype.isFastboot = function() { - let match = this.find({ classCode: 255, subclassCode: 66, protocolCode: 3 }); - return match != null; - }; - - Adb.WebUSB.Transport.prototype.getDevice = function(filter) { - let match = this.find(filter); - return this.device.selectConfiguration(match.conf.configurationValue) - .then(() => this.device.claimInterface(match.intf.interfaceNumber)) - .then(() => this.device.selectAlternateInterface(match.intf.interfaceNumber, match.alt.alternateSetting)) - .then(() => match); - }; - - Adb.WebUSB.Transport.prototype.connectAdb = function(banner, auth_user_notify = null) { - let VERSION = 0x01000000; - let VERSION_NO_CHECKSUM = 0x01000001; - let MAX_PAYLOAD = 256 * 1024; - - let key_idx = 0; - let AUTH_TOKEN = 1; - - let version_used = Adb.Opt.use_checksum ? VERSION : VERSION_NO_CHECKSUM; - let m = new Adb.Message("CNXN", version_used, MAX_PAYLOAD, "" + banner + "\0"); - return this.getDevice({ classCode: 255, subclassCode: 66, protocolCode: 1 }) - .then(match => new Adb.WebUSB.Device(this, match)) - .then(adb => m.send_receive(adb) - .then((function do_auth_response(response) { - if (response.cmd != "AUTH" || response.arg0 != AUTH_TOKEN) - return response; - - return keys.then(keys => - do_auth(adb, keys, key_idx++, response.data.buffer, do_auth_response, auth_user_notify)); - })) - .then(response => { - if (response.cmd != "CNXN") - throw new Error("Failed to connect with '" + banner + "'"); - if (response.arg0 != VERSION && response.arg0 != VERSION_NO_CHECKSUM) - throw new Error("Version mismatch: " + response.arg0 + " (expected: " + VERSION + " or " + VERSION_NO_CHECKSUM + ")"); - if (Adb.Opt.debug) - console.log("Connected with '" + banner + "', max_payload: " + response.arg1); - adb.max_payload = response.arg1; - if (response.arg0 == VERSION_NO_CHECKSUM) - Adb.Opt.use_checksum = false; - adb.banner = new TextDecoder("utf-8").decode(response.data); - let pieces = adb.banner.split(':'); - adb.mode = pieces[0]; - return adb; - }) - ); - }; - - Adb.WebUSB.Transport.prototype.connectFastboot = function() { - return this.getDevice({ classCode: 255, subclassCode: 66, protocolCode: 3 }) - .then(match => new Fastboot.WebUSB.Device(this, match)) - .then(fastboot => fastboot.send("getvar:max-download-size") - .then(() => fastboot.receive() - .then(response => { - let cmd = decode_cmd(response.getUint32(0, true)); - if (cmd == "FAIL") - throw new Error("Unable to open Fastboot"); - - fastboot.get_cmd = r => decode_cmd(r.getUint32(0, true)); - fastboot.get_payload = r => r.buffer.slice(4); - return fastboot; - }) - ) - ); - }; - - Adb.WebUSB.Device = function(transport, match) { - this.transport = transport; - this.max_payload = 4096; - - this.ep_in = get_ep_num(match.alt.endpoints, "in"); - this.ep_out = get_ep_num(match.alt.endpoints, "out"); - - this.transport.reset(); - } - - Adb.WebUSB.Device.prototype.open = function(service) { - return Adb.Stream.open(this, service); - }; - - Adb.WebUSB.Device.prototype.shell = function(command) { - return Adb.Stream.open(this, "shell:" + command); - }; - - Adb.WebUSB.Device.prototype.tcpip = function(port) { - return Adb.Stream.open(this, "tcpip:" + port); - }; - - Adb.WebUSB.Device.prototype.sync = function() { - return Adb.Stream.open(this, "sync:"); - }; - - Adb.WebUSB.Device.prototype.reboot = function(command="") { - return Adb.Stream.open(this, "reboot:" + command); - }; - - Adb.WebUSB.Device.prototype.send = function(data) { - if (typeof data === "string") { - let encoder = new TextEncoder(); - let string_data = data; - data = encoder.encode(string_data).buffer; - } - - if (data != null && data.length > this.max_payload) - throw new Error("data is too big: " + data.length + " bytes (max: " + this.max_payload + " bytes)"); - - return this.transport.send(this.ep_out, data); - }; - - Adb.WebUSB.Device.prototype.receive = function(len) { - return this.transport.receive(this.ep_in, len); - }; - - let Fastboot = {}; - Fastboot.WebUSB = {}; - - Fastboot.WebUSB.Device = function(transport, match) { - this.transport = transport; - this.max_datasize = 64; - - this.ep_in = get_ep_num(match.alt.endpoints, "in"); - this.ep_out = get_ep_num(match.alt.endpoints, "out"); - }; - - Fastboot.WebUSB.Device.prototype.send = function(data) { - if (typeof data === "string") { - let encoder = new TextEncoder(); - let string_data = data; - data = encoder.encode(string_data).buffer; - } - - if (data != null && data.length > this.max_datasize) - throw new Error("data is too big: " + data.length + " bytes (max: " + this.max_datasize + " bytes)"); - - return this.transport.send(this.ep_out, data); - }; - - Fastboot.WebUSB.Device.prototype.receive = function() { - return this.transport.receive(this.ep_in, 64); - }; - - Adb.Message = function(cmd, arg0, arg1, data = null) { - if (cmd.length != 4) - throw new Error("Invalid command: '" + cmd + "'"); - - this.cmd = cmd; - this.arg0 = arg0; - this.arg1 = arg1; - this.length = (data === null) ? 0 : (typeof data === "string") ? data.length : data.byteLength; - this.data = data; - }; - - Adb.Message.checksum = function(data_view) { - let sum = 0; - - for (let i = 0; i < data_view.byteLength; i++) - sum += data_view.getUint8(i); - - return sum & 0xffffffff; - }; - - Adb.Message.send = function(device, message) { - let header = new ArrayBuffer(24); - let cmd = encode_cmd(message.cmd); - let magic = cmd ^ 0xffffffff; - let data = null; - let len = 0; - let checksum = 0; - - if (Adb.Opt.debug) - console.log(message); - - if (message.data != null) { - if (typeof message.data === "string") { - let encoder = new TextEncoder(); - data = encoder.encode(message.data).buffer; - } else if (ArrayBuffer.isView(message.data)) { - data = message.data.buffer; - } else { - data = message.data; - } - - len = data.byteLength; - if (Adb.Opt.use_checksum) - checksum = Adb.Message.checksum(new DataView(data)); - - if (len > device.max_payload) - throw new Error("data is too big: " + len + " bytes (max: " + device.max_payload + " bytes)"); - } - - let view = new DataView(header); - view.setUint32(0, cmd, true); - view.setUint32(4, message.arg0, true); - view.setUint32(8, message.arg1, true); - view.setUint32(12, len, true); - view.setUint32(16, checksum, true); - view.setUint32(20, magic, true); - - let seq = device.send(header); - if (len > 0) - seq.then(() => device.send(data)); - return seq; - }; - - Adb.Message.receive = function(device) { - return device.receive(24) //Adb.Opt.use_checksum ? 24 : 20) - .then(response => { - let cmd = response.getUint32(0, true); - let arg0 = response.getUint32(4, true); - let arg1 = response.getUint32(8, true); - let len = response.getUint32(12, true); - let check = response.getUint32(16, true); - // Android seems to have stopped providing checksums - if (Adb.use_checksum && response.byteLength > 20) { - let magic = response.getUint32(20, true); - - if ((cmd ^ magic) != -1) - throw new Error("magic mismatch"); - } - - cmd = decode_cmd(cmd); - - if (len == 0) { - let message = new Adb.Message(cmd, arg0, arg1); - if (Adb.Opt.debug) - console.log(message); - return message; - } - - return device.receive(len) - .then(data => { - if (Adb.Opt.use_checksum && Adb.Message.checksum(data) != check) - throw new Error("checksum mismatch"); - - let message = new Adb.Message(cmd, arg0, arg1, data); - if (Adb.Opt.debug) - console.log(message); - return message; - }); - }); - }; - - Adb.Message.prototype.send = function(device) { - return Adb.Message.send(device, this); - }; - - Adb.Message.prototype.send_receive = function(device) { - return this.send(device) - .then(() => Adb.Message.receive(device)); - }; - - Adb.SyncFrame = function(cmd, length = 0, data = null) { - if (cmd.length != 4) - throw new Error("Invalid command: '" + cmd + "'"); - - this.cmd = cmd; - this.length = length; - this.data = data; - }; - - Adb.SyncFrame.send = function(stream, frame) { - let data = new ArrayBuffer(8); - let cmd = encode_cmd(frame.cmd); - - if (Adb.Opt.debug) - console.log(frame); - - let view = new DataView(data); - view.setUint32(0, cmd, true); - view.setUint32(4, frame.length, true); - - return stream.send("WRTE", data); - }; - - Adb.SyncFrame.receive = function(stream) { - return stream.receive() - .then(response => { - if (response.cmd == "WRTE") { - let cmd = decode_cmd(response.data.getUint32(0, true)); - - if (cmd == "OKAY" || cmd == "DATA" || cmd == "DONE" || cmd == "FAIL") { - let len = response.data.getUint32(4, true); - let data = new DataView(response.data.buffer.slice(8)); - - if (len == 0 || data.byteLength >= len) { - let frame = new Adb.SyncFrame(cmd, len, data); - if (Adb.Opt.debug) - console.log(frame); - return frame; - } - - return stream.send("OKAY") - .then(() => stream.receive()) - .then(response => { - if (response.data == null) { - let frame = new Adb.SyncFrame(cmd); - if (Adb.Opt.debug) - console.log(frame); - return frame; - } - - let cmd2 = decode_cmd(response.data.getUint32(0, true)); - - if (cmd2 == "OKAY" || cmd2 == "DATA" || cmd2 == "DONE" || cmd2 == "FAIL") { - let len = response.data.getUint32(4, true); - let data = new DataView(response.data.buffer.slice(8)); - - if (len == 0 || data.byteLength >= len) { - let frame = new Adb.SyncFrame(cmd2, len, data); - if (Adb.Opt.debug) - console.log(frame); - return frame; - } - } - - if (response.data.byteLength < len) - throw new Error("expected at least " + len + ", got " + response.data.byteLength); - - let frame = new Adb.SyncFrame(cmd, len, response.data); - if (Adb.Opt.debug) - console.log(frame); - return frame; - }); - } - - if (Adb.Opt.debug) - console.log(response); - if (Adb.Opt.dump) - hexdump(response.data, "WRTE: "); - - throw new Error("invalid WRTE frame"); - } - - if (response.cmd == "OKAY") { - let frame = new Adb.SyncFrame("OKAY"); - if (Adb.Opt.debug) - console.log(frame); - return frame; - } - - if (Adb.Opt.debug) - console.log(response); - - throw new Error("invalid SYNC frame"); - }); - }; - - Adb.SyncFrame.prototype.send = function(stream) { - return Adb.SyncFrame.send(stream, this); - }; - - Adb.SyncFrame.prototype.send_receive = function(stream) { - return Adb.SyncFrame.send(stream, this) - .then(() => Adb.SyncFrame.receive(stream)); - }; - - Adb.Stream = function(device, service, local_id, remote_id) { - this.device = device; - this.service = service; - this.local_id = local_id; - this.remote_id = remote_id; - this.cancel = null; - }; - - let next_id = 1; - - Adb.Stream.open = function(device, service) { - let local_id = next_id++; - let remote_id = 0; - - let m = new Adb.Message("OPEN", local_id, remote_id, "" + service + "\0"); - return m.send_receive(device) - .then(function do_response(response) { - if (response.arg1 != local_id) - return Adb.Message.receive(device).then(do_response); - - if (response.cmd != "OKAY") - throw new Error("Open failed"); - - remote_id = response.arg0; - - if (Adb.Opt.debug) { - console.log("Opened stream '" + service + "'"); - console.log(" local_id: 0x" + toHex32(local_id)); - console.log(" remote_id: 0x" + toHex32(remote_id)); - } - - return new Adb.Stream(device, service, local_id, remote_id); - }); - }; - - Adb.Stream.prototype.close = function() { - if (this.local_id != 0) { - this.local_id = 0; - return this.send("CLSE"); - } - - if (Adb.Opt.debug) { - console.log("Closed stream '" + this.service + "'"); - console.log(" local_id: 0x" + toHex32(this.local_id)); - console.log(" remote_id: 0x" + toHex32(this.remote_id)); - } - - this.service = ""; - this.remote_id = 0; - }; - - Adb.Stream.prototype.send = function(cmd, data=null) { - let m = new Adb.Message(cmd, this.local_id, this.remote_id, data); - return m.send(this.device); - }; - - Adb.Stream.prototype.receive = function() { - return Adb.Message.receive(this.device) - .then(response => { - // remote's prospective of local_id/remote_id is reversed - if (response.arg0 != 0 && response.arg0 != this.remote_id) - throw new Error("Incorrect arg0: 0x" + toHex32(response.arg0) + " (expected 0x" + toHex32(this.remote_id) + ")"); - if (this.local_id != 0 && response.arg1 != this.local_id) - throw new Error("Incorrect arg1: 0x" + toHex32(response.arg1) + " (expected 0x" + toHex32(this.local_id) + ")"); - return response; - }); - }; - - Adb.Stream.prototype.send_receive = function(cmd, data=null) { - return this.send(cmd, data) - .then(() => this.receive()); - }; - - Adb.Stream.prototype.abort = function() { - if (Adb.Opt.debug) - console.log("aborting..."); - - let self = this; - return new Promise(function(resolve, reject) { - self.cancel = function() { - if (Adb.Opt.debug) - console.log("aborted"); - self.cancel = null; - resolve(); - }; - }); - }; - - Adb.Stream.prototype.stat = function(filename) { - let frame = new Adb.SyncFrame("STAT", filename.length); - return frame.send_receive(this) - .then(check_ok("STAT failed on " + filename)) - .then(response => { - let encoder = new TextEncoder(); - return this.send_receive("WRTE", encoder.encode(filename)) - }) - .then(check_ok("STAT failed on " + filename)) - .then(response => { - return this.receive().then(response => - this.send("OKAY").then(() => - response.data)); - }) - .then(response => { - let id = decode_cmd(response.getUint32(0, true)); - let mode = response.getUint32(4, true); - let size = response.getUint32(8, true); - let time = response.getUint32(12, true); - - if (Adb.Opt.debug) { - console.log("STAT: " + filename); - console.log("id: " + id); - console.log("mode: " + mode); - console.log("size: " + size); - console.log("time: " + time); - } - - if (id != "STAT") - throw new Error("STAT failed on " + filename); - - return { mode: mode, size: size, time: time }; - }); - }; - - Adb.Stream.prototype.pull = function(filename) { - let frame = new Adb.SyncFrame("RECV", filename.length); - return frame.send_receive(this) - .then(check_ok("PULL RECV failed on " + filename)) - .then(response => { - let encoder = new TextEncoder(); - return this.send_receive("WRTE", encoder.encode(filename)) - }) - .then(check_ok("PULL WRTE failed on " + filename)) - .then(() => Adb.SyncFrame.receive(this)) - .then(check_cmd("DATA", "PULL DATA failed on " + filename)) - .catch(err => { - return this.send("OKAY") - .then(() => { throw err; }); - }) - .then(response => { - return this.send("OKAY") - .then(() => response); - }) - .then(response => { - let len = response.length; - if (response.data.byteLength == len + 8) { - let cmd = response.data.getUint32(len, true); - let zero = response.data.getUint32(len + 4, true); - if (decode_cmd(cmd) != "DONE" || zero != 0) - throw new Error("PULL DONE failed on " + filename); - - return new DataView(response.data.buffer, 0, len); - } - - if (response.data.byteLength > 64 * 1024) { - let cmd = response.data.getUint32(response.data.byteLength - 8, true); - let zero = response.data.getUint32(response.data.byteLength - 4, true); - if (decode_cmd(cmd) != "DONE" || zero != 0) - throw new Error("PULL DONE failed on " + filename); - - return new DataView(response.data.buffer, 0, response.data.byteLength - 8); - } - - if (response.data.byteLength != len) - throw new Error("PULL DATA failed on " + filename + ": " + response.data.byteLength + "!=" + len); - - return this.receive() - .then(response => { - let cmd = response.data.getUint32(0, true); - let zero = response.data.getUint32(4, true); - if (decode_cmd(cmd) != "DONE" || zero != 0) - throw new Error("PULL DONE failed on " + filename); - }) - .then(() => this.send("OKAY")) - .then(() => response.data); - }); - }; - - Adb.Stream.prototype.push_start = function(filename, mode) { - let mode_str = mode.toString(10); - let encoder = new TextEncoder(); - - let frame = new Adb.SyncFrame("SEND", filename.length + 1 + mode_str.length); - return frame.send_receive(this) - .then(check_ok("PUSH failed on " + filename)) - .then(response => { - return this.send("WRTE", encoder.encode(filename)) - }) - .then(() => Adb.SyncFrame.receive(this)) - .then(check_ok("PUSH failed on " + filename)) - .then(response => { - return this.send("WRTE", encoder.encode("," + mode_str)) - }) - .then(() => Adb.SyncFrame.receive(this)) - .then(check_ok("PUSH failed on " + filename)); - }; - - Adb.Stream.prototype.push_data = function(data) { - if (typeof data === "string") { - let encoder = new TextEncoder(); - let string_data = data; - data = encoder.encode(string_data).buffer; - } else if (ArrayBuffer.isView(data)) { - data = data.buffer; - } - - let frame = new Adb.SyncFrame("DATA", data.byteLength); - return frame.send_receive(this) - .then(check_ok("PUSH failed")) - .then(response => { - return this.send("WRTE", data); - }) - .then(() => Adb.SyncFrame.receive(this)) - .then(check_ok("PUSH failed")); - }; - - Adb.Stream.prototype.push_done = function() { - let frame = new Adb.SyncFrame("DONE", Math.round(Date.now() / 1000)); - return frame.send_receive(this) - .then(check_ok("PUSH failed")) - .then(response => { - return Adb.SyncFrame.receive(this); - }) - .then(check_ok("PUSH failed")) - .then(response => { - return this.send("OKAY"); - }); - }; - - Adb.Stream.prototype.push = function(file, filename, mode, on_progress = null) { - // we need reduced logging during the data transfer otherwise the console may explode - let old_debug = Adb.Opt.debug; - let old_dump = Adb.Opt.dump; - Adb.Opt.debug = false; - Adb.Opt.dump = false; - - // read the whole file - return read_blob(file).then(data => - this.push_start(filename, mode).then(() => { - let seq = Promise.resolve(); - let rem = file.size; - let max = Math.min(0x10000, this.device.max_payload); - while (rem > 0) { - // these two are needed here for the closure - let len = Math.min(rem, max); - let count = file.size - rem; - seq = seq.then(() => { - if (this.cancel) { - Adb.Opt.debug = old_debug; - Adb.Opt.dump = old_dump; - this.cancel(); - throw new Error("cancelled"); - } - if (on_progress != null) - on_progress(count, file.size); - return this.push_data(data.slice(count, count + len)); - }); - rem -= len; - } - return seq.then(() => { - Adb.Opt.debug = old_debug; - Adb.Opt.dump = old_dump; - return this.push_done(); - }); - })); - }; - - Adb.Stream.prototype.quit = function() { - let frame = new Adb.SyncFrame("QUIT"); - return frame.send_receive(this) - .then(check_ok("QUIT failed")) - .then(response => { - return this.receive(); - }) - .then(check_cmd("CLSE", "QUIT failed")) - .then(response => { - return this.close(); - }); - }; - - function check_cmd(cmd, err_msg) - { - return function(response) { - if (response.cmd == "FAIL") { - let decoder = new TextDecoder(); - throw new Error(decoder.decode(response.data)); - } - if (response.cmd != cmd) - throw new Error(err_msg); - return response; - }; - } - - function check_ok(err_msg) - { - return check_cmd("OKAY", err_msg); - } - - function paddit(text, width, padding) - { - let padlen = width - text.length; - let padded = ""; - - for (let i = 0; i < padlen; i++) - padded += padding; - - return padded + text; - } - - function toHex8(num) - { - return paddit(num.toString(16), 2, "0"); - } - - function toHex16(num) - { - return paddit(num.toString(16), 4, "0"); - } - - function toHex32(num) - { - return paddit(num.toString(16), 8, "0"); - } - - function toB64(buffer) - { - return btoa(new Uint8Array(buffer).reduce((s, b) => s + String.fromCharCode(b), "")); - } - - function hexdump(view, prefix="") - { - let decoder = new TextDecoder(); - - for (let i = 0; i < view.byteLength; i += 16) { - let max = (view.byteLength - i) > 16 ? 16 : (view.byteLength - i); - let row = prefix + toHex16(i) + " "; - let j; - - for (j = 0; j < max; j++) - row += " " + toHex8(view.getUint8(i + j)); - for (; j < 16; j++) - row += " "; - - row += " | " + decoder.decode(new DataView(view.buffer, i, max)); - } - } - - function get_ep_num(endpoints, dir, type = "bulk") - { - let e, ep; - for (e in endpoints) - if (ep = endpoints[e], ep.direction == dir && ep.type == type) - return ep.endpointNumber; - if (Adb.Opt.debug) - console.log(endpoints); - throw new Error("Cannot find " + dir + " endpoint"); - } - - function encode_cmd(cmd) - { - let encoder = new TextEncoder(); - let buffer = encoder.encode(cmd).buffer; - let view = new DataView(buffer); - return view.getUint32(0, true); - } - - function decode_cmd(cmd) - { - let decoder = new TextDecoder(); - let buffer = new ArrayBuffer(4); - let view = new DataView(buffer); - view.setUint32(0, cmd, true); - return decoder.decode(buffer); - } - - function generate_key() - { - let extractable = Adb.Opt.dump; - - return crypto.subtle.generateKey({ - name: "RSASSA-PKCS1-v1_5", - modulusLength: Adb.Opt.key_size, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - hash: { name: "SHA-1" } - }, extractable, [ "sign", "verify" ]) - .then(key => { - if (!Adb.Opt.dump) - return key; - - return privkey_dump(key) - .then(() => pubkey_dump(key)) - .then(() => key); - }); - } - - function do_auth(adb, keys, key_idx, token, do_auth_response, auth_user_notify) - { - let AUTH_SIGNATURE = 2; - let AUTH_RSAPUBLICKEY = 3; - - if (key_idx < keys.length) { - let slot = keys.length - key_idx - 1; - let key = keys[slot]; - let seq = Promise.resolve(); - - if (Adb.Opt.debug) - console.log("signing with key " + slot + "..."); - if (Adb.Opt.dump) { - seq = seq.then(() => privkey_dump(key)) - .then(() => pubkey_dump(key)) - .then(() => hexdump(new DataView(token))) - .then(() => console.log("-----BEGIN TOKEN-----\n" + toB64(token) + "\n-----END TOKEN-----")); - } - - return seq.then(() => crypto.subtle.sign({ name: "RSASSA-PKCS1-v1_5" }, key.privateKey, token)) - .then(signed => { - if (Adb.Opt.dump) - console.log("-----BEGIN SIGNATURE-----\n" + toB64(signed) + "\n-----END SIGNATURE-----"); - - let m = new Adb.Message("AUTH", AUTH_SIGNATURE, 0, signed); - return m.send_receive(adb).then(do_auth_response); - }); - } - - let seq = null; - let dirty = false; - - if (Adb.Opt.reuse_key !== false) { - key_idx = Adb.Opt.reuse_key === true ? -1 : Adb.Opt.reuse_key; - - if (key_idx < 0) - key_idx += keys.length; - - if (key_idx >= 0 && key_idx < keys.length) { - if (Adb.Opt.debug) - console.log("reusing key " + key_idx + "..."); - seq = Promise.resolve(keys[key_idx]); - } - } - - if (seq === null) { - if (Adb.Opt.debug) - console.log("generating key " + key_idx + " (" + Adb.Opt.key_size + " bits)..."); - - seq = generate_key(); - dirty = true; - } - - return seq.then(key => { - return crypto.subtle.exportKey("spki", key.publicKey) - .then(pubkey => { - let m = new Adb.Message("AUTH", AUTH_RSAPUBLICKEY, 0, toB64(pubkey) + "\0"); - return m.send(adb); - }) - .then(() => { - if (Adb.Opt.debug) - console.log("waiting for user confirmation..."); - if (auth_user_notify != null) - auth_user_notify(key.publicKey); - return Adb.Message.receive(adb); - }) - .then(response => { - // return response; - if (response.cmd != "CNXN") - return response; - if (!dirty) - return response; - - keys.push(key); - return db.then(db => store_key(db, key)) - .then(() => response); - }); - }); - } - - function privkey_dump(key) - { - if (!key.privateKey.extractable) { - console.log("cannot dump the private key, it's not extractable"); - return; - } - - return crypto.subtle.exportKey("pkcs8", key.privateKey) - .then(privkey => console.log("-----BEGIN PRIVATE KEY-----\n" + toB64(privkey) + "\n-----END PRIVATE KEY-----")); - } - - function pubkey_dump(key) - { - if (!key.publicKey.extractable) { - console.log("cannot dump the public key, it's not extractable"); - return; - } - - return crypto.subtle.exportKey("spki", key.publicKey) - .then(pubkey => console.log("-----BEGIN PUBLIC KEY-----\n" + toB64(pubkey) + "\n-----END PUBLIC KEY-----")); - } - - function read_blob(blob) - { - return new Promise(function(resolve, reject) { - let reader = new FileReader(); - reader.onload = e => resolve(e.target.result); - reader.onerror = e => reject(e.target.error); - reader.readAsArrayBuffer(blob); - }); - } - - function promisify(request, onsuccess = "onsuccess", onerror = "onerror") - { - return new Promise(function (resolve, reject) { - request[onsuccess] = event => resolve(event.target.result); - request[onerror] = event => reject(event.target.errorCode); - }); - } - - function init_db() - { - let req = window.indexedDB.open("WebADB", 1); - - req.onupgradeneeded = function (event) { - let db = event.target.result; - - if (Adb.Opt.debug) - console.log("DB: migrating from version " + event.oldVersion + " to " + event.newVersion + "..."); - - if (db.objectStoreNames.contains('keys')) { - if (Adb.Opt.debug) - console.log("DB: deleting old keys..."); - - db.deleteObjectStore('keys'); - } - - db.createObjectStore("keys", { autoIncrement: true }); - }; - - return promisify(req); - } - - function load_keys(db) - { - let transaction = db.transaction("keys"); - let store = transaction.objectStore("keys"); - let cursor = store.openCursor(); - let keys = []; - - cursor.onsuccess = function (event) { - let result = event.target.result; - if (result != null) { - keys.push(result.value); - result.continue(); - } - }; - - return promisify(transaction, "oncomplete").then(function (result) { - if (Adb.Opt.debug) - console.log("DB: loaded " + keys.length + " keys"); - return keys; - }); - } - - function store_key(db, key) - { - let transaction = db.transaction("keys", "readwrite"); - let store = transaction.objectStore('keys'); - let request = store.put(key); - - return promisify(request).then(function (result) { - if (Adb.Opt.debug) - console.log("DB: stored key " + (result - 1)); - return result; - }); - } - - function clear_keys(db) - { - let transaction = db.transaction("keys", "readwrite"); - let store = transaction.objectStore("keys"); - let request = store.clear(); - - return promisify(request).then(function (result) { - if (Adb.Opt.debug) - console.log("DB: removed all the keys"); - return result; - }); - } - - return Adb; -})); diff --git a/app/src/lib/webadb/adb.bundle.js b/app/src/lib/webadb/adb.bundle.js deleted file mode 100644 index 62d1364..0000000 --- a/app/src/lib/webadb/adb.bundle.js +++ /dev/null @@ -1,2794 +0,0 @@ -var PromiseResolver = /** @class */ (function () { - function PromiseResolver() { - var _this = this; - this._state = 'running'; - this._promise = new Promise(function (resolve, reject) { - _this._resolve = resolve; - _this._reject = reject; - }); - } - Object.defineProperty(PromiseResolver.prototype, "promise", { - get: function () { return this._promise; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(PromiseResolver.prototype, "state", { - get: function () { return this._state; }, - enumerable: false, - configurable: true - }); - PromiseResolver.prototype.resolve = function (value) { - this._resolve(value); - this._state = 'resolved'; - }; - PromiseResolver.prototype.reject = function (reason) { - this._reject(reason); - this._state = 'rejected'; - }; - return PromiseResolver; -}()); - -var AsyncOperationManager = /** @class */ (function () { - function AsyncOperationManager(initialId) { - if (initialId === void 0) { initialId = 0; } - this.operations = new Map(); - this.operationId = initialId; - } - AsyncOperationManager.prototype.add = function () { - var id = this.operationId++; - var resolver = new PromiseResolver(); - this.operations.set(id, resolver); - return [id, resolver.promise]; - }; - AsyncOperationManager.prototype.getResolver = function (id) { - if (!this.operations.has(id)) { - return null; - } - var resolver = this.operations.get(id); - this.operations.delete(id); - return resolver; - }; - AsyncOperationManager.prototype.resolve = function (id, result) { - var resolver = this.getResolver(id); - if (resolver !== null) { - resolver.resolve(result); - return true; - } - return false; - }; - AsyncOperationManager.prototype.reject = function (id, reason) { - var resolver = this.getResolver(id); - if (resolver !== null) { - resolver.reject(reason); - return true; - } - return false; - }; - return AsyncOperationManager; -}()); - -class EventEmitter { - constructor() { - this.listeners = []; - this.event = this.event.bind(this); - } - event(listener, thisArg, ...args) { - const info = { - listener, - thisArg, - args, - }; - this.listeners.push(info); - const remove = () => { - const index = this.listeners.indexOf(info); - if (index !== -1) { - this.listeners.splice(index, 1); - } - }; - remove.dispose = remove; - return remove; - } - fire(e) { - for (const info of this.listeners.slice()) { - info.listener.apply(info.thisArg, [e, ...info.args]); - } - } - dispose() { - this.listeners.length = 0; - } -} - -class AsyncEventEmitter extends EventEmitter { - async fire(e) { - for (const info of this.listeners) { - await info.listener.apply(info.thisArg, [e, ...info.args]); - } - } -} - -class AutoDisposable { - constructor() { - this.disposables = []; - this.dispose = this.dispose.bind(this); - } - addDisposable(disposable) { - this.disposables.push(disposable); - return disposable; - } - dispose() { - for (const disposable of this.disposables) { - disposable.dispose(); - } - this.disposables = []; - } -} - -class DisposableList extends AutoDisposable { - add(disposable) { - return this.addDisposable(disposable); - } -} - -const BigInt0 = BigInt(0); -const BigInt1 = BigInt(1); -const BigInt2 = BigInt(2); -const BigInt2To64 = BigInt2 ** BigInt(64); - -function getBig(buffer, offset = 0, length = buffer.byteLength - offset) { - const view = new DataView(buffer); - let result = BigInt0; - // Now `length` must be a multiplication of 8 - // Support for arbitrary length can be easily added - for (let i = offset; i < offset + length; i += 8) { - result *= BigInt2To64; - const value = view.getBigUint64(i, false); - result += value; - } - return result; -} - -function setBig(buffer, value, offset = 0) { - const uint64Array = []; - while (value > BigInt0) { - uint64Array.push(BigInt.asUintN(64, value)); - value /= BigInt2To64; - } - const view = new DataView(buffer); - for (let i = uint64Array.length - 1; i >= 0; i -= 1) { - view.setBigUint64(offset, uint64Array[i], false); - offset += 8; - } -} - -function setBigLE(buffer, value, offset = 0) { - const view = new DataView(buffer); - while (value > BigInt0) { - view.setBigUint64(offset, value, true); - offset += 8; - value /= BigInt2To64; - } -} - -// These values are correct only if -// modulus length is 2048 and -// public exponent (e) is 65537 -// Anyway, that's how this library generates keys -// To support other parameters, -// a proper ASN.1 parser can be used -// References: -// -// https://tools.ietf.org/html/rfc8017#appendix-A.1.2 -// PKCS #1: RSA Cryptography Specifications Version 2.2 -// A.1.2. RSA Private Key Syntax -// -// https://lapo.it/asn1js/ -// https://github.com/lapo-luchini/asn1js -// ASN.1 JavaScript decoder -// -// https://www.itu.int/rec/T-REC-X.690-201508-I/en -// X.690: Specification of Distinguished Encoding Rules (DER) -const RsaPrivateKeyNOffset = 38; -const RsaPrivateKeyNLength = 2048 / 8; -const RsaPrivateKeyDOffset = 303; -const RsaPrivateKeyDLength = 2048 / 8; - -function parsePrivateKey(key) { - let n = getBig(key, RsaPrivateKeyNOffset, RsaPrivateKeyNLength); - let d = getBig(key, RsaPrivateKeyDOffset, RsaPrivateKeyDLength); - return [n, d]; -} -// Taken from https://stackoverflow.com/a/51562038 -// I can't understand, but it does work -// Only used with numbers less than 2^32 so doesn't need BigInt -function modInverse(a, m) { - // validate inputs - [a, m] = [Number(a), Number(m)]; - if (Number.isNaN(a) || Number.isNaN(m)) { - return NaN; // invalid input - } - a = (a % m + m) % m; - if (!a || m < 2) { - return NaN; // invalid input - } - // find the gcd - const s = []; - let b = m; - while (b) { - [a, b] = [b, a % b]; - s.push({ a, b }); - } - if (a !== 1) { - return NaN; // inverse does not exists - } - // find the inverse - let x = 1; - let y = 0; - for (let i = s.length - 2; i >= 0; --i) { - [x, y] = [y, x - y * Math.floor(s[i].a / s[i].b)]; - } - return (y % m + m) % m; -} -function calculatePublicKeyLength() { - return 4 + 4 + 2048 / 8 + 2048 / 8 + 4; -} -function calculatePublicKey(privateKey, output, outputOffset = 0) { - // Android has its own public key generation algorithm - // See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111 - // The public key is an array of - // - // [ - // modulusLengthInWords, // 32-bit integer, a "word" is 32-bit so it must be 2048 / 8 / 4 - // // Actually the comment in Android source code was wrong - // n0inv, // 32-bit integer, the modular inverse of (lower 32 bits of) n - // modulus, // n - // rr, // Montgomery parameter R^2 - // exponent, // 32-bit integer, must be 65537 - // ] - // - // (All in little endian) - // See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#38 - // extract `n` from private key - const [n] = parsePrivateKey(privateKey); - let outputType; - const outputLength = calculatePublicKeyLength(); - if (!output) { - output = new ArrayBuffer(outputLength); - outputType = 'ArrayBuffer'; - } - else { - if (output.byteLength - outputOffset < outputLength) { - throw new Error('output buffer is too small'); - } - outputType = 'number'; - } - const outputView = new DataView(output); - // modulusLengthInWords - outputView.setUint32(outputOffset, 2048 / 8 / 4, true); - outputOffset += 4; - // Calculate `n0inv` - // Don't know why need to multiple -1 - // Didn't exist in Android codebase - const n0inv = modInverse(Number(BigInt.asUintN(32, n) * BigInt(-1)), 2 ** 32); - outputView.setUint32(outputOffset, n0inv, true); - outputOffset += 4; - // Write n - setBigLE(output, n, outputOffset); - outputOffset += 256; - // Calculate rr = (2^(rsa_size)) ^ 2 mod n - let rr = BigInt(2) ** BigInt(4096) % n; - setBigLE(output, rr, outputOffset); - outputOffset += 256; - // exponent - outputView.setUint32(outputOffset, 65537, true); - outputOffset += 4; - if (outputType === 'ArrayBuffer') { - return output; - } - else { - return outputLength; - } -} -// Modular exponentiation -// See https://en.wikipedia.org/wiki/Modular_exponentiation#Implementation_in_Lua -function powMod(base, exponent, modulus) { - if (modulus === BigInt1) { - return BigInt0; - } - let r = BigInt1; - base = base % modulus; - while (exponent > BigInt0) { - if (BigInt.asUintN(1, exponent) === BigInt1) { - r = r * base % modulus; - } - exponent >>= BigInt1; - base = base ** BigInt2 % modulus; - } - return r; -} -const Sha1DigestLength = 20; -const Asn1Sequence = 0x30; -const Asn1OctetString = 0x04; -const Asn1Null = 0x05; -const Asn1Oid = 0x06; -// PKCS#1 SHA-1 hash digest info -const Sha1DigestInfo = [ - Asn1Sequence, 0x0d + Sha1DigestLength, - Asn1Sequence, 0x09, - // SHA-1 (1 3 14 3 2 26) - Asn1Oid, 0x05, 1 * 40 + 3, 14, 3, 2, 26, - Asn1Null, 0x00, - Asn1OctetString, Sha1DigestLength -]; -// SubtleCrypto.sign() will hash the given data and sign the hash -// But we don't need the hashing step -// (In another word, ADB just requires the client to -// encrypt the given data with its private key) -// However SubtileCrypto.encrypt() doesn't accept 'RSASSA-PKCS1-v1_5' algorithm -// So we need to implement the encryption by ourself -function sign(privateKey, data) { - const [n, d] = parsePrivateKey(privateKey); - // PKCS#1 padding - const padded = new Uint8Array(256); - let index = 0; - padded[index] = 0; - index += 1; - padded[index] = 1; - index += 1; - const fillLength = padded.length - Sha1DigestInfo.length - data.byteLength - 1; - while (index < fillLength) { - padded[index] = 0xff; - index += 1; - } - padded[index] = 0; - index += 1; - padded.set(new Uint8Array(Sha1DigestInfo), index); - index += Sha1DigestInfo.length; - padded.set(new Uint8Array(data), index); - // Encryption - // signature = padded ** d % n - let signature = powMod(getBig(padded.buffer), d, n); - // Put into an ArrayBuffer - const result = new ArrayBuffer(256); - setBig(result, signature); - return result; -} - -const BackingField = Symbol('BackingField'); -function getBackingField(object, field) { - return object[BackingField][field]; -} -function setBackingField(object, field, value) { - object[BackingField][field] = value; -} -function defineSimpleAccessors(object, field) { - Object.defineProperty(object, field, { - configurable: true, - enumerable: true, - get() { return getBackingField(object, field); }, - set(value) { setBackingField(object, field, value); }, - }); -} - -var Array; -(function (Array) { - let SubType; - (function (SubType) { - SubType[SubType["ArrayBuffer"] = 0] = "ArrayBuffer"; - SubType[SubType["String"] = 1] = "String"; - })(SubType = Array.SubType || (Array.SubType = {})); - function initialize(object, field, value) { - switch (field.subType) { - case SubType.ArrayBuffer: - Object.defineProperty(object, field.name, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field.name).buffer; - }, - set(buffer) { - setBackingField(object, field.name, { buffer }); - }, - }); - break; - case SubType.String: - Object.defineProperty(object, field.name, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field.name).string; - }, - set(string) { - setBackingField(object, field.name, { string }); - }, - }); - break; - default: - throw new Error('Unknown type'); - } - setBackingField(object, field.name, value); - } - Array.initialize = initialize; -})(Array || (Array = {})); - -const registry = {}; -function getFieldTypeDefinition(type) { - return registry[type]; -} -function registerFieldTypeDefinition(_field, _initExtra, methods) { - registry[methods.type] = methods; -} - -var FieldType; -(function (FieldType) { - FieldType[FieldType["Number"] = 0] = "Number"; - FieldType[FieldType["FixedLengthArray"] = 1] = "FixedLengthArray"; - FieldType[FieldType["VariableLengthArray"] = 2] = "VariableLengthArray"; -})(FieldType || (FieldType = {})); - -function placeholder() { - return undefined; -} - -registerFieldTypeDefinition(placeholder(), placeholder(), { - type: FieldType.FixedLengthArray, - async deserialize({ context, field }) { - const buffer = await context.read(field.options.length); - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: buffer }; - case Array.SubType.String: - return { - value: context.decodeUtf8(buffer), - extra: buffer - }; - default: - throw new Error('Unknown type'); - } - }, - getSize({ field }) { - return field.options.length; - }, - initialize({ extra, field, object, value }) { - const backingField = {}; - if (typeof value === 'string') { - backingField.string = value; - if (extra) { - backingField.buffer = extra; - } - } - else { - backingField.buffer = value; - } - Array.initialize(object, field, backingField); - }, - serialize({ context, dataView, field, object, offset }) { - var _a; - const backingField = getBackingField(object, field.name); - (_a = backingField.buffer) !== null && _a !== void 0 ? _a : (backingField.buffer = context.encodeUtf8(backingField.string)); - new Uint8Array(dataView.buffer).set(new Uint8Array(backingField.buffer), offset); - } -}); - -var Number$1; -(function (Number) { - let SubType; - (function (SubType) { - SubType[SubType["Uint8"] = 0] = "Uint8"; - SubType[SubType["Uint16"] = 1] = "Uint16"; - SubType[SubType["Int32"] = 2] = "Int32"; - SubType[SubType["Uint32"] = 3] = "Uint32"; - SubType[SubType["Uint64"] = 4] = "Uint64"; - SubType[SubType["Int64"] = 5] = "Int64"; - })(SubType = Number.SubType || (Number.SubType = {})); - Number.SizeMap = { - [SubType.Uint8]: 1, - [SubType.Uint16]: 2, - [SubType.Int32]: 4, - [SubType.Uint32]: 4, - [SubType.Uint64]: 8, - [SubType.Int64]: 8, - }; - Number.DataViewGetterMap = { - [SubType.Uint8]: 'getUint8', - [SubType.Uint16]: 'getUint16', - [SubType.Int32]: 'getInt32', - [SubType.Uint32]: 'getUint32', - [SubType.Uint64]: 'getBigUint64', - [SubType.Int64]: 'getBigInt64', - }; - Number.DataViewSetterMap = { - [SubType.Uint8]: 'setUint8', - [SubType.Uint16]: 'setUint16', - [SubType.Int32]: 'setInt32', - [SubType.Uint32]: 'setUint32', - [SubType.Uint64]: 'setBigUint64', - [SubType.Int64]: 'setBigInt64', - }; -})(Number$1 || (Number$1 = {})); -registerFieldTypeDefinition(placeholder(), undefined, { - type: FieldType.Number, - getSize({ field }) { - return Number$1.SizeMap[field.subType]; - }, - async deserialize({ context, field, options }) { - const buffer = await context.read(Number$1.SizeMap[field.subType]); - const view = new DataView(buffer); - const value = view[Number$1.DataViewGetterMap[field.subType]](0, options.littleEndian); - return { value }; - }, - serialize({ dataView, field, object, offset, options }) { - dataView[Number$1.DataViewSetterMap[field.subType]](offset, object[field.name], options.littleEndian); - }, -}); - -var VariableLengthArray; -(function (VariableLengthArray) { - let EmptyBehavior; - (function (EmptyBehavior) { - EmptyBehavior[EmptyBehavior["Undefined"] = 0] = "Undefined"; - EmptyBehavior[EmptyBehavior["Empty"] = 1] = "Empty"; - })(EmptyBehavior = VariableLengthArray.EmptyBehavior || (VariableLengthArray.EmptyBehavior = {})); - function getLengthBackingField(object, field) { - return getBackingField(object, field.options.lengthField); - } - VariableLengthArray.getLengthBackingField = getLengthBackingField; - function setLengthBackingField(object, field, value) { - setBackingField(object, field.options.lengthField, value); - } - VariableLengthArray.setLengthBackingField = setLengthBackingField; - function initialize(object, field, value, context) { - Array.initialize(object, field, value); - const descriptor = Object.getOwnPropertyDescriptor(object, field.name); - delete object[field.name]; - switch (field.subType) { - case Array.SubType.ArrayBuffer: - Object.defineProperty(object, field.name, { - ...descriptor, - set(buffer) { - var _a; - descriptor.set.call(object, buffer); - setLengthBackingField(object, field, (_a = buffer === null || buffer === void 0 ? void 0 : buffer.byteLength) !== null && _a !== void 0 ? _a : 0); - }, - }); - delete object[field.options.lengthField]; - Object.defineProperty(object, field.options.lengthField, { - configurable: true, - enumerable: true, - get() { - return getLengthBackingField(object, field); - } - }); - break; - case Array.SubType.String: - Object.defineProperty(object, field.name, { - ...descriptor, - set(string) { - descriptor.set.call(object, string); - if (string) { - setLengthBackingField(object, field, undefined); - } - else { - setLengthBackingField(object, field, 0); - } - }, - }); - delete object[field.options.lengthField]; - Object.defineProperty(object, field.options.lengthField, { - configurable: true, - enumerable: true, - get() { - let value = getLengthBackingField(object, field); - if (value === undefined) { - const backingField = getBackingField(object, field.name); - const buffer = context.encodeUtf8(backingField.string); - backingField.buffer = buffer; - value = buffer.byteLength; - setLengthBackingField(object, field, value); - } - return value; - } - }); - break; - default: - throw new Error('Unknown type'); - } - setBackingField(object, field.name, value); - if (value.buffer) { - setLengthBackingField(object, field, value.buffer.byteLength); - } - } - VariableLengthArray.initialize = initialize; -})(VariableLengthArray || (VariableLengthArray = {})); -registerFieldTypeDefinition(placeholder(), placeholder(), { - type: FieldType.VariableLengthArray, - async deserialize({ context, field, object }) { - let length = object[field.options.lengthField]; - if (typeof length === 'string') { - length = Number.parseInt(length, 10); - } - if (length === 0) { - if (field.options.emptyBehavior === VariableLengthArray.EmptyBehavior.Empty) { - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: new ArrayBuffer(0) }; - case Array.SubType.String: - return { value: '', extra: new ArrayBuffer(0) }; - default: - throw new Error('Unknown type'); - } - } - else { - return { value: undefined }; - } - } - const buffer = await context.read(length); - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: buffer }; - case Array.SubType.String: - return { - value: context.decodeUtf8(buffer), - extra: buffer - }; - default: - throw new Error('Unknown type'); - } - }, - getSize() { return 0; }, - getDynamicSize({ field, object }) { - return object[field.options.lengthField]; - }, - initialize({ context, extra, field, object, value }) { - const backingField = {}; - if (typeof value === 'string') { - backingField.string = value; - if (extra) { - backingField.buffer = extra; - } - } - else { - backingField.buffer = value; - } - Array.initialize(object, field, backingField); - VariableLengthArray.initialize(object, field, backingField, context); - }, - serialize({ dataView, field, object, offset }) { - const backingField = getBackingField(object, field.name); - new Uint8Array(dataView.buffer).set(new Uint8Array(backingField.buffer), offset); - }, -}); - -const StructDefaultOptions = { - littleEndian: false, -}; - -class Struct { - constructor(options = StructDefaultOptions) { - this._size = 0; - this.fields = []; - this._extra = {}; - this.array = (name, type, options) => { - if ('length' in options) { - return this.field({ - type: FieldType.FixedLengthArray, - name, - subType: type, - options: options, - }); - } - else { - return this.field({ - type: FieldType.VariableLengthArray, - name, - subType: type, - options: options, - }); - } - }; - this.arrayBuffer = (name, options) => { - return this.array(name, Array.SubType.ArrayBuffer, options); - }; - this.string = (name, options) => { - return this.array(name, Array.SubType.String, options); - }; - this.options = { ...StructDefaultOptions, ...options }; - } - get size() { return this._size; } - clone() { - const result = new Struct(this.options); - result.fields = this.fields.slice(); - result._size = this._size; - result._extra = this._extra; - result._afterParsed = this._afterParsed; - return result; - } - field(field) { - const result = this.clone(); - result.fields.push(field); - const definition = getFieldTypeDefinition(field.type); - const size = definition.getSize({ field, options: this.options }); - result._size += size; - return result; - } - number(name, type, options = {}, _typescriptType) { - return this.field({ - type: FieldType.Number, - name, - subType: type, - options, - }); - } - uint8(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint8, options, _typescriptType); - } - uint16(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint16, options, _typescriptType); - } - int32(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Int32, options, _typescriptType); - } - uint32(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint32, options, _typescriptType); - } - uint64(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint64, options, _typescriptType); - } - int64(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Int64, options, _typescriptType); - } - extra(value) { - const result = this.clone(); - result._extra = { ...result._extra, ...Object.getOwnPropertyDescriptors(value) }; - return result; - } - afterParsed(callback) { - const result = this.clone(); - result._afterParsed = callback; - return result; - } - initializeField(context, field, fieldTypeDefinition, object, value, extra) { - if (fieldTypeDefinition.initialize) { - fieldTypeDefinition.initialize({ - context, - extra, - field, - object, - options: this.options, - value, - }); - } - else { - setBackingField(object, field.name, value); - defineSimpleAccessors(object, field.name); - } - } - create(init, context) { - const object = { - [BackingField]: {}, - }; - Object.defineProperties(object, this._extra); - for (const field of this.fields) { - const fieldTypeDefinition = getFieldTypeDefinition(field.type); - this.initializeField(context, field, fieldTypeDefinition, object, init[field.name]); - } - return object; - } - async deserialize(context) { - const object = { - [BackingField]: {}, - }; - Object.defineProperties(object, this._extra); - for (const field of this.fields) { - const fieldTypeDefinition = getFieldTypeDefinition(field.type); - const { value, extra } = await fieldTypeDefinition.deserialize({ - context, - field, - object, - options: this.options, - }); - this.initializeField(context, field, fieldTypeDefinition, object, value, extra); - } - if (this._afterParsed) { - const result = this._afterParsed.call(object, object); - if (result) { - return result; - } - } - return object; - } - serialize(init, context) { - const object = this.create(init, context); - let size = this._size; - let fieldSize = []; - for (let i = 0; i < this.fields.length; i += 1) { - const field = this.fields[i]; - const type = getFieldTypeDefinition(field.type); - if (type.getDynamicSize) { - fieldSize[i] = type.getDynamicSize({ - context, - field, - object, - options: this.options, - }); - size += fieldSize[i]; - } - else { - fieldSize[i] = type.getSize({ field, options: this.options }); - } - } - const buffer = new ArrayBuffer(size); - const dataView = new DataView(buffer); - let offset = 0; - for (let i = 0; i < this.fields.length; i += 1) { - const field = this.fields[i]; - const type = getFieldTypeDefinition(field.type); - type.serialize({ - context, - dataView, - field, - object, - offset, - options: this.options, - }); - offset += fieldSize[i]; - } - return buffer; - } -} - -class AutoResetEvent { - constructor(initialSet = false) { - this.list = []; - this.blocking = initialSet; - } - wait() { - if (!this.blocking) { - this.blocking = true; - if (this.list.length === 0) { - return Promise.resolve(); - } - } - const resolver = new PromiseResolver(); - this.list.push(resolver); - return resolver.promise; - } - notify() { - if (this.list.length !== 0) { - this.list.pop().resolve(); - } - else { - this.blocking = false; - } - } - dispose() { - for (const item of this.list) { - item.reject(new Error('The AutoResetEvent has been disposed')); - } - this.list.length = 0; - } -} - -// Prepare maps for O(1) searching -const charToIndex = {}; -const indexToChar = []; -const paddingChar = '='.charCodeAt(0); -function addRange(start, end) { - const charCodeStart = start.charCodeAt(0); - const charCodeEnd = end.charCodeAt(0); - for (let charCode = charCodeStart; charCode <= charCodeEnd; charCode += 1) { - charToIndex[String.fromCharCode(charCode)] = indexToChar.length; - indexToChar.push(charCode); - } -} -addRange('A', 'Z'); -addRange('a', 'z'); -addRange('0', '9'); -addRange('+', '+'); -addRange('/', '/'); - -function calculateBase64EncodedLength(inputLength) { - const remainder = inputLength % 3; - const paddingLength = remainder !== 0 ? 3 - remainder : 0; - return [(inputLength + paddingLength) / 3 * 4, paddingLength]; -} -function encodeBase64(input, arg1, arg2, _arg3, _arg4) { - var _a; - if (input instanceof ArrayBuffer) { - input = new Uint8Array(input); - } - // Because `Uint8Array` is type compatible with `ArrayBuffer`, - // TypeScript doesn't correctly narrow `input` to `Uint8Array` when assigning. - // Manually eliminate `ArrayBuffer` from `input` with a type guard. - if (input instanceof ArrayBuffer) { - return input; - } - let inputOffset; - let inputLength; - let output; - let outputOffset; - let outputArgumentIndex; - if (typeof arg1 === 'number') { - // overload 1, 3, 4 - inputOffset = arg1; - if (typeof arg2 === 'number') { - // overload 1, 4 - inputLength = arg2; - outputArgumentIndex = 3; - } - else { - // overload 3 - inputLength = input.byteLength - inputOffset; - outputArgumentIndex = 2; - } - } - else { - // overload 2 - inputOffset = 0; - inputLength = input.byteLength; - outputArgumentIndex = 1; - } - const [outputLength, paddingLength] = calculateBase64EncodedLength(inputLength); - let maybeOutput = arguments[outputArgumentIndex]; - let outputType; - if (maybeOutput) { - outputOffset = (_a = arguments[outputArgumentIndex + 1]) !== null && _a !== void 0 ? _a : 0; - if (maybeOutput.byteLength - outputOffset < outputLength) { - throw new Error('output buffer is too small'); - } - if (maybeOutput instanceof ArrayBuffer) { - output = new Uint8Array(maybeOutput); - } - else { - output = maybeOutput; - } - outputType = 'number'; - } - else { - const buffer = new ArrayBuffer(outputLength); - output = new Uint8Array(buffer); - outputOffset = 0; - outputType = 'ArrayBuffer'; - } - // Because `Uint8Array` is type compatible with `ArrayBuffer`, - // TypeScript doesn't correctly narrow `output` to `Uint8Array` when assigning. - // Manually eliminate `ArrayBuffer` from `output` with a type guard. - if (output instanceof ArrayBuffer) { - return output; - } - if (input.buffer === output.buffer) { - const bufferInputStart = input.byteOffset + inputOffset; - const bufferOutputStart = output.byteOffset + outputOffset; - if (bufferOutputStart < bufferInputStart - 1) { - const bufferOutputEnd = bufferOutputStart + outputLength; - if (bufferOutputEnd >= bufferInputStart) { - throw new Error('input and output buffer can not be overlapping'); - } - } - } - // Run backward to do in-place overwrite - let inputIndex = inputOffset + inputLength - 1; - let outputIndex = outputOffset + outputLength - 1; - if (paddingLength === 2) { - // aaaaaabb - const x = input[inputIndex]; - inputIndex -= 1; - output[outputIndex] = paddingChar; - outputIndex -= 1; - output[outputIndex] = paddingChar; - outputIndex -= 1; - output[outputIndex] = indexToChar[((x & 0b11) << 4)]; - outputIndex -= 1; - output[outputIndex] = indexToChar[x >> 2]; - outputIndex -= 1; - } - else if (paddingLength === 1) { - // bbbbcccc - const y = input[inputIndex]; - inputIndex -= 1; - // aaaaaabb - const x = input[inputIndex]; - inputIndex -= 1; - output[outputIndex] = paddingChar; - outputIndex -= 1; - output[outputIndex] = indexToChar[((y & 0b1111) << 2)]; - outputIndex -= 1; - output[outputIndex] = indexToChar[((x & 0b11) << 4) | (y >> 4)]; - outputIndex -= 1; - output[outputIndex] = indexToChar[x >> 2]; - outputIndex -= 1; - } - while (inputIndex >= inputOffset) { - // ccdddddd - const z = input[inputIndex]; - inputIndex -= 1; - // bbbbcccc - const y = input[inputIndex]; - inputIndex -= 1; - // aaaaaabb - const x = input[inputIndex]; - inputIndex -= 1; - output[outputIndex] = indexToChar[z & 0b111111]; - outputIndex -= 1; - output[outputIndex] = indexToChar[((y & 0b1111) << 2) | (z >> 6)]; - outputIndex -= 1; - output[outputIndex] = indexToChar[((x & 0b11) << 4) | (y >> 4)]; - outputIndex -= 1; - output[outputIndex] = indexToChar[x >> 2]; - outputIndex -= 1; - } - if (outputType === 'ArrayBuffer') { - return output.buffer; - } - else { - return outputLength; - } -} - -function decodeBase64(input) { - let padding; - if (input[input.length - 2] === '=') { - padding = 2; - } - else if (input[input.length - 1] === '=') { - padding = 1; - } - else { - padding = 0; - } - const result = new Uint8Array(input.length / 4 * 3 - padding); - let sIndex = 0; - let dIndex = 0; - while (sIndex < input.length - (padding !== 0 ? 4 : 0)) { - const a = charToIndex[input[sIndex]]; - sIndex += 1; - const b = charToIndex[input[sIndex]]; - sIndex += 1; - const c = charToIndex[input[sIndex]]; - sIndex += 1; - const d = charToIndex[input[sIndex]]; - sIndex += 1; - result[dIndex] = (a << 2) | ((b & 48) >> 4); - dIndex += 1; - result[dIndex] = ((b & 0b1111) << 4) | ((c & 60) >> 2); - dIndex += 1; - result[dIndex] = ((c & 0b11) << 6) | d; - dIndex += 1; - } - if (padding === 1) { - const a = charToIndex[input[sIndex]]; - sIndex += 1; - const b = charToIndex[input[sIndex]]; - sIndex += 1; - const c = charToIndex[input[sIndex]]; - result[dIndex] = (a << 2) | ((b & 48) >> 4); - dIndex += 1; - result[dIndex] = ((b & 0b1111) << 4) | ((c & 60) >> 2); - } - else if (padding === 2) { - const a = charToIndex[input[sIndex]]; - sIndex += 1; - const b = charToIndex[input[sIndex]]; - result[dIndex] = (a << 2) | ((b & 48) >> 4); - } - - return result.buffer; -} - -const EventQueueDefaultOptions = { - maxWaitCount: Infinity, - highWaterMark: 10, - lowWaterMark: 0, -}; -class EventQueue { - constructor(options = EventQueueDefaultOptions) { - this.pullQueue = []; - this.pushQueue = []; - this.ended = false; - this.waterMark = 0; - this.pendingLowWaterEvent = false; - this.lowWaterEvent = new EventEmitter(); - this.options = { ...EventQueueDefaultOptions, ...options }; - } - get onLowWater() { return this.lowWaterEvent.event; } - push(value, size = 1) { - if (this.ended) { - return true; - } - if (this.pullQueue.length) { - this.pullQueue.shift().resolve(value); - return true; - } - this.pushQueue.push([value, size]); - this.waterMark += size; - if (this.waterMark < this.options.highWaterMark) { - return true; - } - this.pendingLowWaterEvent = true; - return false; - } - next() { - if (this.pushQueue.length) { - const [value, size] = this.pushQueue.shift(); - this.waterMark -= size; - if (this.pendingLowWaterEvent && - this.waterMark <= this.options.lowWaterMark) { - this.lowWaterEvent.fire(); - } - return Promise.resolve(value); - } - if (this.ended) { - return Promise.reject(new Error('The EventQueue has already ended')); - } - if (this.pullQueue.length === this.options.maxWaitCount - 1) { - throw new Error('Max wait count exceeded'); - } - const resolver = new PromiseResolver(); - this.pullQueue.push(resolver); - return resolver.promise; - } - end() { - this.ended = true; - let item; - while (item = this.pullQueue.shift()) { - item.reject(new Error('The EventQueue has already ended')); - } - } -} - -class AdbReadableStream { - constructor(stream) { - this.readLock = new AutoResetEvent(); - this.stream = stream; - this.queue = new EventQueue({ - highWaterMark: 16 * 1024, - }); - const resetEvent = new AutoResetEvent(true); - this.stream.onData(buffer => { - if (!this.queue.push(buffer, buffer.byteLength)) { - return resetEvent.wait(); - } - return; - }); - this.stream.onClose(() => { - this.queue.end(); - }); - this.queue.onLowWater(() => { - resetEvent.notify(); - }); - } - get backend() { return this.stream.backend; } - get localId() { return this.stream.localId; } - get remoteId() { return this.stream.remoteId; } - async read() { - await this.readLock.wait(); - try { - return await this.queue.next(); - } - finally { - this.readLock.notify(); - } - } - write(data) { - return this.stream.write(data); - } - close() { - this.stream.close(); - } -} - -class BufferedStream { - constructor(stream) { - this.stream = stream; - } - async read(length) { - let array; - let index; - if (this.buffer) { - const buffer = this.buffer; - if (buffer.byteLength > length) { - this.buffer = buffer.subarray(length); - return buffer.slice(0, length).buffer; - } - array = new Uint8Array(length); - array.set(buffer); - index = buffer.byteLength; - this.buffer = undefined; - } - else { - const buffer = await this.stream.read(length); - if (buffer.byteLength === length) { - return buffer; - } - if (buffer.byteLength > length) { - this.buffer = new Uint8Array(buffer, length); - return buffer.slice(0, length); - } - array = new Uint8Array(length); - array.set(new Uint8Array(buffer), 0); - index = buffer.byteLength; - } - while (index < length) { - const left = length - index; - const buffer = await this.stream.read(left); - if (buffer.byteLength > left) { - array.set(new Uint8Array(buffer, 0, left), index); - this.buffer = new Uint8Array(buffer, left); - return array.buffer; - } - array.set(new Uint8Array(buffer), index); - index += buffer.byteLength; - } - return array.buffer; - } - close() { - var _a, _b; - (_b = (_a = this.stream).close) === null || _b === void 0 ? void 0 : _b.call(_a); - } -} -class AdbBufferedStream extends BufferedStream { - get backend() { return this.stream.backend; } - get localId() { return this.stream.localId; } - get remoteId() { return this.stream.remoteId; } - constructor(stream) { - super(new AdbReadableStream(stream)); - } - write(data) { - return this.stream.write(data); - } - decodeUtf8(buffer) { - return this.backend.decodeUtf8(buffer); - } - encodeUtf8(input) { - return this.backend.encodeUtf8(input); - } -} - -function* chunkArrayLike(value, size) { - if ('length' in value) { - value = new Uint8Array(value).buffer; - } - if (value.byteLength <= size) { - return yield value; - } - for (let i = 0; i < value.byteLength; i += size) { - yield value.slice(i, i + size); - } -} - -class AdbStreamController extends AutoDisposable { - constructor(localId, remoteId, dispatcher) { - super(); - this.writeChunkLock = this.addDisposable(new AutoResetEvent()); - this.writeLock = this.addDisposable(new AutoResetEvent()); - this.dataEvent = this.addDisposable(new AsyncEventEmitter()); - this._closed = false; - this.closeEvent = this.addDisposable(new EventEmitter()); - this.localId = localId; - this.remoteId = remoteId; - this.dispatcher = dispatcher; - } - get backend() { return this.dispatcher.backend; } - get closed() { return this._closed; } - get onClose() { return this.closeEvent.event; } - async writeChunk(data) { - if (this._closed) { - throw new Error('Can not write after closed'); - } - // Wait for an ack packet - await this.writeChunkLock.wait(); - await this.dispatcher.sendPacket(AdbCommand.Write, this.localId, this.remoteId, data); - } - async write(data) { - // Keep write operations in order - await this.writeLock.wait(); - for await (const chunk of chunkArrayLike(data, this.dispatcher.maxPayloadSize)) { - await this.writeChunk(chunk); - } - this.writeLock.notify(); - } - ack() { - this.writeChunkLock.notify(); - } - async close() { - if (!this._closed) { - await this.dispatcher.sendPacket(AdbCommand.Close, this.localId, this.remoteId); - this._closed = true; - } - } - dispose() { - this._closed = true; - this.closeEvent.fire(); - super.dispose(); - } -} - -class AdbStream { - constructor(controller) { - this.controller = controller; - } - get backend() { return this.controller.backend; } - get localId() { return this.controller.localId; } - get remoteId() { return this.controller.remoteId; } - get closed() { return this.controller.closed; } - get onData() { return this.controller.dataEvent.event; } - get onClose() { return this.controller.onClose; } - write(data) { - return this.controller.write(data); - } - close() { - return this.controller.close(); - } -} - -class AdbPacketDispatcher extends AutoDisposable { - constructor(backend, logger) { - super(); - // ADB stream id starts from 1 - // (0 means open failed) - this.initializers = new AsyncOperationManager(1); - this.streams = new Map(); - this.sendLock = new AutoResetEvent(); - this.maxPayloadSize = 0; - this.calculateChecksum = true; - this.appendNullToServiceString = true; - this.packetEvent = this.addDisposable(new EventEmitter()); - this.streamEvent = this.addDisposable(new EventEmitter()); - this.errorEvent = this.addDisposable(new EventEmitter()); - this._running = false; - this.backend = backend; - this.logger = logger; - } - get onPacket() { return this.packetEvent.event; } - get onStream() { return this.streamEvent.event; } - get onError() { return this.errorEvent.event; } - get running() { return this._running; } - async receiveLoop() { - var _a; - try { - while (this._running) { - const packet = await AdbPacket.read(this.backend); - (_a = this.logger) === null || _a === void 0 ? void 0 : _a.onIncomingPacket(packet); - switch (packet.command) { - case AdbCommand.OK: - this.handleOk(packet); - continue; - case AdbCommand.Close: - // CLSE also has two meanings - if (packet.arg0 === 0) { - // 1. The device don't want to create the Stream - this.initializers.reject(packet.arg1, new Error('Stream open failed')); - continue; - } - if (this.streams.has(packet.arg1)) { - // 2. The device has closed the Stream - this.streams.get(packet.arg1).dispose(); - this.streams.delete(packet.arg1); - continue; - } - // Maybe the device is responding to a packet of last connection - // Just ignore it - continue; - case AdbCommand.Write: - if (this.streams.has(packet.arg1)) { - await this.streams.get(packet.arg1).dataEvent.fire(packet.payload); - await this.sendPacket(AdbCommand.OK, packet.arg1, packet.arg0); - } - // Maybe the device is responding to a packet of last connection - // Just ignore it - continue; - case AdbCommand.Open: - await this.handleOpen(packet); - continue; - } - const args = { - handled: false, - packet, - }; - this.packetEvent.fire(args); - if (!args.handled) { - this.dispose(); - throw new Error(`Unhandled packet with command '${packet.command}'`); - } - } - } - catch (e) { - if (!this._running) { - // ignore error - return; - } - this.errorEvent.fire(e); - } - } - handleOk(packet) { - if (this.initializers.resolve(packet.arg1, packet.arg0)) { - // Device has created the `Stream` - return; - } - if (this.streams.has(packet.arg1)) { - // Device has received last `WRTE` to the `Stream` - this.streams.get(packet.arg1).ack(); - return; - } - // Maybe the device is responding to a packet of last connection - // Tell the device to close the stream - this.sendPacket(AdbCommand.Close, packet.arg1, packet.arg0); - } - async handleOpen(packet) { - // AsyncOperationManager doesn't support get and skip an ID - // Use `add` + `resolve` to simulate this behavior - const [localId] = this.initializers.add(); - this.initializers.resolve(localId, undefined); - const remoteId = packet.arg0; - const controller = new AdbStreamController(localId, remoteId, this); - const stream = new AdbStream(controller); - const args = { - handled: false, - packet, - stream, - }; - this.streamEvent.fire(args); - if (args.handled) { - this.streams.set(localId, controller); - await this.sendPacket(AdbCommand.OK, localId, remoteId); - } - else { - await this.sendPacket(AdbCommand.Close, 0, remoteId); - } - } - start() { - this._running = true; - this.receiveLoop(); - } - - async createStream(service) { - if (this.appendNullToServiceString) { - service += '\0'; - } - const [localId, initializer] = this.initializers.add(); - await this.sendPacket(AdbCommand.Open, localId, 0, service); - const remoteId = await initializer; - const controller = new AdbStreamController(localId, remoteId, this); - this.streams.set(controller.localId, controller); - return new AdbStream(controller); - } - - async sendPacket(packetOrCommand, arg0, arg1, payload) { - var _a; - let init; - if (arguments.length === 1) { - init = packetOrCommand; - } - else { - init = { - command: packetOrCommand, - arg0: arg0, - arg1: arg1, - payload: typeof payload === 'string' ? this.backend.encodeUtf8(payload) : payload, - }; - } - if (init.payload && - init.payload.byteLength > this.maxPayloadSize) { - throw new Error('payload too large'); - } - try { - // `AdbPacke t.write` writes each packet in two parts - // Use a lock to prevent packets been interlaced - await this.sendLock.wait(); - const packet = AdbPacket.create(init, this.calculateChecksum, this.backend); - (_a = this.logger) === null || _a === void 0 ? void 0 : _a.onOutgoingPacket(packet); - await AdbPacket.write(packet, this.backend); - } - finally { - this.sendLock.notify(); - } - } - dispose() { - this._running = false; - for (const stream of this.streams.values()) { - stream.dispose(); - } - this.streams.clear(); - super.dispose(); - } -} - -var AdbCommand; -(function (AdbCommand) { - AdbCommand[AdbCommand["Auth"] = 1213486401] = "Auth"; - AdbCommand[AdbCommand["Close"] = 1163086915] = "Close"; - AdbCommand[AdbCommand["Connect"] = 1314410051] = "Connect"; // CNXN - AdbCommand[AdbCommand["OK"] = 1497451343] = "OK"; - AdbCommand[AdbCommand["Open"] = 1313165391] = "Open"; - AdbCommand[AdbCommand["Write"] = 1163154007] = "Write"; -})(AdbCommand || (AdbCommand = {})); -const AdbPacketWithoutPayload = new Struct({ littleEndian: true }) - .uint32('command', undefined) - .uint32('arg0') - .uint32('arg1') - .uint32('payloadLength') - .uint32('checksum') - .int32('magic'); -const AdbPacketStruct = AdbPacketWithoutPayload - .arrayBuffer('payload', { lengthField: 'payloadLength' }) - .afterParsed((value) => { - if (value[BackingField].magic !== value.magic) { - throw new Error('Invalid command'); - } -}); -var AdbPacket; -(function (AdbPacket) { - function create(init, calculateChecksum, backend) { - let checksum; - if (calculateChecksum && init.payload) { - const array = new Uint8Array(init.payload); - checksum = array.reduce((result, item) => result + item, 0); - } - else { - checksum = 0; - } - return AdbPacketStruct.create({ - ...init, - checksum, - magic: init.command ^ 0xFFFFFFFF, - }, backend); - } - AdbPacket.create = create; - async function read(backend) { - let buffer = await backend.read(24); - if (buffer.byteLength !== 24) { - // Maybe it's a payload from last connection. - // Ignore and try again - buffer = await backend.read(24); - } - let bufferUsed = false; - const stream = new BufferedStream({ - read(length) { - if (!bufferUsed) { - bufferUsed = true; - return buffer; - } - return backend.read(length); - } - }); - return AdbPacketStruct.deserialize({ - read: stream.read.bind(stream), - decodeUtf8: backend.decodeUtf8.bind(backend), - encodeUtf8: backend.encodeUtf8.bind(backend), - }); - } - AdbPacket.read = read; - async function write(packet, backend) { - // Write payload separately to avoid an extra copy - await backend.write(AdbPacketWithoutPayload.serialize(packet, backend)); - if (packet.payload) { - await backend.write(packet.payload); - } - } - AdbPacket.write = write; -})(AdbPacket || (AdbPacket = {})); - -var AdbAuthType; -(function (AdbAuthType) { - AdbAuthType[AdbAuthType["Token"] = 1] = "Token"; - AdbAuthType[AdbAuthType["Signature"] = 2] = "Signature"; - AdbAuthType[AdbAuthType["PublicKey"] = 3] = "PublicKey"; -})(AdbAuthType || (AdbAuthType = {})); -const AdbSignatureAuthenticator = async function* (backend, getNextRequest) { - for await (const key of backend.iterateKeys()) { - const packet = await getNextRequest(); - if (packet.arg0 !== AdbAuthType.Token) { - return; - } - const signature = sign(key, packet.payload); - yield { - command: AdbCommand.Auth, - arg0: AdbAuthType.Signature, - arg1: 0, - payload: signature, - }; - } -}; -const AdbPublicKeyAuthenticator = async function* (backend, getNextRequest) { - const packet = await getNextRequest(); - if (packet.arg0 !== AdbAuthType.Token) { - return; - } - let privateKey; - for await (const key of backend.iterateKeys()) { - privateKey = key; - break; - } - if (!privateKey) { - privateKey = await backend.generateKey(); - } - const publicKeyLength = calculatePublicKeyLength(); - const [publicKeyBase64Length] = calculateBase64EncodedLength(publicKeyLength); - // The public key is null terminated, - // So we allocate the buffer with one extra byte. - const publicKeyBuffer = new ArrayBuffer(publicKeyBase64Length + 1); - calculatePublicKey(privateKey, publicKeyBuffer); - encodeBase64(publicKeyBuffer, 0, publicKeyLength, publicKeyBuffer); - yield { - command: AdbCommand.Auth, - arg0: AdbAuthType.PublicKey, - arg1: 0, - payload: publicKeyBuffer - }; -}; -const AdbDefaultAuthenticators = [ - AdbSignatureAuthenticator, - AdbPublicKeyAuthenticator -]; -class AdbAuthenticationHandler { - constructor(authenticators, backend) { - this.pendingRequest = new PromiseResolver(); - this.getNextRequest = () => { - return this.pendingRequest.promise; - }; - this.authenticators = authenticators; - this.backend = backend; - } - async *runAuthenticator() { - for (const authenticator of this.authenticators) { - for await (const packet of authenticator(this.backend, this.getNextRequest)) { - // If the authenticator yielded a response - // Prepare `nextRequest` for next authentication request - this.pendingRequest = new PromiseResolver(); - // Yield the response to outer layer - yield packet; - } - // If the authenticator returned, - // Next authenticator will be given the same `pendingRequest` - } - throw new Error('Cannot authenticate with device'); - } - async handle(packet) { - if (!this.iterator) { - this.iterator = this.runAuthenticator(); - } - this.pendingRequest.resolve(packet); - const result = await this.iterator.next(); - return result.value; - } - dispose() { - var _a, _b; - (_b = (_a = this.iterator) === null || _a === void 0 ? void 0 : _a.return) === null || _b === void 0 ? void 0 : _b.call(_a); - } -} - -class AdbCommandBase extends AutoDisposable { - constructor(adb) { - super(); - this.adb = adb; - } -} - -const Version = new Struct({ littleEndian: true }).uint32('version'); -/* - * ADB uses 8 int32 fields to describe bit depths - * The only combination I have seen is RGBA8888, which is - * red_offset: 0 - * red_length: 8 - * blue_offset: 16 - * blue_length: 8 - * green_offset: 8 - * green_length: 8 - * alpha_offset: 24 - * alpha_length: 8 - */ -const AdbFrameBufferV1 = new Struct({ littleEndian: true }) - .uint32('bpp') - .uint32('size') - .uint32('width') - .uint32('height') - .uint32('red_offset') - .uint32('red_length') - .uint32('blue_offset') - .uint32('blue_length') - .uint32('green_offset') - .uint32('green_length') - .uint32('alpha_offset') - .uint32('alpha_length') - .arrayBuffer('data', { lengthField: 'size' }); -const AdbFrameBufferV2 = new Struct({ littleEndian: true }) - .uint32('bpp') - .uint32('colorSpace') - .uint32('size') - .uint32('width') - .uint32('height') - .uint32('red_offset') - .uint32('red_length') - .uint32('blue_offset') - .uint32('blue_length') - .uint32('green_offset') - .uint32('green_length') - .uint32('alpha_offset') - .uint32('alpha_length') - .arrayBuffer('data', { lengthField: 'size' }); -async function framebuffer(adb) { - const stream = await adb.createStream('framebuffer:'); - const buffered = new AdbBufferedStream(stream); - const { version } = await Version.deserialize(buffered); - switch (version) { - case 1: - return AdbFrameBufferV1.deserialize(buffered); - case 2: - return AdbFrameBufferV2.deserialize(buffered); - default: - throw new Error('Unknown FrameBuffer version'); - } -} - -function escapeArg(s) { - let result = ''; - result += `'`; - let base = 0; - while (true) { - const found = s.indexOf(`'`, base); - if (found === -1) { - result += s.substring(base); - break; - } - result += s.substring(base, found); - // a'b become a'\'b - result += String.raw `'\''`; - base = found + 1; - } - result += `'`; - return result; -} - -async function install(adb, apk, onProgress) { - const filename = `/data/local/tmp/${Math.random().toString().substr(2)}.apk`; - // Upload apk file to tmp folder - const sync = await adb.sync(); - await sync.write(filename, apk, undefined, undefined, onProgress); - sync.dispose(); - // Invoke `pm install` to install it - await adb.exec('pm', 'install', escapeArg(filename)); - // Remove the temp file - await adb.rm(filename); -} - -const AdbReverseStringResponse = new Struct({ littleEndian: true }) - .string('length', { length: 4 }) - .string('content', { lengthField: 'length' }); -const AdbReverseErrorResponse = AdbReverseStringResponse - .afterParsed((value) => { - throw new Error(value.content); -}); -class AdbReverseCommand extends AutoDisposable { - constructor(dispatcher) { - super(); - this.localPortToHandler = new Map(); - this.deviceAddressToLocalPort = new Map(); - this.listening = false; - this.dispatcher = dispatcher; - this.addDisposable(this.dispatcher.onStream(this.handleStream, this)); - } - async createBufferedStream(service) { - const stream = await this.dispatcher.createStream(service); - return new AdbBufferedStream(stream); - } - async sendRequest(service) { - const stream = await this.createBufferedStream(service); - const success = this.dispatcher.backend.decodeUtf8(await stream.read(4)) === 'OKAY'; - if (!success) { - await AdbReverseErrorResponse.deserialize(stream); - } - return stream; - } - handleStream(e) { - if (e.handled) { - return; - } - const address = this.dispatcher.backend.decodeUtf8(e.packet.payload); - // tcp:1234\0 - const port = Number.parseInt(address.substring(4)); - if (this.localPortToHandler.has(port)) { - this.localPortToHandler.get(port).onStream(e.packet, e.stream); - e.handled = true; - } - } - async list() { - const stream = await this.createBufferedStream('reverse:list-forward'); - const response = await AdbReverseStringResponse.deserialize(stream); - return response.content.split('\n').map(line => { - const [deviceSerial, localName, remoteName] = line.split(' '); - return { deviceSerial, localName, remoteName }; - }); - // No need to close the stream, device will close it - } - async add(deviceAddress, localPort, handler) { - const stream = await this.sendRequest(`reverse:forward:${deviceAddress};tcp:${localPort}`); - // `tcp:0` tells the device to pick an available port. - // However, device will response with the selected port for all `tcp:` requests. - if (deviceAddress.startsWith('tcp:')) { - const response = await AdbReverseStringResponse.deserialize(stream); - deviceAddress = `tcp:${Number.parseInt(response.content, 10)}`; - } - this.localPortToHandler.set(localPort, handler); - this.deviceAddressToLocalPort.set(deviceAddress, localPort); - return deviceAddress; - // No need to close the stream, device will close it - } - async remove(deviceAddress) { - await this.sendRequest(`reverse:killforward:${deviceAddress}`); - if (this.deviceAddressToLocalPort.has(deviceAddress)) { - this.localPortToHandler.delete(this.deviceAddressToLocalPort.get(deviceAddress)); - this.deviceAddressToLocalPort.delete(deviceAddress); - } - // No need to close the stream, device will close it - } - async removeAll() { - await this.sendRequest(`reverse:killforward-all`); - this.deviceAddressToLocalPort.clear(); - this.localPortToHandler.clear(); - // No need to close the stream, device will close it - } -} - -var AdbSyncRequestId; -(function (AdbSyncRequestId) { - AdbSyncRequestId["List"] = "LIST"; - AdbSyncRequestId["Send"] = "SEND"; - AdbSyncRequestId["Lstat"] = "STAT"; - AdbSyncRequestId["Stat"] = "STA2"; - AdbSyncRequestId["Lstat2"] = "LST2"; - AdbSyncRequestId["Data"] = "DATA"; - AdbSyncRequestId["Done"] = "DONE"; - AdbSyncRequestId["Receive"] = "RECV"; -})(AdbSyncRequestId || (AdbSyncRequestId = {})); -const AdbSyncNumberRequest = new Struct({ littleEndian: true }) - .string('id', { length: 4 }) - .uint32('arg'); -const AdbSyncDataRequest = AdbSyncNumberRequest - .arrayBuffer('data', { lengthField: 'arg' }); -async function adbSyncWriteRequest(stream, id, value) { - let buffer; - if (typeof value === 'number') { - buffer = AdbSyncNumberRequest.serialize({ - id, - arg: value, - }, stream); - } - else if (typeof value === 'string') { - buffer = AdbSyncDataRequest.serialize({ - id, - data: stream.encodeUtf8(value), - }, stream); - } - else { - buffer = AdbSyncDataRequest.serialize({ - id, - data: value, - }, stream); - } - await stream.write(buffer); -} - -var AdbSyncResponseId; -(function (AdbSyncResponseId) { - AdbSyncResponseId["Entry"] = "DENT"; - AdbSyncResponseId["Lstat"] = "STAT"; - AdbSyncResponseId["Stat"] = "STA2"; - AdbSyncResponseId["Lstat2"] = "LST2"; - AdbSyncResponseId["Done"] = "DONE"; - AdbSyncResponseId["Data"] = "DATA"; - AdbSyncResponseId["Ok"] = "OKAY"; - AdbSyncResponseId["Fail"] = "FAIL"; -})(AdbSyncResponseId || (AdbSyncResponseId = {})); -// DONE responses' size are always same as the request's normal response. -// For example DONE responses for LIST requests are 16 bytes (same as DENT responses), -// but DONE responses for STAT requests are 12 bytes (same as STAT responses) -// So we need to know responses' size in advance. -class AdbSyncDoneResponse { - constructor(length) { - this.id = AdbSyncResponseId.Done; - this.length = length; - } - async deserialize(context) { - await context.read(this.length); - return this; - } -} -const AdbSyncFailResponse = new Struct({ littleEndian: true }) - .uint32('messageLength') - .string('message', { lengthField: 'messageLength' }) - .afterParsed(object => { - throw new Error(object.message); -}); -async function adbSyncReadResponse(stream, types) { - const id = stream.backend.decodeUtf8(await stream.read(4)); - if (id === AdbSyncResponseId.Fail) { - await AdbSyncFailResponse.deserialize(stream); - } - if (types[id]) { - return types[id].deserialize(stream); - } - throw new Error('Unexpected response id'); -} - -// https://github.com/python/cpython/blob/4e581d64b8aff3e2eda99b12f080c877bb78dfca/Lib/stat.py#L36 -var LinuxFileType; -(function (LinuxFileType) { - LinuxFileType[LinuxFileType["Directory"] = 4] = "Directory"; - LinuxFileType[LinuxFileType["File"] = 8] = "File"; - LinuxFileType[LinuxFileType["Link"] = 10] = "Link"; -})(LinuxFileType || (LinuxFileType = {})); -const AdbSyncLstatResponse = new Struct({ littleEndian: true }) - .int32('mode') - .int32('size') - .int32('mtime') - .extra({ - id: AdbSyncResponseId.Lstat, - get type() { return this.mode >> 12; }, - get permission() { return this.mode & 4095; }, -}) - .afterParsed((object) => { - if (object.mode === 0 && - object.size === 0 && - object.mtime === 0) { - throw new Error('lstat failed'); - } -}); -var AdbSyncStatErrorCode; -(function (AdbSyncStatErrorCode) { - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EACCES"] = 13] = "EACCES"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EEXIST"] = 17] = "EEXIST"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EFAULT"] = 14] = "EFAULT"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EFBIG"] = 27] = "EFBIG"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EINTR"] = 4] = "EINTR"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EINVAL"] = 22] = "EINVAL"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EIO"] = 5] = "EIO"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EISDIR"] = 21] = "EISDIR"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ELOOP"] = 40] = "ELOOP"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EMFILE"] = 24] = "EMFILE"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ENAMETOOLONG"] = 36] = "ENAMETOOLONG"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ENFILE"] = 23] = "ENFILE"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ENOENT"] = 2] = "ENOENT"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ENOMEM"] = 12] = "ENOMEM"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ENOSPC"] = 28] = "ENOSPC"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ENOTDIR"] = 20] = "ENOTDIR"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EOVERFLOW"] = 75] = "EOVERFLOW"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EPERM"] = 1] = "EPERM"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["EROFS"] = 30] = "EROFS"; - AdbSyncStatErrorCode[AdbSyncStatErrorCode["ETXTBSY"] = 26] = "ETXTBSY"; -})(AdbSyncStatErrorCode || (AdbSyncStatErrorCode = {})); -const AdbSyncStatResponse = new Struct({ littleEndian: true }) - .uint32('error', undefined, placeholder()) - .uint64('dev') - .uint64('ino') - .uint32('mode') - .uint32('nlink') - .uint32('uid') - .uint32('gid') - .uint64('size') - .uint64('atime') - .uint64('mtime') - .uint64('ctime') - .extra({ - id: AdbSyncResponseId.Stat, - get type() { return this.mode >> 12; }, - get permission() { return this.mode & 4095; }, -}) - .afterParsed((object) => { - if (object.error) { - throw new Error(AdbSyncStatErrorCode[object.error]); - } -}); -const StatResponseType = { - [AdbSyncResponseId.Stat]: AdbSyncStatResponse, -}; -const LstatResponseType = { - [AdbSyncResponseId.Lstat]: AdbSyncLstatResponse, -}; -const Lstat2ResponseType = { - [AdbSyncResponseId.Lstat2]: AdbSyncStatResponse, -}; -async function adbSyncLstat(stream, path, v2) { - let requestId; - let responseType; - if (v2) { - requestId = AdbSyncRequestId.Lstat2; - responseType = Lstat2ResponseType; - } - else { - requestId = AdbSyncRequestId.Lstat; - responseType = LstatResponseType; - } - await adbSyncWriteRequest(stream, requestId, path); - return adbSyncReadResponse(stream, responseType); -} -async function adbSyncStat(stream, path) { - await adbSyncWriteRequest(stream, AdbSyncRequestId.Stat, path); - return adbSyncReadResponse(stream, StatResponseType); -} - -const AdbSyncEntryResponse = AdbSyncLstatResponse - .afterParsed() - .uint32('nameLength') - .string('name', { lengthField: 'nameLength' }) - .extra({ id: AdbSyncResponseId.Entry }); -const ResponseTypes = { - [AdbSyncResponseId.Entry]: AdbSyncEntryResponse, - [AdbSyncResponseId.Done]: new AdbSyncDoneResponse(AdbSyncEntryResponse.size), -}; -async function* adbSyncOpenDir(stream, path) { - await adbSyncWriteRequest(stream, AdbSyncRequestId.List, path); - while (true) { - const response = await adbSyncReadResponse(stream, ResponseTypes); - switch (response.id) { - case AdbSyncResponseId.Entry: - yield response; - break; - case AdbSyncResponseId.Done: - return; - default: - throw new Error('Unexpected response id'); - } - } -} - -const AdbSyncDataResponse = new Struct({ littleEndian: true }) - .uint32('dataLength') - .arrayBuffer('data', { lengthField: 'dataLength' }) - .extra({ id: AdbSyncResponseId.Data }); -const ResponseTypes$1 = { - [AdbSyncResponseId.Data]: AdbSyncDataResponse, - [AdbSyncResponseId.Done]: new AdbSyncDoneResponse(AdbSyncDataResponse.size), -}; -async function* adbSyncPull(stream, path) { - await adbSyncWriteRequest(stream, AdbSyncRequestId.Receive, path); - while (true) { - const response = await adbSyncReadResponse(stream, ResponseTypes$1); - switch (response.id) { - case AdbSyncResponseId.Data: - yield response.data; - break; - case AdbSyncResponseId.Done: - return; - default: - throw new Error('Unexpected response id'); - } - } -} - -const AdbSyncOkResponse = new Struct({ littleEndian: true }) - .uint32('unused'); -const ResponseTypes$2 = { - [AdbSyncResponseId.Ok]: AdbSyncOkResponse, -}; -async function* chunkAsyncIterable(value, size) { - let result = new Uint8Array(size); - let index = 0; - for await (let buffer of value) { - // `result` has some data, `result + buffer` is enough - if (index !== 0 && index + buffer.byteLength >= size) { - const remainder = size - index; - result.set(new Uint8Array(buffer, 0, remainder), index); - yield result.buffer; - result = new Uint8Array(size); - index = 0; - if (buffer.byteLength > remainder) { - // `buffer` still has some data - buffer = buffer.slice(remainder); - } - else { - continue; - } - } - // `result` is empty, `buffer` alone is enough - if (buffer.byteLength >= size) { - let remainder = false; - for (const chunk of chunkArrayLike(buffer, size)) { - if (chunk.byteLength === size) { - yield chunk; - continue; - } - // `buffer` still has some data - remainder = true; - buffer = chunk; - } - if (!remainder) { - continue; - } - } - // `result` has some data but `result + buffer` is still not enough - // or after previous steps `buffer` still has some data - result.set(new Uint8Array(buffer), index); - index += buffer.byteLength; - } - if (index !== 0) { - yield result.buffer.slice(0, index); - } -} -const AdbSyncMaxPacketSize = 64 * 1024; -async function adbSyncPush(stream, filename, content, mode = (LinuxFileType.File << 12) | 0o666, mtime = (Date.now() / 1000) | 0, packetSize = AdbSyncMaxPacketSize, onProgress) { - const pathAndMode = `${filename},${mode.toString()}`; - await adbSyncWriteRequest(stream, AdbSyncRequestId.Send, pathAndMode); - let chunkReader; - if ('length' in content || 'byteLength' in content) { - chunkReader = chunkArrayLike(content, packetSize); - } - else { - chunkReader = chunkAsyncIterable(content, packetSize); - } - let uploaded = 0; - for await (const buffer of chunkReader) { - await adbSyncWriteRequest(stream, AdbSyncRequestId.Data, buffer); - uploaded += buffer.byteLength; - onProgress === null || onProgress === void 0 ? void 0 : onProgress(uploaded); - } - await adbSyncWriteRequest(stream, AdbSyncRequestId.Done, mtime); - await adbSyncReadResponse(stream, ResponseTypes$2); -} - -var AdbFeatures; -(function (AdbFeatures) { - AdbFeatures["StatV2"] = "stat_v2"; - AdbFeatures["Cmd"] = "cmd"; -})(AdbFeatures || (AdbFeatures = {})); - -class AdbSync extends AutoDisposable { - constructor(adb, stream) { - super(); - this.sendLock = this.addDisposable(new AutoResetEvent()); - this.adb = adb; - this.stream = new AdbBufferedStream(stream); - } - get supportStat() { - return this.adb.features.includes(AdbFeatures.StatV2); - } - async lstat(path) { - await this.sendLock.wait(); - try { - return adbSyncLstat(this.stream, path, this.supportStat); - } - finally { - this.sendLock.notify(); - } - } - async stat(path) { - if (!this.supportStat) { - throw new Error('Not supported'); - } - await this.sendLock.wait(); - try { - return adbSyncStat(this.stream, path); - } - finally { - this.sendLock.notify(); - } - } - async isDirectory(path) { - try { - await this.lstat(path + '/'); - return true; - } - catch (e) { - return false; - } - } - async *opendir(path) { - await this.sendLock.wait(); - try { - yield* adbSyncOpenDir(this.stream, path); - } - finally { - this.sendLock.notify(); - } - } - async readdir(path) { - const results = []; - for await (const entry of this.opendir(path)) { - results.push(entry); - } - return results; - } - async *read(filename) { - await this.sendLock.wait(); - try { - yield* adbSyncPull(this.stream, filename); - } - finally { - this.sendLock.notify(); - } - } - async write(filename, content, mode, mtime, onProgress) { - await this.sendLock.wait(); - try { - await adbSyncPush(this.stream, filename, content, mode, mtime, undefined, onProgress); - } - finally { - this.sendLock.notify(); - } - } - dispose() { - super.dispose(); - this.stream.close(); - } -} - -class AdbTcpIpCommand extends AdbCommandBase { - async setPort(port) { - if (port <= 0) { - throw new Error(`Invalid port ${port}`); - } - const output = await this.adb.createStreamAndReadAll(`tcpip:${port}`); - if (output !== `restarting in TCP mode port: ${port}\n`) { - throw new Error('Invalid response'); - } - } - async disable() { - const output = await this.adb.createStreamAndReadAll('usb:'); - if (output !== 'restarting in USB mode\n') { - throw new Error('Invalid response'); - } - } -} - -const WebUsbDeviceFilter = { - classCode: 0xFF, - subclassCode: 0x42, - protocolCode: 1, -}; - -const PrivateKeyStorageKey = 'private-key'; -const Utf8Encoder = new TextEncoder(); -const Utf8Decoder = new TextDecoder(); -function encodeUtf8(input) { - return Utf8Encoder.encode(input); -} -function decodeUtf8(buffer) { - return Utf8Decoder.decode(buffer); -} - -export class AdbWebBackend { - constructor(device) { - this.disconnectEvent = new EventEmitter(); - this.onDisconnected = this.disconnectEvent.event; - this.handleDisconnect = (e) => { - if (e.device === this._device) { - this.disconnectEvent.fire(); - } - }; - this._device = device; - window.navigator.usb.addEventListener('disconnect', this.handleDisconnect); - } - static isSupported() { - var _a; - return !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.usb); - } - static async getDevices() { - const devices = await window.navigator.usb.getDevices(); - return devices.map(device => new AdbWebBackend(device)); - } - static async requestDevice() { - try { - const device = await navigator.usb.requestDevice({ filters: [WebUsbDeviceFilter] }); - return new AdbWebBackend(device); - } - catch (e) { - switch (e.name) { - case 'NotFoundError': - return undefined; - default: - throw e; - } - } - } - get serial() { return this._device.serialNumber; } - get name() { return this._device.productName; } - async connect() { - var _a; - if (!this._device.opened) { - await this._device.open(); - } - - for (const configuration of this._device.configurations) { - for (const interface_ of configuration.interfaces) { - for (const alternate of interface_.alternates) { - if (alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && - alternate.interfaceClass === WebUsbDeviceFilter.classCode && - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode) { - if (((_a = this._device.configuration) === null || _a === void 0 ? void 0 : _a.configurationValue) !== configuration.configurationValue) { - await this._device.selectConfiguration(configuration.configurationValue); - } - if (!interface_.claimed) { - await this._device.claimInterface(interface_.interfaceNumber); - } - if (interface_.alternate.alternateSetting !== alternate.alternateSetting) { - await this._device.selectAlternateInterface(interface_.interfaceNumber, alternate.alternateSetting); - } - for (const endpoint of alternate.endpoints) { - switch (endpoint.direction) { - case 'in': - this._inEndpointNumber = endpoint.endpointNumber; - if (this._outEndpointNumber !== undefined) { - return; - } - break; - case 'out': - this._outEndpointNumber = endpoint.endpointNumber; - if (this._inEndpointNumber !== undefined) { - return; - } - break; - } - } - } - } - } - } - throw new Error('Unknown error'); - } - *iterateKeys() { - const privateKey = window.localStorage.getItem(PrivateKeyStorageKey); - if (privateKey) { - yield decodeBase64(privateKey); - } - } - async generateKey() { - const { privateKey: cryptoKey } = await crypto.subtle.generateKey({ - name: 'RSASSA-PKCS1-v1_5', - modulusLength: 2048, - // 65537 - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - hash: 'SHA-1', - }, true, ['sign', 'verify']); - const privateKey = await crypto.subtle.exportKey('pkcs8', cryptoKey); - window.localStorage.setItem(PrivateKeyStorageKey, decodeUtf8(encodeBase64(privateKey))); - return privateKey; - } - encodeUtf8(input) { - return encodeUtf8(input); - } - decodeUtf8(buffer) { - return decodeUtf8(buffer); - } - async write(buffer) { - await this._device.transferOut(this._outEndpointNumber, buffer); - } - async read(length) { - const result = await this._device.transferIn(this._inEndpointNumber, length); - if (result.status === 'stall') { - await this._device.clearHalt('in', this._inEndpointNumber); - } - const { buffer } = result.data; - return buffer; - } - async dispose() { - try{ - window.navigator.usb.removeEventListener('disconnect', this.handleDisconnect); - } catch(e) {} - try { - this.disconnectEvent.dispose(); - } catch (e) {} - try { - await this._device.close(); - } catch (e) {} - } -} - -var AdbPropKey; -(function (AdbPropKey) { - AdbPropKey["Product"] = "ro.product.name"; - AdbPropKey["Model"] = "ro.product.model"; - AdbPropKey["Device"] = "ro.product.device"; - AdbPropKey["Features"] = "features"; -})(AdbPropKey || (AdbPropKey = {})); -export class Adb2 { - constructor(backend, logger) { - this._connected = false; - this.packetDispatcher = new AdbPacketDispatcher(backend, logger); - this.tcpip = new AdbTcpIpCommand(this); - this.reverse = new AdbReverseCommand(this.packetDispatcher); - backend.onDisconnected(this.dispose, this); - } - get backend() { return this.packetDispatcher.backend; } - get onDisconnected() { return this.backend.onDisconnected; } - get connected() { return this._connected; } - get name() { return this.backend.name; } - get protocolVersion() { return this._protocolVersion; } - get product() { return this._product; } - get model() { return this._model; } - get device() { return this._device; } - get features() { return this._features; } - async connect(authenticators = AdbDefaultAuthenticators) { - var _a, _b; - await ((_b = (_a = this.backend).connect) === null || _b === void 0 ? void 0 : _b.call(_a)); - this.packetDispatcher.maxPayloadSize = 0x1000; - this.packetDispatcher.calculateChecksum = true; - this.packetDispatcher.appendNullToServiceString = true; - this.packetDispatcher.start(); - const version = 0x01000001; - const versionNoChecksum = 0x01000001; - const maxPayloadSize = 0x100000; - const features = [ - 'shell_v2', - 'cmd', - AdbFeatures.StatV2, - 'ls_v2', - 'fixed_push_mkdir', - 'apex', - 'abb', - 'fixed_push_symlink_timestamp', - 'abb_exec', - 'remount_shell', - 'track_app', - 'sendrecv_v2', - 'sendrecv_v2_brotli', - 'sendrecv_v2_lz4', - 'sendrecv_v2_zstd', - 'sendrecv_v2_dry_run_send', - ].join(','); - const resolver = new PromiseResolver(); - const authHandler = new AdbAuthenticationHandler(authenticators, this.backend); - const disposableList = new DisposableList(); - disposableList.add(this.packetDispatcher.onPacket(async (e) => { - e.handled = true; - const { packet } = e; - try { - switch (packet.command) { - case AdbCommand.Connect: - this.packetDispatcher.maxPayloadSize = Math.min(maxPayloadSize, packet.arg1); - const finalVersion = Math.min(version, packet.arg0); - this._protocolVersion = finalVersion; - if (finalVersion >= versionNoChecksum) { - this.packetDispatcher.calculateChecksum = false; - // Android prior to 9.0.0 uses char* to parse service string - // thus requires an extra null character - this.packetDispatcher.appendNullToServiceString = false; - } - this.parseBanner(this.backend.decodeUtf8(packet.payload)); - resolver.resolve(); - break; - case AdbCommand.Auth: - const authPacket = await authHandler.handle(e.packet); - await this.packetDispatcher.sendPacket(authPacket); - break; - case AdbCommand.Close: - // Last connection was interrupted - // Ignore this packet, device will recover - break; - default: - throw new Error('Device not in correct state. Reconnect your device and try again'); - } - } - catch (e) { - resolver.reject(e); - } - })); - disposableList.add(this.packetDispatcher.onError(e => { - resolver.reject(e); - })); - // Android prior 9.0.0 requires the null character - // Newer versions can also handle the null character - // The terminating `;` is required in formal definition - // But ADB daemon can also work without it - await this.packetDispatcher.sendPacket(AdbCommand.Connect, version, maxPayloadSize, `host::features=${features};\0`); - try { - await resolver.promise; - this._connected = true; - } - finally { - disposableList.dispose(); - } - } - parseBanner(banner) { - this._features = []; - const pieces = banner.split('::'); - if (pieces.length > 1) { - const props = pieces[1]; - for (const prop of props.split(';')) { - if (!prop) { - continue; - } - const keyValue = prop.split('='); - if (keyValue.length !== 2) { - continue; - } - const [key, value] = keyValue; - switch (key) { - case AdbPropKey.Product: - this._product = value; - break; - case AdbPropKey.Model: - this._model = value; - break; - case AdbPropKey.Device: - this._device = value; - break; - case AdbPropKey.Features: - this._features = value.split(','); - break; - } - } - } - } - shell() { - return this.createStream('shell:'); - } - spawn(command, ...args) { - // TODO: use shell protocol - return this.createStream(`shell:${command} ${args.join(' ')}`); - } - exec(command, ...args) { - // TODO: use shell protocol - return this.createStreamAndReadAll(`shell:${command} ${args.join(' ')}`); - } - async getProp(key) { - const output = await this.exec('getprop', key); - return output.trim(); - } - async rm(...filenames) { - return await this.exec('rm', '-rf', ...filenames.map(arg => escapeArg(arg))); - } - async install(apk, onProgress) { - return await install(this, apk, onProgress); - } - async sync() { - const stream = await this.createStream('sync:'); - return new AdbSync(this, stream); - } - - async framebuffer() { - return framebuffer(this); - } - async createStream(service) { - return this.packetDispatcher.createStream(service); - } - async createStreamAndReadAll(service) { - const stream = await this.createStream(service); - const resolver = new PromiseResolver(); - let result = ''; - stream.onData(buffer => { - result += this.backend.decodeUtf8(buffer); - }); - stream.onClose(() => resolver.resolve(result)); - return resolver.promise; - } - async dispose() { - this.packetDispatcher.dispose(); - await this.backend.dispose(); - } -} - -export class AdbWebBackend3 { - constructor(device) { - this.disconnectEvent = new EventEmitter(); - this.onDisconnected = this.disconnectEvent.event; - this.handleDisconnect = (e) => { - if (e.device === this._device) { - this.disconnectEvent.fire(); - } - }; - this._device = device; - window.navigator.usb.addEventListener('disconnect', this.handleDisconnect); - } - static isSupported() { - var _a; - return !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.usb); - } - - static async requestDevice() { - try { - const device = await navigator.usb.requestDevice({ filters: [WebUsbDeviceFilter] }); - return new AdbWebBackend3(device); - } - catch (e) { - switch (e.name) { - case 'NotFoundError': - return undefined; - default: - throw e; - } - } - } - - async connect() { - var _a; - if (!this._device.opened) { - await this._device.open(); - } - - for (const configuration of this._device.configurations) { - for (const interface_ of configuration.interfaces) { - for (const alternate of interface_.alternates) { - if (alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && - alternate.interfaceClass === WebUsbDeviceFilter.classCode && - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode) { - if (((_a = this._device.configuration) === null || _a === void 0 ? void 0 : _a.configurationValue) !== configuration.configurationValue) { - await this._device.selectConfiguration(configuration.configurationValue); - } - if (!interface_.claimed) { - await this._device.claimInterface(interface_.interfaceNumber); - } - if (interface_.alternate.alternateSetting !== alternate.alternateSetting) { - await this._device.selectAlternateInterface(interface_.interfaceNumber, alternate.alternateSetting); - } - for (const endpoint of alternate.endpoints) { - switch (endpoint.direction) { - case 'in': - this._inEndpointNumber = endpoint.endpointNumber; - if (this._outEndpointNumber !== undefined) { - return; - } - break; - case 'out': - this._outEndpointNumber = endpoint.endpointNumber; - if (this._inEndpointNumber !== undefined) { - return; - } - break; - } - } - } - } - } - } - throw new Error('Unknown error'); - } - encodeUtf8(input) { - return encodeUtf8(input); - } - decodeUtf8(buffer) { - return decodeUtf8(buffer); - } - async write(buffer) { - await this._device.transferOut(this._outEndpointNumber, buffer); - } - async read(length) { - const result = await this._device.transferIn(this._inEndpointNumber, length); - if (result.status === 'stall') { - await this._device.clearHalt('in', this._inEndpointNumber); - } - const { buffer } = result.data; - return buffer; - } - - async read2(length) { - const result = await this._device.transferIn(this._inEndpointNumber, length); - if (result.status === 'stall') { - await this._device.clearHalt('in', this._inEndpointNumber); - } - const { buffer } = result.data; - - const stream = new BufferedStream({ - read(length) { - return buffer; - } - }); - return AdbPacketStruct.deserialize({ - read: stream.read.bind(stream), - decodeUtf8: decodeUtf8.bind(this), - encodeUtf8: encodeUtf8.bind(this), - }); - } - async dispose() { - window.navigator.usb.removeEventListener('disconnect', this.handleDisconnect); - this.disconnectEvent.dispose(); - await this._device.close(); - } -} - -export class Adb3 { - // This one is dedicated for adb sidelaod - constructor(backend) { - this._connected = false; - this.backend = backend; - this.sendLock = new AutoResetEvent(); - this.initializers = new AsyncOperationManager(1); - } - get connected() { return this._connected; } - async connect() { - var _a, _b; - await ((_b = (_a = this.backend).connect) === null || _b === void 0 ? void 0 : _b.call(_a)); - this.calculateChecksum = true; - this.appendNullToServiceString = true; - const version = 0x01000001; - const maxPayloadSize = 0x100000; - - await this.sendPacket(AdbCommand.Connect, version, maxPayloadSize, "host::\0"); - const r = await AdbPacket.read(this.backend); - if (r.command == AdbCommand.Connect) { - // - } - } - parseBanner(banner) { - this._features = []; - const pieces = banner.split('::'); - if (pieces.length > 1) { - const props = pieces[1]; - for (const prop of props.split(';')) { - if (!prop) { - continue; - } - const keyValue = prop.split('='); - if (keyValue.length !== 2) { - continue; - } - const [key, value] = keyValue; - switch (key) { - case AdbPropKey.Product: - this._product = value; - break; - case AdbPropKey.Model: - this._model = value; - break; - case AdbPropKey.Device: - this._device = value; - break; - case AdbPropKey.Features: - this._features = value.split(','); - break; - } - } - } - } - spawn(command, ...args) { - // TODO: use shell protocol - return this.createStream(`shell:${command} ${args.join(' ')}`); - } - exec(command, ...args) { - // TODO: use shell protocol - return this.createStreamAndReadAll(`shell:${command} ${args.join(' ')}`); - } - async getProp(key) { - const output = await this.exec('getprop', key); - return output.trim(); - } - - async createStream(service) { - const localId = 1; - service+='\0'; - let remoteId; - await this.sendPacket(AdbCommand.Open, localId, 0, service); - //const remoteId = await initializer; - const r = await AdbPacket.read(this.backend); - if (r.command == AdbCommand.OK) { - remoteId = r.arg0; - const controller = new AdbStreamController(localId, remoteId, this); - return new AdbStream(controller); - } - } - - async dispose() { - this.packetDispatcher.dispose(); - await this.backend.dispose(); - } - async sendPacket(packetOrCommand, arg0, arg1, payload) { - var _a; - let init; - if (arguments.length === 1) { - init = packetOrCommand; - } - else { - init = { - command: packetOrCommand, - arg0: arg0, - arg1: arg1, - payload: typeof payload === 'string' ? this.backend.encodeUtf8(payload) : payload, - }; - } - if (init.payload && - init.payload.byteLength > this.maxPayloadSize) { - throw new Error('payload too large'); - } - try { - // `AdbPacke t.write` writes each packet in two parts - // Use a lock to prevent packets been interlaced - - this.sendLock.wait() - const packet = AdbPacket.create(init, this.calculateChecksum, this.backend); - await AdbPacket.write(packet, this.backend); - } - catch(e){ - console.log("error XXX", e); - } - finally { - this.sendLock.notify(); - } - } -} -- GitLab From c938e9818335d6cc8319a0fdb362e2313d88e9fb Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 23 Jan 2025 15:15:38 +0100 Subject: [PATCH 07/19] Modification of DeviceManager code to take account of changes in Adb and adbDaemonWebUsbDevice object properties. In short, the information (product, model, device(codename)) is obtained from the Adb class. And the name from adbDaemonWebUsbDevice. --- app/src/controller.manager.js | 29 +++++++++++++------------- app/src/controller/device/adb.class.js | 4 ++-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/app/src/controller.manager.js b/app/src/controller.manager.js index 990540e..298ddd5 100644 --- a/app/src/controller.manager.js +++ b/app/src/controller.manager.js @@ -314,10 +314,9 @@ export class Controller { WDebug.log("Security patch Error"); current_security_path_level = null; } - - let this_model = this.deviceManager.adb.webusb.device; + let this_model = this.deviceManager.adb.webusb.transport.banner.device; // https://gitlab.e.foundation/e/os/backlog/-/issues/2604#note_609234 - const model = this.deviceManager.adb.webusb.model; + const model = this.deviceManager.adb.webusb.transport.banner.model; if (model.includes("Teracube") && model.includes("2e")) { try { const serial = await this.deviceManager.adb.getSerialNumber(); @@ -329,31 +328,31 @@ export class Controller { } else { const id = "model " + - this.deviceManager.adb.webusb.model + + this.deviceManager.adb.webusb.transport.banner.model + " " + "product " + - this.deviceManager.adb.webusb.product + + this.deviceManager.adb.webusb.transport.banner.product + " " + "name " + - this.deviceManager.adb.webusb.name + + this.deviceManager.adb.device.name + " " + "device " + - this.deviceManager.adb.webusb.device; + this.deviceManager.adb.webusb.transport.banner.device; throw new Error("Cannot find device resource", id); } } catch { const id = "model " + - this.deviceManager.adb.webusb.model + + this.deviceManager.adb.webusb.transport.banner.model + " " + "product " + - this.deviceManager.adb.webusb.product + + this.deviceManager.adb.webusb.transport.banner.product + " " + "name " + - this.deviceManager.adb.webusb.name + + this.deviceManager.adb.device.name + " " + "device " + - this.deviceManager.adb.webusb.device; + this.deviceManager.adb.webusb.transport.banner.device; throw new Error("Error on getting device resource", id); } } @@ -364,16 +363,16 @@ export class Controller { } catch { const id = "model " + - this.deviceManager.adb.webusb.model + + this.deviceManager.adb.webusb.transport.banner.model + " " + "product " + - this.deviceManager.adb.webusb.product + + this.deviceManager.adb.webusb.transport.banner.product + " " + "name " + - this.deviceManager.adb.webusb.name + + this.deviceManager.adb.device.name + " " + "device " + - this.deviceManager.adb.webusb.device; + this.deviceManager.adb.webusb.transport.banner.device; throw new Error("Error on getting devcice resource", id); } } diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index 957c30c..0449d18 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -66,7 +66,7 @@ export class ADB extends Device { } getProductName() { - return this.webusb.name; + return this.device.name; } async getAndroidVersion() { @@ -74,7 +74,7 @@ export class ADB extends Device { } async getSerialNumber() { - return this.webusb.getProp("ro.boot.serialno"); + return this.webusb.getProp("ro.boot.serialno"); } async runCommand(cmd) { -- GitLab From 64d230ba49516466f5634b5a7651e8059b8050fb Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 23 Jan 2025 16:07:38 +0100 Subject: [PATCH 08/19] Modification to take account of changes in the way the mobile is rebooted (bootloader) --- app/src/controller/device/adb.class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index 0449d18..f9d5b6e 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -83,7 +83,7 @@ export class ADB extends Device { } async reboot(mode) { - const res = await this.webusb.createStreamAndReadAll(`reboot:${mode}`); + const res = await this.webusb.power.reboot(mode); return res; } } -- GitLab From cc91e5a929de1d4baf9fa0293bf9ce5f52c5ffcd Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 23 Jan 2025 16:51:57 +0100 Subject: [PATCH 09/19] Modification to take account of changes in the way the mobile needs to starts its adb connection --- app/src/controller/device/recovery.class.js | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index 71133b8..6585974 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -2,7 +2,9 @@ import { MessageClass } from "../../lib/webadb/message.class.js"; import { MessageHeader } from "../../lib/webadb/message-header.class.js"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -//import { AdbWebBackend3, Adb3 } from "../../lib/webadb/adb.bundle.js"; +import { AdbDaemonWebUsbDeviceManager } from "@yume-chan/adb-daemon-webusb"; +import { Adb, AdbDaemonTransport } from "@yume-chan/adb"; +import AdbWebCredentialStore from "@yume-chan/adb-credential-web"; export class Recovery extends Device { constructor(device) { @@ -32,15 +34,22 @@ export class Recovery extends Device { if (this.device && this.device.isConnected) { WDebug.log("Connect recovery the device is connected"); } else { - const adbWebBackend = await AdbWebBackend3.requestDevice(); - WDebug.log("adbWebBackend = ", adbWebBackend); - const adbDevice = new Adb3(adbWebBackend, null); //adb.bundle.js - WDebug.log("adbDevice = ", adbDevice); - await adbDevice.connect(); - WDebug.log("adbDevice connected"); - this.device = adbWebBackend._device; - this.webusb = adbDevice; - this.adbWebBackend = adbWebBackend; + + const Manager = AdbDaemonWebUsbDeviceManager.BROWSER; + + const adbDaemonWebUsbDevice = await Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ + const connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + const credentialStore = new AdbWebCredentialStore(); + const transport = await AdbDaemonTransport.authenticate({ + serial: connection.deserial, + connection, + credentialStore: credentialStore + }); + const adb = new Adb(transport); + this.device = adbDaemonWebUsbDevice; + this.webusb = adb; /*Adb*/ + + res = true; } } catch (e) { this.device = null; -- GitLab From 9dcae3cdfa024c7fd9a8e4b3fea2720ae20fde95 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 31 Jan 2025 18:20:20 +0100 Subject: [PATCH 10/19] =?UTF-8?q?Functional=20sideload.=20Isolation=20of?= =?UTF-8?q?=20sideload=20methods=20in=20the=20=E2=80=9Cadb-sideload.js?= =?UTF-8?q?=E2=80=9D=20file=20and=20implementation=20of=20sideload=20logic?= =?UTF-8?q?=20in=20the=20=E2=80=9Crecovery.class.js=E2=80=9D=20file.=20Del?= =?UTF-8?q?ete=20unused=20files.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/controller/adb-sideload.js | 840 ++++++++++++++++++++ app/src/controller/device/adb.class.js | 33 +- app/src/controller/device/recovery.class.js | 58 +- app/src/controller/downloader.manager.js | 7 +- app/src/lib/webadb/helper.class.js | 56 -- app/src/lib/webadb/message-header.class.js | 61 -- app/src/lib/webadb/message.class.js | 149 ---- 7 files changed, 888 insertions(+), 316 deletions(-) create mode 100644 app/src/controller/adb-sideload.js delete mode 100644 app/src/lib/webadb/helper.class.js delete mode 100644 app/src/lib/webadb/message-header.class.js delete mode 100644 app/src/lib/webadb/message.class.js diff --git a/app/src/controller/adb-sideload.js b/app/src/controller/adb-sideload.js new file mode 100644 index 0000000..14850bd --- /dev/null +++ b/app/src/controller/adb-sideload.js @@ -0,0 +1,840 @@ +import { AdbCommand, + AutoResetEvent + } from "@yume-chan/adb"; + +import { AsyncOperationManager } from "@yume-chan/async"; + + +// ADB Message structures +// There are probably equivalents in the new adb library. + +export class MessageClass { + constructor(header, data) { + this.header = header; + this.data = data; + } + + static checksum(dataView) { + let sum = 0; + for (let i = 0; i < dataView.byteLength; i++) { + sum += dataView.getUint8(i); + } + return sum & 0xffffffff; + } +} + +export class MessageHeader { + + constructor(cmd, arg0, arg1, length, checksum) { + this.cmd = cmd; + this.arg0 = arg0; + this.arg1 = arg1; + this.length = length; + this.checksum = checksum; + } + + toDataView() { + const view = new DataView(new ArrayBuffer(24)); + const rawCmd = this.encodeCmd(this.cmd); + const magic = rawCmd ^ 0xffffffff; + view.setUint32(0, rawCmd, true); + view.setUint32(4, this.arg0, true); + view.setUint32(8, this.arg1, true); + view.setUint32(12, this.length, true); + view.setUint32(16, this.checksum, true); + view.setUint32(20, magic, true); + return view; + } + + encodeCmd(cmd) { + const encoder = new TextEncoder(); + const buffer = encoder.encode(cmd).buffer; + const view = new DataView(buffer); + return view.getUint32(0, true); + } +} + +/* +Classes and utilities from an old adb lib. +There are probably equivalents in the new adb library. +It would be interesting to find them, but it may be rather difficult (name change and perhaps synthetics). +*/ + +const BackingField = Symbol('BackingField'); +function getBackingField(object, field) { + return object[BackingField][field]; +} +function setBackingField(object, field, value) { + object[BackingField][field] = value; +} +function defineSimpleAccessors(object, field) { + Object.defineProperty(object, field, { + configurable: true, + enumerable: true, + get() { return getBackingField(object, field); }, + set(value) { setBackingField(object, field, value); }, + }); +} + +var Array; +(function (Array) { + let SubType; + (function (SubType) { + SubType[SubType["ArrayBuffer"] = 0] = "ArrayBuffer"; + SubType[SubType["String"] = 1] = "String"; + })(SubType = Array.SubType || (Array.SubType = {})); + function initialize(object, field, value) { + switch (field.subType) { + case SubType.ArrayBuffer: + Object.defineProperty(object, field.name, { + configurable: true, + enumerable: true, + get() { + return getBackingField(object, field.name).buffer; + }, + set(buffer) { + setBackingField(object, field.name, { buffer }); + }, + }); + break; + case SubType.String: + Object.defineProperty(object, field.name, { + configurable: true, + enumerable: true, + get() { + return getBackingField(object, field.name).string; + }, + set(string) { + setBackingField(object, field.name, { string }); + }, + }); + break; + default: + throw new Error('Unknown type'); + } + setBackingField(object, field.name, value); + } + Array.initialize = initialize; +})(Array || (Array = {})); + +const registry = {}; +function getFieldTypeDefinition(type) { + return registry[type]; +} +function registerFieldTypeDefinition(_field, _initExtra, methods) { + registry[methods.type] = methods; +} + +var FieldType; +(function (FieldType) { + FieldType[FieldType["Number"] = 0] = "Number"; + FieldType[FieldType["FixedLengthArray"] = 1] = "FixedLengthArray"; + FieldType[FieldType["VariableLengthArray"] = 2] = "VariableLengthArray"; +})(FieldType || (FieldType = {})); + +function placeholder() { + return undefined; +} + +registerFieldTypeDefinition(placeholder(), placeholder(), { + type: FieldType.FixedLengthArray, + async deserialize({ context, field }) { + const buffer = await context.read(field.options.length); + switch (field.subType) { + case Array.SubType.ArrayBuffer: + return { value: buffer }; + case Array.SubType.String: + return { + value: context.decodeUtf8(buffer), + extra: buffer + }; + default: + throw new Error('Unknown type'); + } + }, + getSize({ field }) { + return field.options.length; + }, + initialize({ extra, field, object, value }) { + const backingField = {}; + if (typeof value === 'string') { + backingField.string = value; + if (extra) { + backingField.buffer = extra; + } + } + else { + backingField.buffer = value; + } + Array.initialize(object, field, backingField); + }, + serialize({ context, dataView, field, object, offset }) { + var _a; + const backingField = getBackingField(object, field.name); + (_a = backingField.buffer) !== null && _a !== void 0 ? _a : (backingField.buffer = context.encodeUtf8(backingField.string)); + new Uint8Array(dataView.buffer).set(new Uint8Array(backingField.buffer), offset); + } +}); + +var Number$1; +(function (Number) { + let SubType; + (function (SubType) { + SubType[SubType["Uint8"] = 0] = "Uint8"; + SubType[SubType["Uint16"] = 1] = "Uint16"; + SubType[SubType["Int32"] = 2] = "Int32"; + SubType[SubType["Uint32"] = 3] = "Uint32"; + SubType[SubType["Uint64"] = 4] = "Uint64"; + SubType[SubType["Int64"] = 5] = "Int64"; + })(SubType = Number.SubType || (Number.SubType = {})); + Number.SizeMap = { + [SubType.Uint8]: 1, + [SubType.Uint16]: 2, + [SubType.Int32]: 4, + [SubType.Uint32]: 4, + [SubType.Uint64]: 8, + [SubType.Int64]: 8, + }; + Number.DataViewGetterMap = { + [SubType.Uint8]: 'getUint8', + [SubType.Uint16]: 'getUint16', + [SubType.Int32]: 'getInt32', + [SubType.Uint32]: 'getUint32', + [SubType.Uint64]: 'getBigUint64', + [SubType.Int64]: 'getBigInt64', + }; + Number.DataViewSetterMap = { + [SubType.Uint8]: 'setUint8', + [SubType.Uint16]: 'setUint16', + [SubType.Int32]: 'setInt32', + [SubType.Uint32]: 'setUint32', + [SubType.Uint64]: 'setBigUint64', + [SubType.Int64]: 'setBigInt64', + }; +})(Number$1 || (Number$1 = {})); +registerFieldTypeDefinition(placeholder(), undefined, { + type: FieldType.Number, + getSize({ field }) { + return Number$1.SizeMap[field.subType]; + }, + async deserialize({ context, field, options }) { + const buffer = await context.read(Number$1.SizeMap[field.subType]); + const view = new DataView(buffer); + const value = view[Number$1.DataViewGetterMap[field.subType]](0, options.littleEndian); + return { value }; + }, + serialize({ dataView, field, object, offset, options }) { + dataView[Number$1.DataViewSetterMap[field.subType]](offset, object[field.name], options.littleEndian); + }, +}); + +var VariableLengthArray; +(function (VariableLengthArray) { + let EmptyBehavior; + (function (EmptyBehavior) { + EmptyBehavior[EmptyBehavior["Undefined"] = 0] = "Undefined"; + EmptyBehavior[EmptyBehavior["Empty"] = 1] = "Empty"; + })(EmptyBehavior = VariableLengthArray.EmptyBehavior || (VariableLengthArray.EmptyBehavior = {})); + function getLengthBackingField(object, field) { + return getBackingField(object, field.options.lengthField); + } + VariableLengthArray.getLengthBackingField = getLengthBackingField; + function setLengthBackingField(object, field, value) { + setBackingField(object, field.options.lengthField, value); + } + VariableLengthArray.setLengthBackingField = setLengthBackingField; + function initialize(object, field, value, context) { + Array.initialize(object, field, value); + const descriptor = Object.getOwnPropertyDescriptor(object, field.name); + delete object[field.name]; + switch (field.subType) { + case Array.SubType.ArrayBuffer: + Object.defineProperty(object, field.name, { + ...descriptor, + set(buffer) { + var _a; + descriptor.set.call(object, buffer); + setLengthBackingField(object, field, (_a = buffer === null || buffer === void 0 ? void 0 : buffer.byteLength) !== null && _a !== void 0 ? _a : 0); + }, + }); + delete object[field.options.lengthField]; + Object.defineProperty(object, field.options.lengthField, { + configurable: true, + enumerable: true, + get() { + return getLengthBackingField(object, field); + } + }); + break; + case Array.SubType.String: + Object.defineProperty(object, field.name, { + ...descriptor, + set(string) { + descriptor.set.call(object, string); + if (string) { + setLengthBackingField(object, field, undefined); + } + else { + setLengthBackingField(object, field, 0); + } + }, + }); + delete object[field.options.lengthField]; + Object.defineProperty(object, field.options.lengthField, { + configurable: true, + enumerable: true, + get() { + let value = getLengthBackingField(object, field); + if (value === undefined) { + const backingField = getBackingField(object, field.name); + const buffer = context.encodeUtf8(backingField.string); + backingField.buffer = buffer; + value = buffer.byteLength; + setLengthBackingField(object, field, value); + } + return value; + } + }); + break; + default: + throw new Error('Unknown type'); + } + setBackingField(object, field.name, value); + if (value.buffer) { + setLengthBackingField(object, field, value.buffer.byteLength); + } + } + VariableLengthArray.initialize = initialize; +})(VariableLengthArray || (VariableLengthArray = {})); +registerFieldTypeDefinition(placeholder(), placeholder(), { + type: FieldType.VariableLengthArray, + async deserialize({ context, field, object }) { + let length = object[field.options.lengthField]; + if (typeof length === 'string') { + length = Number.parseInt(length, 10); + } + if (length === 0) { + if (field.options.emptyBehavior === VariableLengthArray.EmptyBehavior.Empty) { + switch (field.subType) { + case Array.SubType.ArrayBuffer: + return { value: new ArrayBuffer(0) }; + case Array.SubType.String: + return { value: '', extra: new ArrayBuffer(0) }; + default: + throw new Error('Unknown type'); + } + } + else { + return { value: undefined }; + } + } + const buffer = await context.read(length); + switch (field.subType) { + case Array.SubType.ArrayBuffer: + return { value: buffer }; + case Array.SubType.String: + return { + value: context.decodeUtf8(buffer), + extra: buffer + }; + default: + throw new Error('Unknown type'); + } + }, + getSize() { return 0; }, + getDynamicSize({ field, object }) { + return object[field.options.lengthField]; + }, + initialize({ context, extra, field, object, value }) { + const backingField = {}; + if (typeof value === 'string') { + backingField.string = value; + if (extra) { + backingField.buffer = extra; + } + } + else { + backingField.buffer = value; + } + Array.initialize(object, field, backingField); + VariableLengthArray.initialize(object, field, backingField, context); + }, + serialize({ dataView, field, object, offset }) { + const backingField = getBackingField(object, field.name); + new Uint8Array(dataView.buffer).set(new Uint8Array(backingField.buffer), offset); + }, +}); + +const StructDefaultOptions = { + littleEndian: false, +}; + +class Struct { + constructor(options = StructDefaultOptions) { + this._size = 0; + this.fields = []; + this._extra = {}; + this.array = (name, type, options) => { + if ('length' in options) { + return this.field({ + type: FieldType.FixedLengthArray, + name, + subType: type, + options: options, + }); + } + else { + return this.field({ + type: FieldType.VariableLengthArray, + name, + subType: type, + options: options, + }); + } + }; + this.arrayBuffer = (name, options) => { + return this.array(name, Array.SubType.ArrayBuffer, options); + }; + this.string = (name, options) => { + return this.array(name, Array.SubType.String, options); + }; + this.options = { ...StructDefaultOptions, ...options }; + } + get size() { return this._size; } + clone() { + const result = new Struct(this.options); + result.fields = this.fields.slice(); + result._size = this._size; + result._extra = this._extra; + result._afterParsed = this._afterParsed; + return result; + } + field(field) { + const result = this.clone(); + result.fields.push(field); + const definition = getFieldTypeDefinition(field.type); + const size = definition.getSize({ field, options: this.options }); + result._size += size; + return result; + } + number(name, type, options = {}, _typescriptType) { + return this.field({ + type: FieldType.Number, + name, + subType: type, + options, + }); + } + uint8(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint8, options, _typescriptType); + } + uint16(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint16, options, _typescriptType); + } + int32(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Int32, options, _typescriptType); + } + uint32(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint32, options, _typescriptType); + } + uint64(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint64, options, _typescriptType); + } + int64(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Int64, options, _typescriptType); + } + extra(value) { + const result = this.clone(); + result._extra = { ...result._extra, ...Object.getOwnPropertyDescriptors(value) }; + return result; + } + afterParsed(callback) { + const result = this.clone(); + result._afterParsed = callback; + return result; + } + initializeField(context, field, fieldTypeDefinition, object, value, extra) { + if (fieldTypeDefinition.initialize) { + fieldTypeDefinition.initialize({ + context, + extra, + field, + object, + options: this.options, + value, + }); + } + else { + setBackingField(object, field.name, value); + defineSimpleAccessors(object, field.name); + } + } + create(init, context) { + const object = { + [BackingField]: {}, + }; + Object.defineProperties(object, this._extra); + for (const field of this.fields) { + const fieldTypeDefinition = getFieldTypeDefinition(field.type); + this.initializeField(context, field, fieldTypeDefinition, object, init[field.name]); + } + return object; + } + async deserialize(context) { + const object = { + [BackingField]: {}, + }; + Object.defineProperties(object, this._extra); + for (const field of this.fields) { + const fieldTypeDefinition = getFieldTypeDefinition(field.type); + const { value, extra } = await fieldTypeDefinition.deserialize({ + context, + field, + object, + options: this.options, + }); + this.initializeField(context, field, fieldTypeDefinition, object, value, extra); + } + if (this._afterParsed) { + const result = this._afterParsed.call(object, object); + if (result) { + return result; + } + } + return object; + } + serialize(init, context) { + const object = this.create(init, context); + let size = this._size; + let fieldSize = []; + for (let i = 0; i < this.fields.length; i += 1) { + const field = this.fields[i]; + const type = getFieldTypeDefinition(field.type); + if (type.getDynamicSize) { + fieldSize[i] = type.getDynamicSize({ + context, + field, + object, + options: this.options, + }); + size += fieldSize[i]; + } + else { + fieldSize[i] = type.getSize({ field, options: this.options }); + } + } + const buffer = new ArrayBuffer(size); + const dataView = new DataView(buffer); + let offset = 0; + for (let i = 0; i < this.fields.length; i += 1) { + const field = this.fields[i]; + const type = getFieldTypeDefinition(field.type); + type.serialize({ + context, + dataView, + field, + object, + offset, + options: this.options, + }); + offset += fieldSize[i]; + } + return buffer; + } +} + +class BufferedStream { + constructor(stream) { + this.stream = stream; + } + async read(length) { + let array; + let index; + if (this.buffer) { + const buffer = this.buffer; + if (buffer.byteLength > length) { + this.buffer = buffer.subarray(length); + return buffer.slice(0, length).buffer; + } + array = new Uint8Array(length); + array.set(buffer); + index = buffer.byteLength; + this.buffer = undefined; + } + else { + const buffer = await this.stream.read(length); + if (buffer.byteLength === length) { + return buffer; + } + if (buffer.byteLength > length) { + this.buffer = new Uint8Array(buffer, length); + return buffer.slice(0, length); + } + array = new Uint8Array(length); + array.set(new Uint8Array(buffer), 0); + index = buffer.byteLength; + } + while (index < length) { + const left = length - index; + const buffer = await this.stream.read(left); + if (buffer.byteLength > left) { + array.set(new Uint8Array(buffer, 0, left), index); + this.buffer = new Uint8Array(buffer, left); + return array.buffer; + } + array.set(new Uint8Array(buffer), index); + index += buffer.byteLength; + } + return array.buffer; + } + close() { + var _a, _b; + (_b = (_a = this.stream).close) === null || _b === void 0 ? void 0 : _b.call(_a); + } +} + +const AdbPacketWithoutPayload = new Struct({ littleEndian: true }) + .uint32('command', undefined) + .uint32('arg0') + .uint32('arg1') + .uint32('payloadLength') + .uint32('checksum') + .int32('magic'); +const AdbPacketStruct = AdbPacketWithoutPayload + .arrayBuffer('payload', { lengthField: 'payloadLength' }) + .afterParsed((value) => { + if (value[BackingField].magic !== value.magic) { + throw new Error('Invalid command'); + } +}); +let AdbPacket; +(function (AdbPacket) { + function create(init, calculateChecksum, backend) { + let checksum; + if (calculateChecksum && init.payload) { + const array = new Uint8Array(init.payload); + checksum = array.reduce((result, item) => result + item, 0); + } + else { + checksum = 0; + } + return AdbPacketStruct.create({ + ...init, + checksum, + magic: init.command ^ 0xFFFFFFFF, + }, backend); + } + AdbPacket.create = create; + async function read(backend) { + let buffer = await backend.read(24); + if (buffer.byteLength !== 24) { + // Maybe it's a payload from last connection. + // Ignore and try again + buffer = await backend.read(24); + } + let bufferUsed = false; + const stream = new BufferedStream({ + read(length) { + if (!bufferUsed) { + bufferUsed = true; + return buffer; + } + return backend.read(length); + } + }); + return AdbPacketStruct.deserialize({ + read: stream.read.bind(stream), + decodeUtf8: backend.decodeUtf8.bind(backend), + encodeUtf8: backend.encodeUtf8.bind(backend), + }); + } + AdbPacket.read = read; + async function write(packet, backend) { + // Write payload separately to avoid an extra copy + await backend.write(AdbPacketWithoutPayload.serialize(packet, backend)); + if (packet.payload) { + await backend.write(packet.payload); + } + } + AdbPacket.write = write; +})(AdbPacket || (AdbPacket = {})); + +const WebUsbDeviceFilter = { + classCode: 0xFF, + subclassCode: 0x42, + protocolCode: 1, +}; + +const Utf8Encoder = new TextEncoder(); +const Utf8Decoder = new TextDecoder(); +function encodeUtf8(input) { + return Utf8Encoder.encode(input); +} +function decodeUtf8(buffer) { + return Utf8Decoder.decode(buffer); +} + +//////////////////////////////////////////////////////// +// Dedicated adb and transport function for sideload. / +//////////////////////////////////////////////////////// + +class AdbWebBackendSideload { + constructor(device) { + this._device = device; + } + + async connect() { + var _a; + if (!this._device.opened) { + await this._device.open(); + } + + for (const configuration of this._device.configurations) { + for (const interface_ of configuration.interfaces) { + for (const alternate of interface_.alternates) { + if (alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && + alternate.interfaceClass === WebUsbDeviceFilter.classCode && + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode) { + if (((_a = this._device.configuration) === null || _a === void 0 ? void 0 : _a.configurationValue) !== configuration.configurationValue) { + await this._device.selectConfiguration(configuration.configurationValue); + } + if (!interface_.claimed) { + await this._device.claimInterface(interface_.interfaceNumber); + } + if (interface_.alternate.alternateSetting !== alternate.alternateSetting) { + await this._device.selectAlternateInterface(interface_.interfaceNumber, alternate.alternateSetting); + } + for (const endpoint of alternate.endpoints) { + switch (endpoint.direction) { + case 'in': + this._inEndpointNumber = endpoint.endpointNumber; + if (this._outEndpointNumber !== undefined) { + return; + } + break; + case 'out': + this._outEndpointNumber = endpoint.endpointNumber; + if (this._inEndpointNumber !== undefined) { + return; + } + break; + } + } + } + } + } + } + throw new Error('Unknown error'); + } + + async write(buffer) { + await this._device.transferOut(this._outEndpointNumber, buffer); + } + async read(length) { + const result = await this._device.transferIn(this._inEndpointNumber, length); + if (result.status === 'stall') { + await this._device.clearHalt('in', this._inEndpointNumber); + } + const { buffer } = result.data; + return buffer; + } + + encodeUtf8(input) { + return encodeUtf8(input); + } + decodeUtf8(buffer) { + return decodeUtf8(buffer); + } + +} + +export class AdbSideload { + // This one is dedicated for adb sidelaod + constructor(backend) { + this._connected = false; + this.backend = new AdbWebBackendSideload(backend); + this.sendLock = new AutoResetEvent(); + this.initializers = new AsyncOperationManager(1); + } + get connected() { return this._connected; } + async connect() { + var _a, _b; + await ((_b = (_a = this.backend).connect) === null || _b === void 0 ? void 0 : _b.call(_a)); + this.calculateChecksum = true; + this.appendNullToServiceString = true; + const version = 0x01000001; + const maxPayloadSize = 0x100000; + + await this.sendPacket(AdbCommand.Connect, version, maxPayloadSize, "host::\0"); + const r = await AdbPacket.read(this.backend); + if (r.command == AdbCommand.Connect) { + //All is fine + } else { + throw new Error('Adb sideload connection error'); + } + } + + spawn(command, ...args) { + return this.createStream(`shell:${command} ${args.join(' ')}`); + } + exec(command, ...args) { + return this.createStreamAndReadAll(`shell:${command} ${args.join(' ')}`); + } + async getProp(key) { + const output = await this.exec('getprop', key); + return output.trim(); + } + + async createStream(service) { + const localId = 1; + service+='\0'; + let remoteId; + await this.sendPacket(AdbCommand.Open, localId, 0, service); + const r = await AdbPacket.read(this.backend); + if (r.command == AdbCommand.Okay) { + remoteId = r.arg0; + return { localId: localId, remoteId:remoteId }; + } else { + throw new Error('Adb sideload create stream error'); + } + } + + async dispose() { + this.packetDispatcher.dispose(); + await this.backend.dispose(); + } + async sendPacket(packetOrCommand, arg0, arg1, payload) { + var _a; + let init; + if (arguments.length === 1) { + init = packetOrCommand; + } + else { + init = { + command: packetOrCommand, + arg0: arg0, + arg1: arg1, + payload: typeof payload === 'string' ? this.backend.encodeUtf8(payload) : payload, + }; + } + if (init.payload && + init.payload.byteLength > this.maxPayloadSize) { + throw new Error('payload too large'); + } + try { + this.sendLock.wait() + const packet = AdbPacket.create(init, this.calculateChecksum, this.backend); + await AdbPacket.write(packet, this.backend); + } + catch(e){ + console.log("error send sendPacket ", e); + } + finally { + this.sendLock.notifyOne(); + } + } +} + +//////////////////////////////// + + diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index f9d5b6e..123a48a 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -1,11 +1,14 @@ import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -import { AdbDaemonWebUsbDeviceManager, AdbDaemonWebUsbDevice } from "@yume-chan/adb-daemon-webusb"; +import { AdbDaemonWebUsbDeviceManager } from "@yume-chan/adb-daemon-webusb"; import { Adb, AdbDaemonTransport } from "@yume-chan/adb"; import AdbWebCredentialStore from "@yume-chan/adb-credential-web"; export class ADB extends Device { + + static Manager = AdbDaemonWebUsbDeviceManager.BROWSER; + constructor(device) { super(device); this.webusb = null; @@ -29,15 +32,23 @@ export class ADB extends Device { async connect() { try { console.log("debug adb connect"); + + const adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ + if (typeof adbDaemonWebUsbDevice == "undefined") { + throw new Error("No device connected (1)"); + } - const Manager = AdbDaemonWebUsbDeviceManager.BROWSER; - const devices = await Manager.getDevices(); - if (!devices.length) { - throw new Error("No device connected"); + let connection; + try { + connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + } catch ( err ) { + const devices = await Manager.getDevices(); + if (!devices.length) { + throw new Error("No device connected (2)"); + } + adbDaemonWebUsbDevice = devices[0]; /*AdbDaemonWebUsbDevice*/ } - - const adbDaemonWebUsbDevice = devices[0]; /*AdbDaemonWebUsbDevice*/ - const connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + const credentialStore = new AdbWebCredentialStore(); const transport = await AdbDaemonTransport.authenticate({ serial: connection.deserial, @@ -46,7 +57,7 @@ export class ADB extends Device { }); const adb = new Adb(transport); - const version = await adb.getProp("ro.build.version.release"); + //const version = await adb.getProp("ro.build.version.release"); this.device = adbDaemonWebUsbDevice; this.webusb = adb; /*Adb*/ @@ -57,8 +68,7 @@ export class ADB extends Device { WDebug.log("Name", adbDaemonWebUsbDevice.name); WDebug.log(">Device (codename)", adb.transport.banner.device); // codemane WDebug.log("----------------------------------"); - - + } catch (e) { this.device = null; throw new Error(`Cannot connect ADB ${e.message || e}`); @@ -87,3 +97,4 @@ export class ADB extends Device { return res; } } + diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index 6585974..d466922 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -1,10 +1,8 @@ -import { MessageClass } from "../../lib/webadb/message.class.js"; -import { MessageHeader } from "../../lib/webadb/message-header.class.js"; +import { ADB } from "./adb.class.js"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -import { AdbDaemonWebUsbDeviceManager } from "@yume-chan/adb-daemon-webusb"; -import { Adb, AdbDaemonTransport } from "@yume-chan/adb"; -import AdbWebCredentialStore from "@yume-chan/adb-credential-web"; + +import { AdbSideload, MessageClass, MessageHeader } from "../adb-sideload.js" export class Recovery extends Device { constructor(device) { @@ -34,22 +32,13 @@ export class Recovery extends Device { if (this.device && this.device.isConnected) { WDebug.log("Connect recovery the device is connected"); } else { + const adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ + const adbDevice = new AdbSideload(adbDaemonWebUsbDevice.raw, null); + WDebug.log("adbDevice = ", adbDevice); + await adbDevice.connect(); - const Manager = AdbDaemonWebUsbDeviceManager.BROWSER; - - const adbDaemonWebUsbDevice = await Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ - const connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ - const credentialStore = new AdbWebCredentialStore(); - const transport = await AdbDaemonTransport.authenticate({ - serial: connection.deserial, - connection, - credentialStore: credentialStore - }); - const adb = new Adb(transport); this.device = adbDaemonWebUsbDevice; - this.webusb = adb; /*Adb*/ - - res = true; + this.webusb = adbDevice; } } catch (e) { this.device = null; @@ -59,7 +48,7 @@ export class Recovery extends Device { async sideload(blob) { try { - await this.adbOpen(blob, true); + await this.adbOpen(blob); } catch (e) { throw new Error(`Sideload fails ${e.message || e}`); } @@ -77,9 +66,7 @@ export class Recovery extends Device { return this.webusb.product; } - async adbOpen(blob, useChecksum) { - // This one is used by the adb.bundle.js - + async adbOpen(blob) { const encoder = new TextEncoder(); const decoder = new TextDecoder(); @@ -90,22 +77,21 @@ export class Recovery extends Device { let data = new DataView(encoder.encode("" + service + "\0").buffer); this.stream = await this.webusb.createStream(service); // Send Open message and receive OKAY. - let checksum = 0; - if (useChecksum) { - checksum = MessageClass.checksum(data); - } + let checksum = MessageClass.checksum(data); + const localId = this.stream.localId; const remoteId = this.stream.remoteId; + let header = new MessageHeader("OKAY", localId, remoteId, 0, checksum); let receivedData, message; - await this.adbWebBackend.write(header.toDataView().buffer); - let r = await this.adbWebBackend.read(24); + await this.webusb.backend.write(header.toDataView().buffer); + let r = await this.webusb.backend.read(24); let v = Array.from(new Uint8Array(r)) .map((byte) => String.fromCharCode(byte)) .join(""); if (v[0] == "W" && v[1] == "R" && v[2] == "T" && v[3] == "E") { - receivedData = await this.adbWebBackend.read(8); + receivedData = await this.webusb.backend.read(8); message = new MessageClass(header, receivedData); } else { throw new Error("Write OKAY Failed (init)"); @@ -137,23 +123,23 @@ export class Recovery extends Device { header = new MessageHeader("WRTE", localId, remoteId, to_write, checksum); let buff = await slice.arrayBuffer(); - await this.adbWebBackend.write(header.toDataView().buffer); - await this.adbWebBackend.write(buff); - r = await this.adbWebBackend.read(24); + await this.webusb.backend.write(header.toDataView().buffer); + await this.webusb.backend.write(buff); + r = await this.webusb.backend.read(24); v = Array.from(new Uint8Array(r)) .map((byte) => String.fromCharCode(byte)) .join(""); //test OKAY if (v[0] == "O" && v[1] == "K" && v[2] == "A" && v[3] == "Y") { header = new MessageHeader("OKAY", localId, remoteId, 0, checksum); - await this.adbWebBackend.write(header.toDataView().buffer); - r = await this.adbWebBackend.read(24); + await this.webusb.backend.write(header.toDataView().buffer); + r = await this.webusb.backend.read(24); v = Array.from(new Uint8Array(r)) .map((byte) => String.fromCharCode(byte)) .join(""); //Test WRTE if (v[0] == "W" && v[1] == "R" && v[2] == "T" && v[3] == "E") { - receivedData = await this.adbWebBackend.read(8); + receivedData = await this.webusb.backend.read(8); message = new MessageClass(header, receivedData); } else { throw new Error(`WRTE Failed ${block}`); diff --git a/app/src/controller/downloader.manager.js b/app/src/controller/downloader.manager.js index 8940e77..d9363dc 100644 --- a/app/src/controller/downloader.manager.js +++ b/app/src/controller/downloader.manager.js @@ -20,7 +20,7 @@ export class Downloader { if (this.db) return; // Already initialized this.db = await this.openDBStore(); - await this.clearDBStore(); + //await this.clearDBStore(); this.quota = await navigator.storage.estimate(); } @@ -32,6 +32,7 @@ export class Downloader { onDownloadProgress, onUnzipProgress, ) { + return; let current_file; try { for (let i = 0; i < folder.length; i++) { @@ -111,9 +112,9 @@ export class Downloader { */ async getFile(name) { const file = this.stored[name]; - if (!file) { + /*if (!file) { throw new Error(`File ${name} was not previously downloaded`); - } + }*/ return await this.getFromDBStore(name); } diff --git a/app/src/lib/webadb/helper.class.js b/app/src/lib/webadb/helper.class.js deleted file mode 100644 index 69d50c5..0000000 --- a/app/src/lib/webadb/helper.class.js +++ /dev/null @@ -1,56 +0,0 @@ -export class Helper { - static paddit(text, width, padding) { - const padlen = width - text.length; - let padded = ''; - for (let i = 0; i < padlen; i++) { - padded += padding; - } - return padded + text; - } - - static toHex8(num) { - return this.paddit(num.toString(16), 2, '0'); - } - - static toHex16(num) { - return this.paddit(num.toString(16), 4, '0'); - } - - static toHex32(num) { - return this.paddit(num.toString(16), 8, '0'); - } - - static hexdump(view, prefix = '') { - const decoder = new TextDecoder(); - let global = ''; - for (let i = 0; i < view.byteLength; i += 16) { - const max = (view.byteLength - i) > 16 ? 16 : (view.byteLength - i); - let row = prefix + this.toHex16(i) + ' '; - let j; - for (j = 0; j < max; j++) { - row += ' ' + this.toHex8(view.getUint8(i + j)); - } - for (; j < 16; j++) { - row += ' '; - } - row += ' | ' + decoder.decode(new DataView(view.buffer, i, max)); - global += row; - } - return global; - } - - static encodeCmd(cmd) { - const encoder = new TextEncoder(); - const buffer = encoder.encode(cmd).buffer; - const view = new DataView(buffer); - return view.getUint32(0, true); - } - - static decodeCmd(cmd) { - const decoder = new TextDecoder(); - const buffer = new ArrayBuffer(4); - const view = new DataView(buffer); - view.setUint32(0, cmd, true); - return decoder.decode(buffer); - } -} diff --git a/app/src/lib/webadb/message-header.class.js b/app/src/lib/webadb/message-header.class.js deleted file mode 100644 index afe0cbc..0000000 --- a/app/src/lib/webadb/message-header.class.js +++ /dev/null @@ -1,61 +0,0 @@ -import {Helper} from "./helper.class.js"; - -export class MessageHeader { - /** - * Creates a new MessageHeader - * - * @param {string} cmd The command that this message represents. - * @param {number} arg0 The meaning depends on the command. - * @param {number} arg1 The meaning depends on the command. - * @param {number} length The length of the data part of the message. - * @param {number} checksum Checksum for the data part of the message. Only used in version 0x01000000 of the - * protocol. - * - * - */ - constructor(cmd, arg0, arg1, length, checksum) { - this.cmd = cmd; - this.arg0 = arg0; - this.arg1 = arg1; - this.length = length; - this.checksum = checksum; - } - - /** - * Converts the MessageHeader into a {@link DataView}. - * @returns {DataView} a DataView with 24 bytes, with the header content. - */ - toDataView() { - const view = new DataView(new ArrayBuffer(24)); - const rawCmd = Helper.encodeCmd(this.cmd); - const magic = rawCmd ^ 0xffffffff; - view.setUint32(0, rawCmd, true); - view.setUint32(4, this.arg0, true); - view.setUint32(8, this.arg1, true); - view.setUint32(12, this.length, true); - view.setUint32(16, this.checksum, true); - view.setUint32(20, magic, true); - return view; - } - - /** - * Creates a header from a {@link DataView}. - * @param {DataView} data the {@link DataView} that will be used to create the header. - * @param {boolean} useChecksum if the checksum should be verified. - */ - static parse(data, useChecksum = false) { - const cmd = data.getUint32(0, true); - const arg0 = data.getUint32(4, true); - const arg1 = data.getUint32(8, true); - const len = data.getUint32(12, true); - const checksum = data.getUint32(16, true); - // Android seems to have stopped providing checksums - if (useChecksum && data.byteLength > 20) { - const magic = data.getUint32(20, true); - if ((cmd ^ magic) !== -1) { - throw new Error('magic mismatch'); - } - } - return new MessageHeader(Helper.decodeCmd(cmd), arg0, arg1, len, checksum); - } -} \ No newline at end of file diff --git a/app/src/lib/webadb/message.class.js b/app/src/lib/webadb/message.class.js deleted file mode 100644 index 76f58bf..0000000 --- a/app/src/lib/webadb/message.class.js +++ /dev/null @@ -1,149 +0,0 @@ -import {MessageHeader} from "./message-header.class.js"; - -export class MessageClass { - constructor(header, data) { - this.header = header; - this.data = data; - } - - /** - * Returns the data content as a {@link string} or {@link null} if data is not available. - * @returns {string | null} a {@link string} or {@link null} if data is not available. - */ - dataAsString() { - if (!this.data) { - return null; - } - const textDecoder = new TextDecoder(); - return textDecoder.decode(this.data); - } - - /** - * Creates a new Message. See {@link MessageHeader}. - * @param {string} cmd the command. - * @param {number} arg0 value for the first argument. - * @param {number} arg1 value for the second argument. - * @param {boolean} useChecksum if the checksum for the data should be calculated. - * @param {DataView} data message data. - * @returns {MessageClass} a new Message - */ - static newMessage(cmd, arg0, arg1, useChecksum, data) { - let checksum = 0; - let byteLength = 0; - if (data) { - byteLength = data.byteLength; - if (useChecksum) { - checksum = MessageClass.checksum(data); - } - } - const header = new MessageHeader.MessageHeader(cmd, arg0, arg1, byteLength, checksum); - return new MessageClass(header, data); - } - - /** - * Creates a new `OPEN` message. - * @param {number} localId local stream ID - * @param {number} remoteId remote stream ID. - * @param {string} service service description - * @param {boolean} useChecksum if the checksum for the data should be calculated. - * @returns {MessageClass} a correctly setup message with an 'OPEN' command - */ - static open(localId, remoteId, service, useChecksum) { - const encoder = new TextEncoder(); - const data = new DataView(encoder.encode('' + service + '\0').buffer); - return MessageClass.newMessage('OPEN', localId, remoteId, useChecksum, data); - } - - /** - * Creates a new `CNXN` message. - * @param {number} version version of the protocol to be used. - * @param {number} maxPayload maximum payload size for the connection. - * @param {string} banner host description. - * @param {boolean} useChecksum if the checksum for the data should be calculated. - * @returns {MessageClass} a correctly setup message with an 'CNXN' command - */ - static cnxn(version, maxPayload, banner, useChecksum) { - const encoder = new TextEncoder(); - const data = new DataView(encoder.encode(banner).buffer); - return MessageClass.newMessage('CNXN', version, maxPayload, useChecksum, data); - } - - /** - * Creates a new `AUTH` message, with the a signed token. - * @param {DataView} signedToken a DataView with the signed token. - * @param {boolean} useChecksum if the checksum for the data should be calculated. - * @returns {MessageClass} a correctly setup message with an 'AUTH' command - */ - static authSignature(signedToken, useChecksum) { - return MessageClass.newMessage('AUTH', 2, 0, useChecksum, signedToken); - } - - /** - * Creates a new `AUTH` message, with the a Public Key. - * @param {DataView} publicKey a DataView with the public key - * @param {boolean} useChecksum if the checksum for the data should be calculated. - * @returns {MessageClass} a correctly setup message with an 'AUTH' command - */ - static authPublicKey(publicKey, useChecksum) { - const textEncoder = new TextEncoder(); - const data = textEncoder.encode(Helper.toB64(publicKey.buffer) + '\0'); - return MessageClass.newMessage('AUTH', 3, 0, useChecksum, new DataView(data.buffer)); - } - - /** - * Creates a new `SYNC` message, with the a Public Key. - * @param - * @returns {MessageClass} - * - [4 octets] : "SYNC" en ASCII - [4 octets] : longueur du nom de service (4 en ASCII, soit 0x34 en hexadécimal) - [4 octets] : longueur des données (0) - [16 octets] : nom de service ("sideload") - */ - static sync(publicKey, useChecksum) { - const textEncoder = new TextEncoder(); - const data = textEncoder.encode(Helper.toB64(publicKey.buffer) + '\0'); - return MessageClass.newMessage('SYNC', 3, 0, useChecksum, new DataView(data.buffer)); - } - - /** - * Creates a new `SEND` message, with the a Public Key. - * @param - * @returns {MessageClass} - [4 octets] : "SEND" en ASCII - [4 octets] : longueur du nom de service (4 en ASCII, soit 0x34 en hexadécimal) - [4 octets] : longueur des données (maximal selon la réponse à la requête CNXN) - [16 octets] : nom de service ("sideload") - [data] : données du fichier de mise à jour, avec une longueur égale à la longueur des données envoyées dans l'en-tête du message - */ - static send(publicKey, useChecksum) { - const textEncoder = new TextEncoder(); - const data = textEncoder.encode(Helper.toB64(publicKey.buffer) + '\0'); - return MessageClass.newMessage('SEND', 3, 0, useChecksum, new DataView(data.buffer)); - } - - /** - * Creates a new `DONE` message, with the a Public Key. - * @param - * @returns {MessageClass} - [4 octets] : "DONE" en ASCII - [4 octets] : longueur du nom de service (4 en ASCII, soit 0x34 en hexadécimal) - [4 octets] : longueur des données (0) - [16 octets] : nom de service ("sideload") - - Ces messages doivent être envoyés dans l'ordre ci-dessus et avec les valeurs appropriées pour les champs d'en-tête, tels que la longueur maximale des paquets et le numéro de série généré par le client ADB. - */ - static done(publicKey, useChecksum) { - const textEncoder = new TextEncoder(); - const data = textEncoder.encode(Helper.toB64(publicKey.buffer) + '\0'); - return MessageClass.newMessage('DONE', 3, 0, useChecksum, new DataView(data.buffer)); - } - - static checksum(dataView) { - let sum = 0; - for (let i = 0; i < dataView.byteLength; i++) { - sum += dataView.getUint8(i); - } - return sum & 0xffffffff; - } -} \ No newline at end of file -- GitLab From e37eb5222a152dc50ef57999e211ebf925793df0 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 31 Jan 2025 18:24:58 +0100 Subject: [PATCH 11/19] Remove debug --- app/src/controller/downloader.manager.js | 527 +++++++++++------------ 1 file changed, 263 insertions(+), 264 deletions(-) diff --git a/app/src/controller/downloader.manager.js b/app/src/controller/downloader.manager.js index d9363dc..b2d38d0 100644 --- a/app/src/controller/downloader.manager.js +++ b/app/src/controller/downloader.manager.js @@ -1,264 +1,263 @@ -const DB_NAME = "MurenaBlobStore"; -const DB_VERSION = 1; - -import ky from "ky"; -import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"; - -/** - * Download Manager - * Download files from the device folder of [modelname].json - * Download with DBStore and unzip if necessary - * Blobs are in this.files[filename] - */ -export class Downloader { - constructor() { - this.db = null; - this.stored = {}; - } - - async init() { - if (this.db) return; // Already initialized - - this.db = await this.openDBStore(); - //await this.clearDBStore(); - this.quota = await navigator.storage.estimate(); - } - - /* - * */ - async downloadAndUnzipFolder( - filesRequired, - folder, - onDownloadProgress, - onUnzipProgress, - ) { - return; - let current_file; - try { - for (let i = 0; i < folder.length; i++) { - const file = folder[i]; - current_file = file.path; - if (filesRequired.includes(file.name) || file.unzip) { - const blob = await this.download(file.path, (value, total) => { - onDownloadProgress(value, total, file.name); - }); - if (file.unzip) { - const zipReader = new ZipReader(new BlobReader(blob)); - const filesEntries = await zipReader.getEntries(); - for (let i = 0; i < filesEntries.length; i++) { - const unzippedEntry = await this.getFileFromZip( - filesEntries[i], - (value, total) => { - onUnzipProgress(value, total, filesEntries[i].filename); - }, - ); - let filename = this.getMappedName( - filesEntries[i].filename, - file.mapping, - ); - if (filesRequired.includes(filename)) { - await this.setInDBStore(unzippedEntry.blob, filename); - this.stored[filename] = true; - } - } - await zipReader.close(); - } else { - await this.setInDBStore(blob, file.name); - this.stored[file.name] = true; - } - } - } - } catch (e) { - throw new Error( - `downloadAndUnzipFolder Error
current_file ${current_file}
${e.message || e}`, - ); - } - } - - async getFileFromZip(file, onProgress) { - const name = file.filename; - const blob = await file.getData(new BlobWriter(), { - onprogress: (value, total) => { - onProgress(value, total, name); - }, - useWebWorkers: false, - }); - return { - name, - blob, - }; - } - - getMappedName(filename, map) { - if (!map) { - return filename; - } - - console.log(map); - for (const [regex, newFilename] of Object.entries(map)) { - let re = new RegExp(regex); - if (filename.match(re)) { - return newFilename; - } - } - return filename; - } - - /** - * @param name - * @returns {} - * It does not launch download (downloads are launched with downloadFolder) - * this function retrieve the promise linked to the fileName - */ - async getFile(name) { - const file = this.stored[name]; - /*if (!file) { - throw new Error(`File ${name} was not previously downloaded`); - }*/ - return await this.getFromDBStore(name); - } - - /* - * getData from a zip file - * */ - async getData(dbFile, fileEntry, onProgress) { - const _zip = new BlobWriter(); - const blob = await fileEntry.getData(_zip, { - onprogress: (value, total) => { - onProgress(value, total, dbFile); - }, - onend: () => {}, - useWebWorkers: true, - }); - return blob; - } - - async download(path, onProgress) { - try { - const buffers = await this.fetch( - { - url: path, - chunkSize: 16 * 1024 * 1024, - poolLimit: 1, - }, - onProgress, - ); - - //let totalSize = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0); - const ret = new Blob(buffers); - return ret; - } catch (e) { - throw new Error(`${e.message || e}`); - } - } - - async clearDBStore() { - const store = this.db - .transaction(DB_NAME, "readwrite") - .objectStore(DB_NAME); - store.clear(); - } - - async setInDBStore(blob, key) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction(DB_NAME, "readwrite"); - const store = transaction.objectStore(DB_NAME); - const request = store.put(blob, key); - - request.onsuccess = () => { - resolve(); - }; - - request.onerror = (event) => { - reject(event.target.error); - }; - }); - } - - async getFromDBStore(key) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction(DB_NAME, "readonly"); - const store = transaction.objectStore(DB_NAME); - const request = store.get(key); - request.onsuccess = function (event) { - const result = event.target.result; - if (result) { - resolve(result); - } else { - resolve(null); - } - }; - request.onerror = function (event) { - reject(event.target.error); - }; - }); - } - - async openDBStore() { - return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); - request.onerror = reject; - request.onupgradeneeded = function (event) { - const db = event.target.result; - db.createObjectStore(DB_NAME, { autoIncrement: false }); - }; - request.onsuccess = function (event) { - resolve(event.target.result); - }; - }); - } - - concatenate(arrays) { - if (!arrays.length) return null; - let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); - let result = new Uint8Array(totalLength); - let length = 0; - for (let array of arrays) { - result.set(array, length); - length += array.length; - } - return result; - } - - async getContentLength(url) { - const response = await ky.head(url); - const contentLength = response.headers.get("content-length"); - return parseInt(contentLength, 10); - } - - async fetch({ url, chunkSize }, onProgress) { - try { - const contentLength = await this.getContentLength(url); - const totalChunks = Math.ceil(contentLength / chunkSize); - const buffers = []; - - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize - 1, contentLength - 1); - try { - const response = await ky.get(url, { - headers: { - Range: `bytes=${start}-${end}`, - }, - }); - if (!response.ok) { - throw new Error( - `Cannot download chunk (1) ${i + 1}: ${response.status} ${response.statusText}`, - ); - } - - const chunk = await response.arrayBuffer(); - buffers.push(chunk); - onProgress(start + chunk.byteLength, contentLength); - } catch (chunkError) { - throw new Error( - `Cannot download chunk (2) ${i + 1} ${chunkError.message || chunkError}`, - ); - } - } - return buffers; - } catch (error) { - throw new Error(`Download fails ${error.message || error}`); - } - } -} +const DB_NAME = "MurenaBlobStore"; +const DB_VERSION = 1; + +import ky from "ky"; +import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"; + +/** + * Download Manager + * Download files from the device folder of [modelname].json + * Download with DBStore and unzip if necessary + * Blobs are in this.files[filename] + */ +export class Downloader { + constructor() { + this.db = null; + this.stored = {}; + } + + async init() { + if (this.db) return; // Already initialized + + this.db = await this.openDBStore(); + await this.clearDBStore(); + this.quota = await navigator.storage.estimate(); + } + + /* + * */ + async downloadAndUnzipFolder( + filesRequired, + folder, + onDownloadProgress, + onUnzipProgress, + ) { + let current_file; + try { + for (let i = 0; i < folder.length; i++) { + const file = folder[i]; + current_file = file.path; + if (filesRequired.includes(file.name) || file.unzip) { + const blob = await this.download(file.path, (value, total) => { + onDownloadProgress(value, total, file.name); + }); + if (file.unzip) { + const zipReader = new ZipReader(new BlobReader(blob)); + const filesEntries = await zipReader.getEntries(); + for (let i = 0; i < filesEntries.length; i++) { + const unzippedEntry = await this.getFileFromZip( + filesEntries[i], + (value, total) => { + onUnzipProgress(value, total, filesEntries[i].filename); + }, + ); + let filename = this.getMappedName( + filesEntries[i].filename, + file.mapping, + ); + if (filesRequired.includes(filename)) { + await this.setInDBStore(unzippedEntry.blob, filename); + this.stored[filename] = true; + } + } + await zipReader.close(); + } else { + await this.setInDBStore(blob, file.name); + this.stored[file.name] = true; + } + } + } + } catch (e) { + throw new Error( + `downloadAndUnzipFolder Error
current_file ${current_file}
${e.message || e}`, + ); + } + } + + async getFileFromZip(file, onProgress) { + const name = file.filename; + const blob = await file.getData(new BlobWriter(), { + onprogress: (value, total) => { + onProgress(value, total, name); + }, + useWebWorkers: false, + }); + return { + name, + blob, + }; + } + + getMappedName(filename, map) { + if (!map) { + return filename; + } + + console.log(map); + for (const [regex, newFilename] of Object.entries(map)) { + let re = new RegExp(regex); + if (filename.match(re)) { + return newFilename; + } + } + return filename; + } + + /** + * @param name + * @returns {} + * It does not launch download (downloads are launched with downloadFolder) + * this function retrieve the promise linked to the fileName + */ + async getFile(name) { + const file = this.stored[name]; + if (!file) { + throw new Error(`File ${name} was not previously downloaded`); + } + return await this.getFromDBStore(name); + } + + /* + * getData from a zip file + * */ + async getData(dbFile, fileEntry, onProgress) { + const _zip = new BlobWriter(); + const blob = await fileEntry.getData(_zip, { + onprogress: (value, total) => { + onProgress(value, total, dbFile); + }, + onend: () => {}, + useWebWorkers: true, + }); + return blob; + } + + async download(path, onProgress) { + try { + const buffers = await this.fetch( + { + url: path, + chunkSize: 16 * 1024 * 1024, + poolLimit: 1, + }, + onProgress, + ); + + //let totalSize = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0); + const ret = new Blob(buffers); + return ret; + } catch (e) { + throw new Error(`${e.message || e}`); + } + } + + async clearDBStore() { + const store = this.db + .transaction(DB_NAME, "readwrite") + .objectStore(DB_NAME); + store.clear(); + } + + async setInDBStore(blob, key) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction(DB_NAME, "readwrite"); + const store = transaction.objectStore(DB_NAME); + const request = store.put(blob, key); + + request.onsuccess = () => { + resolve(); + }; + + request.onerror = (event) => { + reject(event.target.error); + }; + }); + } + + async getFromDBStore(key) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction(DB_NAME, "readonly"); + const store = transaction.objectStore(DB_NAME); + const request = store.get(key); + request.onsuccess = function (event) { + const result = event.target.result; + if (result) { + resolve(result); + } else { + resolve(null); + } + }; + request.onerror = function (event) { + reject(event.target.error); + }; + }); + } + + async openDBStore() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(DB_NAME, DB_VERSION); + request.onerror = reject; + request.onupgradeneeded = function (event) { + const db = event.target.result; + db.createObjectStore(DB_NAME, { autoIncrement: false }); + }; + request.onsuccess = function (event) { + resolve(event.target.result); + }; + }); + } + + concatenate(arrays) { + if (!arrays.length) return null; + let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); + let result = new Uint8Array(totalLength); + let length = 0; + for (let array of arrays) { + result.set(array, length); + length += array.length; + } + return result; + } + + async getContentLength(url) { + const response = await ky.head(url); + const contentLength = response.headers.get("content-length"); + return parseInt(contentLength, 10); + } + + async fetch({ url, chunkSize }, onProgress) { + try { + const contentLength = await this.getContentLength(url); + const totalChunks = Math.ceil(contentLength / chunkSize); + const buffers = []; + + for (let i = 0; i < totalChunks; i++) { + const start = i * chunkSize; + const end = Math.min(start + chunkSize - 1, contentLength - 1); + try { + const response = await ky.get(url, { + headers: { + Range: `bytes=${start}-${end}`, + }, + }); + if (!response.ok) { + throw new Error( + `Cannot download chunk (1) ${i + 1}: ${response.status} ${response.statusText}`, + ); + } + + const chunk = await response.arrayBuffer(); + buffers.push(chunk); + onProgress(start + chunk.byteLength, contentLength); + } catch (chunkError) { + throw new Error( + `Cannot download chunk (2) ${i + 1} ${chunkError.message || chunkError}`, + ); + } + } + return buffers; + } catch (error) { + throw new Error(`Download fails ${error.message || error}`); + } + } +} -- GitLab From ed0c10016014e56cd1b951f7916cff35b0972375 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 31 Jan 2025 18:37:55 +0100 Subject: [PATCH 12/19] Add debug to understand sideload issue --- app/src/controller/device/recovery.class.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index d466922..470fd6a 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -142,9 +142,11 @@ export class Recovery extends Device { receivedData = await this.webusb.backend.read(8); message = new MessageClass(header, receivedData); } else { + console.error("Error sideload (A)", v); throw new Error(`WRTE Failed ${block}`); } } else { + console.error("Error sideload (B)", v); throw new Error(`OKAY Failed ${block}`); } } -- GitLab From dbd27e63227e31bd950779e25064717015be38a7 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 3 Feb 2025 10:29:10 +0100 Subject: [PATCH 13/19] add @yume-chan/adb dependencies --- app/package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/package.json b/app/package.json index 6e30fa7..af77c51 100644 --- a/app/package.json +++ b/app/package.json @@ -12,6 +12,9 @@ }, "devDependencies": { "@eslint/js": "^9.17.0", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", + "@yume-chan/adb-credential-web" : "1.1.0", "eslint": "^9.17.0", "globals": "^15.14.0", "prettier": "3.4.2", @@ -20,6 +23,9 @@ }, "dependencies": { "@e/fastboot": "1.1.4", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", + "@yume-chan/adb-credential-web" : "1.1.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" } -- GitLab From cccc681031796295a5dd3847d165fc0f5dd4a133 Mon Sep 17 00:00:00 2001 From: Nicolas Gelot Date: Mon, 3 Feb 2025 10:51:12 +0100 Subject: [PATCH 14/19] Format webadb changes --- app/package-lock.json | 85 + app/package.json | 5 +- app/src/controller/adb-sideload.js | 1752 ++++++++++--------- app/src/controller/device/adb.class.js | 29 +- app/src/controller/device/recovery.class.js | 12 +- app/src/controller/downloader.manager.js | 526 +++--- 6 files changed, 1281 insertions(+), 1128 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 5e9875c..9757828 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -10,6 +10,9 @@ "license": "GPLv3", "dependencies": { "@e/fastboot": "1.1.4", + "@yume-chan/adb": "1.1.0", + "@yume-chan/adb-credential-web": "1.1.0", + "@yume-chan/adb-daemon-webusb": "1.1.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" }, @@ -334,6 +337,88 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/w3c-web-usb": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz", + "integrity": "sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ==", + "license": "MIT" + }, + "node_modules/@yume-chan/adb": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yume-chan/adb/-/adb-1.1.0.tgz", + "integrity": "sha512-AC2HhTtxvEPrAQfMP9qDC3FI5Uc6U8j4oH+WMOQ+PKqzI4eme1X3V7OXgPNkrLTQ9SUWgLRw+lgzpvyTvNYpng==", + "license": "MIT", + "dependencies": { + "@yume-chan/async": "^4.0.2", + "@yume-chan/event": "^1.0.0", + "@yume-chan/no-data-view": "^1.0.0", + "@yume-chan/stream-extra": "^1.0.0", + "@yume-chan/struct": "^1.0.0" + } + }, + "node_modules/@yume-chan/adb-credential-web": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yume-chan/adb-credential-web/-/adb-credential-web-1.1.0.tgz", + "integrity": "sha512-jdg0JTZ1Z82gPoxtc29511aPVKPQyXRx5Nf2uRy7UXRmg5oeH6dqO5a45Li1yRo1dwAxZHShxIt90RnP7zDH0g==", + "license": "MIT", + "dependencies": { + "@yume-chan/adb": "^1.1.0" + } + }, + "node_modules/@yume-chan/adb-daemon-webusb": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yume-chan/adb-daemon-webusb/-/adb-daemon-webusb-1.1.0.tgz", + "integrity": "sha512-Q0jkEX/V/PTMZov3udN0gR4uwxfJz0EmKBmRqdJl619rXGC8OfkqlnbrOI4aOjCebm2HCc6d3jVAmjo5sIB7OQ==", + "license": "MIT", + "dependencies": { + "@types/w3c-web-usb": "^1.0.10", + "@yume-chan/adb": "^1.1.0", + "@yume-chan/event": "^1.0.0", + "@yume-chan/stream-extra": "^1.0.0", + "@yume-chan/struct": "^1.0.0" + } + }, + "node_modules/@yume-chan/async": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@yume-chan/async/-/async-4.0.2.tgz", + "integrity": "sha512-YP5Hg4DZoq6CXzeTsiOu6rDNUaWw8SMiM4cB2rHam4zRTatgUHCWpSKMawQt0+nUro/+IeNTZLh2QpIFyxuGzg==", + "license": "MIT" + }, + "node_modules/@yume-chan/event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/event/-/event-1.0.0.tgz", + "integrity": "sha512-tr4V34WQ5dz2UDMQl4ekj2zGLqwzmclOJpJL+9s2LJpURHw+Szy5g4gi4j86M+5epMFD8dpT9ym/wXHiUdtpsg==", + "license": "MIT", + "dependencies": { + "@yume-chan/async": "^4.0.2" + } + }, + "node_modules/@yume-chan/no-data-view": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/no-data-view/-/no-data-view-1.0.0.tgz", + "integrity": "sha512-KrkXhJJQiCFFXb/eeHB++HCfKuwwiI7RVzHR7X/0XiwjQouxBpNpRFjEO25458Q5p/EPGprGWQ7BsHrmV3mkZQ==", + "license": "MIT" + }, + "node_modules/@yume-chan/stream-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/stream-extra/-/stream-extra-1.0.0.tgz", + "integrity": "sha512-xltJYD5txn63e0jm7bHExmULowJTgjbsC205DN0GCxfdfrZIl6adKVheQNh1yOuOKV5Ok5luWNVSBp7Y2OVffA==", + "license": "MIT", + "dependencies": { + "@yume-chan/async": "^4.0.2", + "@yume-chan/struct": "^1.0.0" + } + }, + "node_modules/@yume-chan/struct": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@yume-chan/struct/-/struct-1.0.0.tgz", + "integrity": "sha512-PQWUjgITlZstIkLD6ouRDwmR35Z9OJZ9daOQ6ZipzQ/mCnHTeoJf2v8x2+fmGyVrrHf9oaCWe8U/XW65onRlGg==", + "license": "MIT", + "dependencies": { + "@yume-chan/async": "^4.0.2", + "@yume-chan/no-data-view": "^1.0.0" + } + }, "node_modules/@zip.js/zip.js": { "version": "2.7.54", "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.54.tgz", diff --git a/app/package.json b/app/package.json index af77c51..de88fc5 100644 --- a/app/package.json +++ b/app/package.json @@ -12,9 +12,6 @@ }, "devDependencies": { "@eslint/js": "^9.17.0", - "@yume-chan/adb": "1.1.0", - "@yume-chan/adb-daemon-webusb": "1.1.0", - "@yume-chan/adb-credential-web" : "1.1.0", "eslint": "^9.17.0", "globals": "^15.14.0", "prettier": "3.4.2", @@ -25,7 +22,7 @@ "@e/fastboot": "1.1.4", "@yume-chan/adb": "1.1.0", "@yume-chan/adb-daemon-webusb": "1.1.0", - "@yume-chan/adb-credential-web" : "1.1.0", + "@yume-chan/adb-credential-web": "1.1.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" } diff --git a/app/src/controller/adb-sideload.js b/app/src/controller/adb-sideload.js index 14850bd..8af8beb 100644 --- a/app/src/controller/adb-sideload.js +++ b/app/src/controller/adb-sideload.js @@ -1,840 +1,912 @@ -import { AdbCommand, - AutoResetEvent - } from "@yume-chan/adb"; - -import { AsyncOperationManager } from "@yume-chan/async"; - - -// ADB Message structures -// There are probably equivalents in the new adb library. - -export class MessageClass { - constructor(header, data) { - this.header = header; - this.data = data; - } - - static checksum(dataView) { - let sum = 0; - for (let i = 0; i < dataView.byteLength; i++) { - sum += dataView.getUint8(i); - } - return sum & 0xffffffff; - } -} - -export class MessageHeader { - - constructor(cmd, arg0, arg1, length, checksum) { - this.cmd = cmd; - this.arg0 = arg0; - this.arg1 = arg1; - this.length = length; - this.checksum = checksum; - } - - toDataView() { - const view = new DataView(new ArrayBuffer(24)); - const rawCmd = this.encodeCmd(this.cmd); - const magic = rawCmd ^ 0xffffffff; - view.setUint32(0, rawCmd, true); - view.setUint32(4, this.arg0, true); - view.setUint32(8, this.arg1, true); - view.setUint32(12, this.length, true); - view.setUint32(16, this.checksum, true); - view.setUint32(20, magic, true); - return view; - } - - encodeCmd(cmd) { - const encoder = new TextEncoder(); - const buffer = encoder.encode(cmd).buffer; - const view = new DataView(buffer); - return view.getUint32(0, true); - } -} - -/* -Classes and utilities from an old adb lib. -There are probably equivalents in the new adb library. -It would be interesting to find them, but it may be rather difficult (name change and perhaps synthetics). -*/ - -const BackingField = Symbol('BackingField'); -function getBackingField(object, field) { - return object[BackingField][field]; -} -function setBackingField(object, field, value) { - object[BackingField][field] = value; -} -function defineSimpleAccessors(object, field) { - Object.defineProperty(object, field, { - configurable: true, - enumerable: true, - get() { return getBackingField(object, field); }, - set(value) { setBackingField(object, field, value); }, - }); -} - -var Array; -(function (Array) { - let SubType; - (function (SubType) { - SubType[SubType["ArrayBuffer"] = 0] = "ArrayBuffer"; - SubType[SubType["String"] = 1] = "String"; - })(SubType = Array.SubType || (Array.SubType = {})); - function initialize(object, field, value) { - switch (field.subType) { - case SubType.ArrayBuffer: - Object.defineProperty(object, field.name, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field.name).buffer; - }, - set(buffer) { - setBackingField(object, field.name, { buffer }); - }, - }); - break; - case SubType.String: - Object.defineProperty(object, field.name, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field.name).string; - }, - set(string) { - setBackingField(object, field.name, { string }); - }, - }); - break; - default: - throw new Error('Unknown type'); - } - setBackingField(object, field.name, value); - } - Array.initialize = initialize; -})(Array || (Array = {})); - -const registry = {}; -function getFieldTypeDefinition(type) { - return registry[type]; -} -function registerFieldTypeDefinition(_field, _initExtra, methods) { - registry[methods.type] = methods; -} - -var FieldType; -(function (FieldType) { - FieldType[FieldType["Number"] = 0] = "Number"; - FieldType[FieldType["FixedLengthArray"] = 1] = "FixedLengthArray"; - FieldType[FieldType["VariableLengthArray"] = 2] = "VariableLengthArray"; -})(FieldType || (FieldType = {})); - -function placeholder() { - return undefined; -} - -registerFieldTypeDefinition(placeholder(), placeholder(), { - type: FieldType.FixedLengthArray, - async deserialize({ context, field }) { - const buffer = await context.read(field.options.length); - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: buffer }; - case Array.SubType.String: - return { - value: context.decodeUtf8(buffer), - extra: buffer - }; - default: - throw new Error('Unknown type'); - } - }, - getSize({ field }) { - return field.options.length; - }, - initialize({ extra, field, object, value }) { - const backingField = {}; - if (typeof value === 'string') { - backingField.string = value; - if (extra) { - backingField.buffer = extra; - } - } - else { - backingField.buffer = value; - } - Array.initialize(object, field, backingField); - }, - serialize({ context, dataView, field, object, offset }) { - var _a; - const backingField = getBackingField(object, field.name); - (_a = backingField.buffer) !== null && _a !== void 0 ? _a : (backingField.buffer = context.encodeUtf8(backingField.string)); - new Uint8Array(dataView.buffer).set(new Uint8Array(backingField.buffer), offset); - } -}); - -var Number$1; -(function (Number) { - let SubType; - (function (SubType) { - SubType[SubType["Uint8"] = 0] = "Uint8"; - SubType[SubType["Uint16"] = 1] = "Uint16"; - SubType[SubType["Int32"] = 2] = "Int32"; - SubType[SubType["Uint32"] = 3] = "Uint32"; - SubType[SubType["Uint64"] = 4] = "Uint64"; - SubType[SubType["Int64"] = 5] = "Int64"; - })(SubType = Number.SubType || (Number.SubType = {})); - Number.SizeMap = { - [SubType.Uint8]: 1, - [SubType.Uint16]: 2, - [SubType.Int32]: 4, - [SubType.Uint32]: 4, - [SubType.Uint64]: 8, - [SubType.Int64]: 8, - }; - Number.DataViewGetterMap = { - [SubType.Uint8]: 'getUint8', - [SubType.Uint16]: 'getUint16', - [SubType.Int32]: 'getInt32', - [SubType.Uint32]: 'getUint32', - [SubType.Uint64]: 'getBigUint64', - [SubType.Int64]: 'getBigInt64', - }; - Number.DataViewSetterMap = { - [SubType.Uint8]: 'setUint8', - [SubType.Uint16]: 'setUint16', - [SubType.Int32]: 'setInt32', - [SubType.Uint32]: 'setUint32', - [SubType.Uint64]: 'setBigUint64', - [SubType.Int64]: 'setBigInt64', - }; -})(Number$1 || (Number$1 = {})); -registerFieldTypeDefinition(placeholder(), undefined, { - type: FieldType.Number, - getSize({ field }) { - return Number$1.SizeMap[field.subType]; - }, - async deserialize({ context, field, options }) { - const buffer = await context.read(Number$1.SizeMap[field.subType]); - const view = new DataView(buffer); - const value = view[Number$1.DataViewGetterMap[field.subType]](0, options.littleEndian); - return { value }; - }, - serialize({ dataView, field, object, offset, options }) { - dataView[Number$1.DataViewSetterMap[field.subType]](offset, object[field.name], options.littleEndian); - }, -}); - -var VariableLengthArray; -(function (VariableLengthArray) { - let EmptyBehavior; - (function (EmptyBehavior) { - EmptyBehavior[EmptyBehavior["Undefined"] = 0] = "Undefined"; - EmptyBehavior[EmptyBehavior["Empty"] = 1] = "Empty"; - })(EmptyBehavior = VariableLengthArray.EmptyBehavior || (VariableLengthArray.EmptyBehavior = {})); - function getLengthBackingField(object, field) { - return getBackingField(object, field.options.lengthField); - } - VariableLengthArray.getLengthBackingField = getLengthBackingField; - function setLengthBackingField(object, field, value) { - setBackingField(object, field.options.lengthField, value); - } - VariableLengthArray.setLengthBackingField = setLengthBackingField; - function initialize(object, field, value, context) { - Array.initialize(object, field, value); - const descriptor = Object.getOwnPropertyDescriptor(object, field.name); - delete object[field.name]; - switch (field.subType) { - case Array.SubType.ArrayBuffer: - Object.defineProperty(object, field.name, { - ...descriptor, - set(buffer) { - var _a; - descriptor.set.call(object, buffer); - setLengthBackingField(object, field, (_a = buffer === null || buffer === void 0 ? void 0 : buffer.byteLength) !== null && _a !== void 0 ? _a : 0); - }, - }); - delete object[field.options.lengthField]; - Object.defineProperty(object, field.options.lengthField, { - configurable: true, - enumerable: true, - get() { - return getLengthBackingField(object, field); - } - }); - break; - case Array.SubType.String: - Object.defineProperty(object, field.name, { - ...descriptor, - set(string) { - descriptor.set.call(object, string); - if (string) { - setLengthBackingField(object, field, undefined); - } - else { - setLengthBackingField(object, field, 0); - } - }, - }); - delete object[field.options.lengthField]; - Object.defineProperty(object, field.options.lengthField, { - configurable: true, - enumerable: true, - get() { - let value = getLengthBackingField(object, field); - if (value === undefined) { - const backingField = getBackingField(object, field.name); - const buffer = context.encodeUtf8(backingField.string); - backingField.buffer = buffer; - value = buffer.byteLength; - setLengthBackingField(object, field, value); - } - return value; - } - }); - break; - default: - throw new Error('Unknown type'); - } - setBackingField(object, field.name, value); - if (value.buffer) { - setLengthBackingField(object, field, value.buffer.byteLength); - } - } - VariableLengthArray.initialize = initialize; -})(VariableLengthArray || (VariableLengthArray = {})); -registerFieldTypeDefinition(placeholder(), placeholder(), { - type: FieldType.VariableLengthArray, - async deserialize({ context, field, object }) { - let length = object[field.options.lengthField]; - if (typeof length === 'string') { - length = Number.parseInt(length, 10); - } - if (length === 0) { - if (field.options.emptyBehavior === VariableLengthArray.EmptyBehavior.Empty) { - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: new ArrayBuffer(0) }; - case Array.SubType.String: - return { value: '', extra: new ArrayBuffer(0) }; - default: - throw new Error('Unknown type'); - } - } - else { - return { value: undefined }; - } - } - const buffer = await context.read(length); - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: buffer }; - case Array.SubType.String: - return { - value: context.decodeUtf8(buffer), - extra: buffer - }; - default: - throw new Error('Unknown type'); - } - }, - getSize() { return 0; }, - getDynamicSize({ field, object }) { - return object[field.options.lengthField]; - }, - initialize({ context, extra, field, object, value }) { - const backingField = {}; - if (typeof value === 'string') { - backingField.string = value; - if (extra) { - backingField.buffer = extra; - } - } - else { - backingField.buffer = value; - } - Array.initialize(object, field, backingField); - VariableLengthArray.initialize(object, field, backingField, context); - }, - serialize({ dataView, field, object, offset }) { - const backingField = getBackingField(object, field.name); - new Uint8Array(dataView.buffer).set(new Uint8Array(backingField.buffer), offset); - }, -}); - -const StructDefaultOptions = { - littleEndian: false, -}; - -class Struct { - constructor(options = StructDefaultOptions) { - this._size = 0; - this.fields = []; - this._extra = {}; - this.array = (name, type, options) => { - if ('length' in options) { - return this.field({ - type: FieldType.FixedLengthArray, - name, - subType: type, - options: options, - }); - } - else { - return this.field({ - type: FieldType.VariableLengthArray, - name, - subType: type, - options: options, - }); - } - }; - this.arrayBuffer = (name, options) => { - return this.array(name, Array.SubType.ArrayBuffer, options); - }; - this.string = (name, options) => { - return this.array(name, Array.SubType.String, options); - }; - this.options = { ...StructDefaultOptions, ...options }; - } - get size() { return this._size; } - clone() { - const result = new Struct(this.options); - result.fields = this.fields.slice(); - result._size = this._size; - result._extra = this._extra; - result._afterParsed = this._afterParsed; - return result; - } - field(field) { - const result = this.clone(); - result.fields.push(field); - const definition = getFieldTypeDefinition(field.type); - const size = definition.getSize({ field, options: this.options }); - result._size += size; - return result; - } - number(name, type, options = {}, _typescriptType) { - return this.field({ - type: FieldType.Number, - name, - subType: type, - options, - }); - } - uint8(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint8, options, _typescriptType); - } - uint16(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint16, options, _typescriptType); - } - int32(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Int32, options, _typescriptType); - } - uint32(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint32, options, _typescriptType); - } - uint64(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint64, options, _typescriptType); - } - int64(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Int64, options, _typescriptType); - } - extra(value) { - const result = this.clone(); - result._extra = { ...result._extra, ...Object.getOwnPropertyDescriptors(value) }; - return result; - } - afterParsed(callback) { - const result = this.clone(); - result._afterParsed = callback; - return result; - } - initializeField(context, field, fieldTypeDefinition, object, value, extra) { - if (fieldTypeDefinition.initialize) { - fieldTypeDefinition.initialize({ - context, - extra, - field, - object, - options: this.options, - value, - }); - } - else { - setBackingField(object, field.name, value); - defineSimpleAccessors(object, field.name); - } - } - create(init, context) { - const object = { - [BackingField]: {}, - }; - Object.defineProperties(object, this._extra); - for (const field of this.fields) { - const fieldTypeDefinition = getFieldTypeDefinition(field.type); - this.initializeField(context, field, fieldTypeDefinition, object, init[field.name]); - } - return object; - } - async deserialize(context) { - const object = { - [BackingField]: {}, - }; - Object.defineProperties(object, this._extra); - for (const field of this.fields) { - const fieldTypeDefinition = getFieldTypeDefinition(field.type); - const { value, extra } = await fieldTypeDefinition.deserialize({ - context, - field, - object, - options: this.options, - }); - this.initializeField(context, field, fieldTypeDefinition, object, value, extra); - } - if (this._afterParsed) { - const result = this._afterParsed.call(object, object); - if (result) { - return result; - } - } - return object; - } - serialize(init, context) { - const object = this.create(init, context); - let size = this._size; - let fieldSize = []; - for (let i = 0; i < this.fields.length; i += 1) { - const field = this.fields[i]; - const type = getFieldTypeDefinition(field.type); - if (type.getDynamicSize) { - fieldSize[i] = type.getDynamicSize({ - context, - field, - object, - options: this.options, - }); - size += fieldSize[i]; - } - else { - fieldSize[i] = type.getSize({ field, options: this.options }); - } - } - const buffer = new ArrayBuffer(size); - const dataView = new DataView(buffer); - let offset = 0; - for (let i = 0; i < this.fields.length; i += 1) { - const field = this.fields[i]; - const type = getFieldTypeDefinition(field.type); - type.serialize({ - context, - dataView, - field, - object, - offset, - options: this.options, - }); - offset += fieldSize[i]; - } - return buffer; - } -} - -class BufferedStream { - constructor(stream) { - this.stream = stream; - } - async read(length) { - let array; - let index; - if (this.buffer) { - const buffer = this.buffer; - if (buffer.byteLength > length) { - this.buffer = buffer.subarray(length); - return buffer.slice(0, length).buffer; - } - array = new Uint8Array(length); - array.set(buffer); - index = buffer.byteLength; - this.buffer = undefined; - } - else { - const buffer = await this.stream.read(length); - if (buffer.byteLength === length) { - return buffer; - } - if (buffer.byteLength > length) { - this.buffer = new Uint8Array(buffer, length); - return buffer.slice(0, length); - } - array = new Uint8Array(length); - array.set(new Uint8Array(buffer), 0); - index = buffer.byteLength; - } - while (index < length) { - const left = length - index; - const buffer = await this.stream.read(left); - if (buffer.byteLength > left) { - array.set(new Uint8Array(buffer, 0, left), index); - this.buffer = new Uint8Array(buffer, left); - return array.buffer; - } - array.set(new Uint8Array(buffer), index); - index += buffer.byteLength; - } - return array.buffer; - } - close() { - var _a, _b; - (_b = (_a = this.stream).close) === null || _b === void 0 ? void 0 : _b.call(_a); - } -} - -const AdbPacketWithoutPayload = new Struct({ littleEndian: true }) - .uint32('command', undefined) - .uint32('arg0') - .uint32('arg1') - .uint32('payloadLength') - .uint32('checksum') - .int32('magic'); -const AdbPacketStruct = AdbPacketWithoutPayload - .arrayBuffer('payload', { lengthField: 'payloadLength' }) - .afterParsed((value) => { - if (value[BackingField].magic !== value.magic) { - throw new Error('Invalid command'); - } -}); -let AdbPacket; -(function (AdbPacket) { - function create(init, calculateChecksum, backend) { - let checksum; - if (calculateChecksum && init.payload) { - const array = new Uint8Array(init.payload); - checksum = array.reduce((result, item) => result + item, 0); - } - else { - checksum = 0; - } - return AdbPacketStruct.create({ - ...init, - checksum, - magic: init.command ^ 0xFFFFFFFF, - }, backend); - } - AdbPacket.create = create; - async function read(backend) { - let buffer = await backend.read(24); - if (buffer.byteLength !== 24) { - // Maybe it's a payload from last connection. - // Ignore and try again - buffer = await backend.read(24); - } - let bufferUsed = false; - const stream = new BufferedStream({ - read(length) { - if (!bufferUsed) { - bufferUsed = true; - return buffer; - } - return backend.read(length); - } - }); - return AdbPacketStruct.deserialize({ - read: stream.read.bind(stream), - decodeUtf8: backend.decodeUtf8.bind(backend), - encodeUtf8: backend.encodeUtf8.bind(backend), - }); - } - AdbPacket.read = read; - async function write(packet, backend) { - // Write payload separately to avoid an extra copy - await backend.write(AdbPacketWithoutPayload.serialize(packet, backend)); - if (packet.payload) { - await backend.write(packet.payload); - } - } - AdbPacket.write = write; -})(AdbPacket || (AdbPacket = {})); - -const WebUsbDeviceFilter = { - classCode: 0xFF, - subclassCode: 0x42, - protocolCode: 1, -}; - -const Utf8Encoder = new TextEncoder(); -const Utf8Decoder = new TextDecoder(); -function encodeUtf8(input) { - return Utf8Encoder.encode(input); -} -function decodeUtf8(buffer) { - return Utf8Decoder.decode(buffer); -} - -//////////////////////////////////////////////////////// -// Dedicated adb and transport function for sideload. / -//////////////////////////////////////////////////////// - -class AdbWebBackendSideload { - constructor(device) { - this._device = device; - } - - async connect() { - var _a; - if (!this._device.opened) { - await this._device.open(); - } - - for (const configuration of this._device.configurations) { - for (const interface_ of configuration.interfaces) { - for (const alternate of interface_.alternates) { - if (alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && - alternate.interfaceClass === WebUsbDeviceFilter.classCode && - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode) { - if (((_a = this._device.configuration) === null || _a === void 0 ? void 0 : _a.configurationValue) !== configuration.configurationValue) { - await this._device.selectConfiguration(configuration.configurationValue); - } - if (!interface_.claimed) { - await this._device.claimInterface(interface_.interfaceNumber); - } - if (interface_.alternate.alternateSetting !== alternate.alternateSetting) { - await this._device.selectAlternateInterface(interface_.interfaceNumber, alternate.alternateSetting); - } - for (const endpoint of alternate.endpoints) { - switch (endpoint.direction) { - case 'in': - this._inEndpointNumber = endpoint.endpointNumber; - if (this._outEndpointNumber !== undefined) { - return; - } - break; - case 'out': - this._outEndpointNumber = endpoint.endpointNumber; - if (this._inEndpointNumber !== undefined) { - return; - } - break; - } - } - } - } - } - } - throw new Error('Unknown error'); - } - - async write(buffer) { - await this._device.transferOut(this._outEndpointNumber, buffer); - } - async read(length) { - const result = await this._device.transferIn(this._inEndpointNumber, length); - if (result.status === 'stall') { - await this._device.clearHalt('in', this._inEndpointNumber); - } - const { buffer } = result.data; - return buffer; - } - - encodeUtf8(input) { - return encodeUtf8(input); - } - decodeUtf8(buffer) { - return decodeUtf8(buffer); - } - -} - -export class AdbSideload { - // This one is dedicated for adb sidelaod - constructor(backend) { - this._connected = false; - this.backend = new AdbWebBackendSideload(backend); - this.sendLock = new AutoResetEvent(); - this.initializers = new AsyncOperationManager(1); - } - get connected() { return this._connected; } - async connect() { - var _a, _b; - await ((_b = (_a = this.backend).connect) === null || _b === void 0 ? void 0 : _b.call(_a)); - this.calculateChecksum = true; - this.appendNullToServiceString = true; - const version = 0x01000001; - const maxPayloadSize = 0x100000; - - await this.sendPacket(AdbCommand.Connect, version, maxPayloadSize, "host::\0"); - const r = await AdbPacket.read(this.backend); - if (r.command == AdbCommand.Connect) { - //All is fine - } else { - throw new Error('Adb sideload connection error'); - } - } - - spawn(command, ...args) { - return this.createStream(`shell:${command} ${args.join(' ')}`); - } - exec(command, ...args) { - return this.createStreamAndReadAll(`shell:${command} ${args.join(' ')}`); - } - async getProp(key) { - const output = await this.exec('getprop', key); - return output.trim(); - } - - async createStream(service) { - const localId = 1; - service+='\0'; - let remoteId; - await this.sendPacket(AdbCommand.Open, localId, 0, service); - const r = await AdbPacket.read(this.backend); - if (r.command == AdbCommand.Okay) { - remoteId = r.arg0; - return { localId: localId, remoteId:remoteId }; - } else { - throw new Error('Adb sideload create stream error'); - } - } - - async dispose() { - this.packetDispatcher.dispose(); - await this.backend.dispose(); - } - async sendPacket(packetOrCommand, arg0, arg1, payload) { - var _a; - let init; - if (arguments.length === 1) { - init = packetOrCommand; - } - else { - init = { - command: packetOrCommand, - arg0: arg0, - arg1: arg1, - payload: typeof payload === 'string' ? this.backend.encodeUtf8(payload) : payload, - }; - } - if (init.payload && - init.payload.byteLength > this.maxPayloadSize) { - throw new Error('payload too large'); - } - try { - this.sendLock.wait() - const packet = AdbPacket.create(init, this.calculateChecksum, this.backend); - await AdbPacket.write(packet, this.backend); - } - catch(e){ - console.log("error send sendPacket ", e); - } - finally { - this.sendLock.notifyOne(); - } - } -} - -//////////////////////////////// - - +import { AdbCommand, AutoResetEvent } from "@yume-chan/adb"; + +import { AsyncOperationManager } from "@yume-chan/async"; + +// ADB Message structures +// There are probably equivalents in the new adb library. + +export class MessageClass { + constructor(header, data) { + this.header = header; + this.data = data; + } + + static checksum(dataView) { + let sum = 0; + for (let i = 0; i < dataView.byteLength; i++) { + sum += dataView.getUint8(i); + } + return sum & 0xffffffff; + } +} + +export class MessageHeader { + constructor(cmd, arg0, arg1, length, checksum) { + this.cmd = cmd; + this.arg0 = arg0; + this.arg1 = arg1; + this.length = length; + this.checksum = checksum; + } + + toDataView() { + const view = new DataView(new ArrayBuffer(24)); + const rawCmd = this.encodeCmd(this.cmd); + const magic = rawCmd ^ 0xffffffff; + view.setUint32(0, rawCmd, true); + view.setUint32(4, this.arg0, true); + view.setUint32(8, this.arg1, true); + view.setUint32(12, this.length, true); + view.setUint32(16, this.checksum, true); + view.setUint32(20, magic, true); + return view; + } + + encodeCmd(cmd) { + const encoder = new TextEncoder(); + const buffer = encoder.encode(cmd).buffer; + const view = new DataView(buffer); + return view.getUint32(0, true); + } +} + +/* +Classes and utilities from an old adb lib. +There are probably equivalents in the new adb library. +It would be interesting to find them, but it may be rather difficult (name change and perhaps synthetics). +*/ + +const BackingField = Symbol("BackingField"); +function getBackingField(object, field) { + return object[BackingField][field]; +} +function setBackingField(object, field, value) { + object[BackingField][field] = value; +} +function defineSimpleAccessors(object, field) { + Object.defineProperty(object, field, { + configurable: true, + enumerable: true, + get() { + return getBackingField(object, field); + }, + set(value) { + setBackingField(object, field, value); + }, + }); +} + +var Array; +(function (Array) { + let SubType; + (function (SubType) { + SubType[(SubType["ArrayBuffer"] = 0)] = "ArrayBuffer"; + SubType[(SubType["String"] = 1)] = "String"; + })((SubType = Array.SubType || (Array.SubType = {}))); + function initialize(object, field, value) { + switch (field.subType) { + case SubType.ArrayBuffer: + Object.defineProperty(object, field.name, { + configurable: true, + enumerable: true, + get() { + return getBackingField(object, field.name).buffer; + }, + set(buffer) { + setBackingField(object, field.name, { buffer }); + }, + }); + break; + case SubType.String: + Object.defineProperty(object, field.name, { + configurable: true, + enumerable: true, + get() { + return getBackingField(object, field.name).string; + }, + set(string) { + setBackingField(object, field.name, { string }); + }, + }); + break; + default: + throw new Error("Unknown type"); + } + setBackingField(object, field.name, value); + } + Array.initialize = initialize; +})(Array || (Array = {})); + +const registry = {}; +function getFieldTypeDefinition(type) { + return registry[type]; +} +function registerFieldTypeDefinition(_field, _initExtra, methods) { + registry[methods.type] = methods; +} + +var FieldType; +(function (FieldType) { + FieldType[(FieldType["Number"] = 0)] = "Number"; + FieldType[(FieldType["FixedLengthArray"] = 1)] = "FixedLengthArray"; + FieldType[(FieldType["VariableLengthArray"] = 2)] = "VariableLengthArray"; +})(FieldType || (FieldType = {})); + +function placeholder() { + return undefined; +} + +registerFieldTypeDefinition(placeholder(), placeholder(), { + type: FieldType.FixedLengthArray, + async deserialize({ context, field }) { + const buffer = await context.read(field.options.length); + switch (field.subType) { + case Array.SubType.ArrayBuffer: + return { value: buffer }; + case Array.SubType.String: + return { + value: context.decodeUtf8(buffer), + extra: buffer, + }; + default: + throw new Error("Unknown type"); + } + }, + getSize({ field }) { + return field.options.length; + }, + initialize({ extra, field, object, value }) { + const backingField = {}; + if (typeof value === "string") { + backingField.string = value; + if (extra) { + backingField.buffer = extra; + } + } else { + backingField.buffer = value; + } + Array.initialize(object, field, backingField); + }, + serialize({ context, dataView, field, object, offset }) { + var _a; + const backingField = getBackingField(object, field.name); + (_a = backingField.buffer) !== null && _a !== void 0 + ? _a + : (backingField.buffer = context.encodeUtf8(backingField.string)); + new Uint8Array(dataView.buffer).set( + new Uint8Array(backingField.buffer), + offset, + ); + }, +}); + +var Number$1; +(function (Number) { + let SubType; + (function (SubType) { + SubType[(SubType["Uint8"] = 0)] = "Uint8"; + SubType[(SubType["Uint16"] = 1)] = "Uint16"; + SubType[(SubType["Int32"] = 2)] = "Int32"; + SubType[(SubType["Uint32"] = 3)] = "Uint32"; + SubType[(SubType["Uint64"] = 4)] = "Uint64"; + SubType[(SubType["Int64"] = 5)] = "Int64"; + })((SubType = Number.SubType || (Number.SubType = {}))); + Number.SizeMap = { + [SubType.Uint8]: 1, + [SubType.Uint16]: 2, + [SubType.Int32]: 4, + [SubType.Uint32]: 4, + [SubType.Uint64]: 8, + [SubType.Int64]: 8, + }; + Number.DataViewGetterMap = { + [SubType.Uint8]: "getUint8", + [SubType.Uint16]: "getUint16", + [SubType.Int32]: "getInt32", + [SubType.Uint32]: "getUint32", + [SubType.Uint64]: "getBigUint64", + [SubType.Int64]: "getBigInt64", + }; + Number.DataViewSetterMap = { + [SubType.Uint8]: "setUint8", + [SubType.Uint16]: "setUint16", + [SubType.Int32]: "setInt32", + [SubType.Uint32]: "setUint32", + [SubType.Uint64]: "setBigUint64", + [SubType.Int64]: "setBigInt64", + }; +})(Number$1 || (Number$1 = {})); +registerFieldTypeDefinition(placeholder(), undefined, { + type: FieldType.Number, + getSize({ field }) { + return Number$1.SizeMap[field.subType]; + }, + async deserialize({ context, field, options }) { + const buffer = await context.read(Number$1.SizeMap[field.subType]); + const view = new DataView(buffer); + const value = view[Number$1.DataViewGetterMap[field.subType]]( + 0, + options.littleEndian, + ); + return { value }; + }, + serialize({ dataView, field, object, offset, options }) { + dataView[Number$1.DataViewSetterMap[field.subType]]( + offset, + object[field.name], + options.littleEndian, + ); + }, +}); + +var VariableLengthArray; +(function (VariableLengthArray) { + let EmptyBehavior; + (function (EmptyBehavior) { + EmptyBehavior[(EmptyBehavior["Undefined"] = 0)] = "Undefined"; + EmptyBehavior[(EmptyBehavior["Empty"] = 1)] = "Empty"; + })( + (EmptyBehavior = + VariableLengthArray.EmptyBehavior || + (VariableLengthArray.EmptyBehavior = {})), + ); + function getLengthBackingField(object, field) { + return getBackingField(object, field.options.lengthField); + } + VariableLengthArray.getLengthBackingField = getLengthBackingField; + function setLengthBackingField(object, field, value) { + setBackingField(object, field.options.lengthField, value); + } + VariableLengthArray.setLengthBackingField = setLengthBackingField; + function initialize(object, field, value, context) { + Array.initialize(object, field, value); + const descriptor = Object.getOwnPropertyDescriptor(object, field.name); + delete object[field.name]; + switch (field.subType) { + case Array.SubType.ArrayBuffer: + Object.defineProperty(object, field.name, { + ...descriptor, + set(buffer) { + var _a; + descriptor.set.call(object, buffer); + setLengthBackingField( + object, + field, + (_a = + buffer === null || buffer === void 0 + ? void 0 + : buffer.byteLength) !== null && _a !== void 0 + ? _a + : 0, + ); + }, + }); + delete object[field.options.lengthField]; + Object.defineProperty(object, field.options.lengthField, { + configurable: true, + enumerable: true, + get() { + return getLengthBackingField(object, field); + }, + }); + break; + case Array.SubType.String: + Object.defineProperty(object, field.name, { + ...descriptor, + set(string) { + descriptor.set.call(object, string); + if (string) { + setLengthBackingField(object, field, undefined); + } else { + setLengthBackingField(object, field, 0); + } + }, + }); + delete object[field.options.lengthField]; + Object.defineProperty(object, field.options.lengthField, { + configurable: true, + enumerable: true, + get() { + let value = getLengthBackingField(object, field); + if (value === undefined) { + const backingField = getBackingField(object, field.name); + const buffer = context.encodeUtf8(backingField.string); + backingField.buffer = buffer; + value = buffer.byteLength; + setLengthBackingField(object, field, value); + } + return value; + }, + }); + break; + default: + throw new Error("Unknown type"); + } + setBackingField(object, field.name, value); + if (value.buffer) { + setLengthBackingField(object, field, value.buffer.byteLength); + } + } + VariableLengthArray.initialize = initialize; +})(VariableLengthArray || (VariableLengthArray = {})); +registerFieldTypeDefinition(placeholder(), placeholder(), { + type: FieldType.VariableLengthArray, + async deserialize({ context, field, object }) { + let length = object[field.options.lengthField]; + if (typeof length === "string") { + length = Number.parseInt(length, 10); + } + if (length === 0) { + if ( + field.options.emptyBehavior === VariableLengthArray.EmptyBehavior.Empty + ) { + switch (field.subType) { + case Array.SubType.ArrayBuffer: + return { value: new ArrayBuffer(0) }; + case Array.SubType.String: + return { value: "", extra: new ArrayBuffer(0) }; + default: + throw new Error("Unknown type"); + } + } else { + return { value: undefined }; + } + } + const buffer = await context.read(length); + switch (field.subType) { + case Array.SubType.ArrayBuffer: + return { value: buffer }; + case Array.SubType.String: + return { + value: context.decodeUtf8(buffer), + extra: buffer, + }; + default: + throw new Error("Unknown type"); + } + }, + getSize() { + return 0; + }, + getDynamicSize({ field, object }) { + return object[field.options.lengthField]; + }, + initialize({ context, extra, field, object, value }) { + const backingField = {}; + if (typeof value === "string") { + backingField.string = value; + if (extra) { + backingField.buffer = extra; + } + } else { + backingField.buffer = value; + } + Array.initialize(object, field, backingField); + VariableLengthArray.initialize(object, field, backingField, context); + }, + serialize({ dataView, field, object, offset }) { + const backingField = getBackingField(object, field.name); + new Uint8Array(dataView.buffer).set( + new Uint8Array(backingField.buffer), + offset, + ); + }, +}); + +const StructDefaultOptions = { + littleEndian: false, +}; + +class Struct { + constructor(options = StructDefaultOptions) { + this._size = 0; + this.fields = []; + this._extra = {}; + this.array = (name, type, options) => { + if ("length" in options) { + return this.field({ + type: FieldType.FixedLengthArray, + name, + subType: type, + options: options, + }); + } else { + return this.field({ + type: FieldType.VariableLengthArray, + name, + subType: type, + options: options, + }); + } + }; + this.arrayBuffer = (name, options) => { + return this.array(name, Array.SubType.ArrayBuffer, options); + }; + this.string = (name, options) => { + return this.array(name, Array.SubType.String, options); + }; + this.options = { ...StructDefaultOptions, ...options }; + } + get size() { + return this._size; + } + clone() { + const result = new Struct(this.options); + result.fields = this.fields.slice(); + result._size = this._size; + result._extra = this._extra; + result._afterParsed = this._afterParsed; + return result; + } + field(field) { + const result = this.clone(); + result.fields.push(field); + const definition = getFieldTypeDefinition(field.type); + const size = definition.getSize({ field, options: this.options }); + result._size += size; + return result; + } + number(name, type, options = {}, _typescriptType) { + return this.field({ + type: FieldType.Number, + name, + subType: type, + options, + }); + } + uint8(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint8, options, _typescriptType); + } + uint16(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint16, options, _typescriptType); + } + int32(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Int32, options, _typescriptType); + } + uint32(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint32, options, _typescriptType); + } + uint64(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Uint64, options, _typescriptType); + } + int64(name, options = {}, _typescriptType) { + return this.number(name, Number$1.SubType.Int64, options, _typescriptType); + } + extra(value) { + const result = this.clone(); + result._extra = { + ...result._extra, + ...Object.getOwnPropertyDescriptors(value), + }; + return result; + } + afterParsed(callback) { + const result = this.clone(); + result._afterParsed = callback; + return result; + } + initializeField(context, field, fieldTypeDefinition, object, value, extra) { + if (fieldTypeDefinition.initialize) { + fieldTypeDefinition.initialize({ + context, + extra, + field, + object, + options: this.options, + value, + }); + } else { + setBackingField(object, field.name, value); + defineSimpleAccessors(object, field.name); + } + } + create(init, context) { + const object = { + [BackingField]: {}, + }; + Object.defineProperties(object, this._extra); + for (const field of this.fields) { + const fieldTypeDefinition = getFieldTypeDefinition(field.type); + this.initializeField( + context, + field, + fieldTypeDefinition, + object, + init[field.name], + ); + } + return object; + } + async deserialize(context) { + const object = { + [BackingField]: {}, + }; + Object.defineProperties(object, this._extra); + for (const field of this.fields) { + const fieldTypeDefinition = getFieldTypeDefinition(field.type); + const { value, extra } = await fieldTypeDefinition.deserialize({ + context, + field, + object, + options: this.options, + }); + this.initializeField( + context, + field, + fieldTypeDefinition, + object, + value, + extra, + ); + } + if (this._afterParsed) { + const result = this._afterParsed.call(object, object); + if (result) { + return result; + } + } + return object; + } + serialize(init, context) { + const object = this.create(init, context); + let size = this._size; + let fieldSize = []; + for (let i = 0; i < this.fields.length; i += 1) { + const field = this.fields[i]; + const type = getFieldTypeDefinition(field.type); + if (type.getDynamicSize) { + fieldSize[i] = type.getDynamicSize({ + context, + field, + object, + options: this.options, + }); + size += fieldSize[i]; + } else { + fieldSize[i] = type.getSize({ field, options: this.options }); + } + } + const buffer = new ArrayBuffer(size); + const dataView = new DataView(buffer); + let offset = 0; + for (let i = 0; i < this.fields.length; i += 1) { + const field = this.fields[i]; + const type = getFieldTypeDefinition(field.type); + type.serialize({ + context, + dataView, + field, + object, + offset, + options: this.options, + }); + offset += fieldSize[i]; + } + return buffer; + } +} + +class BufferedStream { + constructor(stream) { + this.stream = stream; + } + async read(length) { + let array; + let index; + if (this.buffer) { + const buffer = this.buffer; + if (buffer.byteLength > length) { + this.buffer = buffer.subarray(length); + return buffer.slice(0, length).buffer; + } + array = new Uint8Array(length); + array.set(buffer); + index = buffer.byteLength; + this.buffer = undefined; + } else { + const buffer = await this.stream.read(length); + if (buffer.byteLength === length) { + return buffer; + } + if (buffer.byteLength > length) { + this.buffer = new Uint8Array(buffer, length); + return buffer.slice(0, length); + } + array = new Uint8Array(length); + array.set(new Uint8Array(buffer), 0); + index = buffer.byteLength; + } + while (index < length) { + const left = length - index; + const buffer = await this.stream.read(left); + if (buffer.byteLength > left) { + array.set(new Uint8Array(buffer, 0, left), index); + this.buffer = new Uint8Array(buffer, left); + return array.buffer; + } + array.set(new Uint8Array(buffer), index); + index += buffer.byteLength; + } + return array.buffer; + } + close() { + var _a, _b; + (_b = (_a = this.stream).close) === null || _b === void 0 + ? void 0 + : _b.call(_a); + } +} + +const AdbPacketWithoutPayload = new Struct({ littleEndian: true }) + .uint32("command", undefined) + .uint32("arg0") + .uint32("arg1") + .uint32("payloadLength") + .uint32("checksum") + .int32("magic"); +const AdbPacketStruct = AdbPacketWithoutPayload.arrayBuffer("payload", { + lengthField: "payloadLength", +}).afterParsed((value) => { + if (value[BackingField].magic !== value.magic) { + throw new Error("Invalid command"); + } +}); +let AdbPacket; +(function (AdbPacket) { + function create(init, calculateChecksum, backend) { + let checksum; + if (calculateChecksum && init.payload) { + const array = new Uint8Array(init.payload); + checksum = array.reduce((result, item) => result + item, 0); + } else { + checksum = 0; + } + return AdbPacketStruct.create( + { + ...init, + checksum, + magic: init.command ^ 0xffffffff, + }, + backend, + ); + } + AdbPacket.create = create; + async function read(backend) { + let buffer = await backend.read(24); + if (buffer.byteLength !== 24) { + // Maybe it's a payload from last connection. + // Ignore and try again + buffer = await backend.read(24); + } + let bufferUsed = false; + const stream = new BufferedStream({ + read(length) { + if (!bufferUsed) { + bufferUsed = true; + return buffer; + } + return backend.read(length); + }, + }); + return AdbPacketStruct.deserialize({ + read: stream.read.bind(stream), + decodeUtf8: backend.decodeUtf8.bind(backend), + encodeUtf8: backend.encodeUtf8.bind(backend), + }); + } + AdbPacket.read = read; + async function write(packet, backend) { + // Write payload separately to avoid an extra copy + await backend.write(AdbPacketWithoutPayload.serialize(packet, backend)); + if (packet.payload) { + await backend.write(packet.payload); + } + } + AdbPacket.write = write; +})(AdbPacket || (AdbPacket = {})); + +const WebUsbDeviceFilter = { + classCode: 0xff, + subclassCode: 0x42, + protocolCode: 1, +}; + +const Utf8Encoder = new TextEncoder(); +const Utf8Decoder = new TextDecoder(); +function encodeUtf8(input) { + return Utf8Encoder.encode(input); +} +function decodeUtf8(buffer) { + return Utf8Decoder.decode(buffer); +} + +//////////////////////////////////////////////////////// +// Dedicated adb and transport function for sideload. / +//////////////////////////////////////////////////////// + +class AdbWebBackendSideload { + constructor(device) { + this._device = device; + } + + async connect() { + var _a; + if (!this._device.opened) { + await this._device.open(); + } + + for (const configuration of this._device.configurations) { + for (const interface_ of configuration.interfaces) { + for (const alternate of interface_.alternates) { + if ( + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && + alternate.interfaceClass === WebUsbDeviceFilter.classCode && + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode + ) { + if ( + ((_a = this._device.configuration) === null || _a === void 0 + ? void 0 + : _a.configurationValue) !== configuration.configurationValue + ) { + await this._device.selectConfiguration( + configuration.configurationValue, + ); + } + if (!interface_.claimed) { + await this._device.claimInterface(interface_.interfaceNumber); + } + if ( + interface_.alternate.alternateSetting !== + alternate.alternateSetting + ) { + await this._device.selectAlternateInterface( + interface_.interfaceNumber, + alternate.alternateSetting, + ); + } + for (const endpoint of alternate.endpoints) { + switch (endpoint.direction) { + case "in": + this._inEndpointNumber = endpoint.endpointNumber; + if (this._outEndpointNumber !== undefined) { + return; + } + break; + case "out": + this._outEndpointNumber = endpoint.endpointNumber; + if (this._inEndpointNumber !== undefined) { + return; + } + break; + } + } + } + } + } + } + throw new Error("Unknown error"); + } + + async write(buffer) { + await this._device.transferOut(this._outEndpointNumber, buffer); + } + async read(length) { + const result = await this._device.transferIn( + this._inEndpointNumber, + length, + ); + if (result.status === "stall") { + await this._device.clearHalt("in", this._inEndpointNumber); + } + const { buffer } = result.data; + return buffer; + } + + encodeUtf8(input) { + return encodeUtf8(input); + } + decodeUtf8(buffer) { + return decodeUtf8(buffer); + } +} + +export class AdbSideload { + // This one is dedicated for adb sidelaod + constructor(backend) { + this._connected = false; + this.backend = new AdbWebBackendSideload(backend); + this.sendLock = new AutoResetEvent(); + this.initializers = new AsyncOperationManager(1); + } + get connected() { + return this._connected; + } + async connect() { + var _a, _b; + await ((_b = (_a = this.backend).connect) === null || _b === void 0 + ? void 0 + : _b.call(_a)); + this.calculateChecksum = true; + this.appendNullToServiceString = true; + const version = 0x01000001; + const maxPayloadSize = 0x100000; + + await this.sendPacket( + AdbCommand.Connect, + version, + maxPayloadSize, + "host::\0", + ); + const r = await AdbPacket.read(this.backend); + if (r.command == AdbCommand.Connect) { + //All is fine + } else { + throw new Error("Adb sideload connection error"); + } + } + + spawn(command, ...args) { + return this.createStream(`shell:${command} ${args.join(" ")}`); + } + exec(command, ...args) { + return this.createStreamAndReadAll(`shell:${command} ${args.join(" ")}`); + } + async getProp(key) { + const output = await this.exec("getprop", key); + return output.trim(); + } + + async createStream(service) { + const localId = 1; + service += "\0"; + let remoteId; + await this.sendPacket(AdbCommand.Open, localId, 0, service); + const r = await AdbPacket.read(this.backend); + if (r.command == AdbCommand.Okay) { + remoteId = r.arg0; + return { localId: localId, remoteId: remoteId }; + } else { + throw new Error("Adb sideload create stream error"); + } + } + + async dispose() { + this.packetDispatcher.dispose(); + await this.backend.dispose(); + } + async sendPacket(packetOrCommand, arg0, arg1, payload) { + var _a; + let init; + if (arguments.length === 1) { + init = packetOrCommand; + } else { + init = { + command: packetOrCommand, + arg0: arg0, + arg1: arg1, + payload: + typeof payload === "string" + ? this.backend.encodeUtf8(payload) + : payload, + }; + } + if (init.payload && init.payload.byteLength > this.maxPayloadSize) { + throw new Error("payload too large"); + } + try { + this.sendLock.wait(); + const packet = AdbPacket.create( + init, + this.calculateChecksum, + this.backend, + ); + await AdbPacket.write(packet, this.backend); + } catch (e) { + console.log("error send sendPacket ", e); + } finally { + this.sendLock.notifyOne(); + } + } +} + +//////////////////////////////// diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index 123a48a..c6a6b65 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -6,7 +6,6 @@ import { Adb, AdbDaemonTransport } from "@yume-chan/adb"; import AdbWebCredentialStore from "@yume-chan/adb-credential-web"; export class ADB extends Device { - static Manager = AdbDaemonWebUsbDeviceManager.BROWSER; constructor(device) { @@ -32,16 +31,18 @@ export class ADB extends Device { async connect() { try { console.log("debug adb connect"); - - const adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ + + const adbDaemonWebUsbDevice = + await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ if (typeof adbDaemonWebUsbDevice == "undefined") { throw new Error("No device connected (1)"); } let connection; try { - connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ - } catch ( err ) { + connection = + await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + } catch (err) { const devices = await Manager.getDevices(); if (!devices.length) { throw new Error("No device connected (2)"); @@ -53,13 +54,13 @@ export class ADB extends Device { const transport = await AdbDaemonTransport.authenticate({ serial: connection.deserial, connection, - credentialStore: credentialStore + credentialStore: credentialStore, }); const adb = new Adb(transport); - + //const version = await adb.getProp("ro.build.version.release"); - this.device = adbDaemonWebUsbDevice; + this.device = adbDaemonWebUsbDevice; this.webusb = adb; /*Adb*/ WDebug.log("----------------------------------"); @@ -68,11 +69,10 @@ export class ADB extends Device { WDebug.log("Name", adbDaemonWebUsbDevice.name); WDebug.log(">Device (codename)", adb.transport.banner.device); // codemane WDebug.log("----------------------------------"); - - } catch (e) { - this.device = null; - throw new Error(`Cannot connect ADB ${e.message || e}`); - } + } catch (e) { + this.device = null; + throw new Error(`Cannot connect ADB ${e.message || e}`); + } } getProductName() { @@ -84,7 +84,7 @@ export class ADB extends Device { } async getSerialNumber() { - return this.webusb.getProp("ro.boot.serialno"); + return this.webusb.getProp("ro.boot.serialno"); } async runCommand(cmd) { @@ -97,4 +97,3 @@ export class ADB extends Device { return res; } } - diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index 470fd6a..8568a77 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -2,7 +2,7 @@ import { ADB } from "./adb.class.js"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -import { AdbSideload, MessageClass, MessageHeader } from "../adb-sideload.js" +import { AdbSideload, MessageClass, MessageHeader } from "../adb-sideload.js"; export class Recovery extends Device { constructor(device) { @@ -32,13 +32,14 @@ export class Recovery extends Device { if (this.device && this.device.isConnected) { WDebug.log("Connect recovery the device is connected"); } else { - const adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ + const adbDaemonWebUsbDevice = + await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ const adbDevice = new AdbSideload(adbDaemonWebUsbDevice.raw, null); WDebug.log("adbDevice = ", adbDevice); - await adbDevice.connect(); + await adbDevice.connect(); - this.device = adbDaemonWebUsbDevice; - this.webusb = adbDevice; + this.device = adbDaemonWebUsbDevice; + this.webusb = adbDevice; } } catch (e) { this.device = null; @@ -81,7 +82,6 @@ export class Recovery extends Device { const localId = this.stream.localId; const remoteId = this.stream.remoteId; - let header = new MessageHeader("OKAY", localId, remoteId, 0, checksum); let receivedData, message; diff --git a/app/src/controller/downloader.manager.js b/app/src/controller/downloader.manager.js index b2d38d0..8940e77 100644 --- a/app/src/controller/downloader.manager.js +++ b/app/src/controller/downloader.manager.js @@ -1,263 +1,263 @@ -const DB_NAME = "MurenaBlobStore"; -const DB_VERSION = 1; - -import ky from "ky"; -import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"; - -/** - * Download Manager - * Download files from the device folder of [modelname].json - * Download with DBStore and unzip if necessary - * Blobs are in this.files[filename] - */ -export class Downloader { - constructor() { - this.db = null; - this.stored = {}; - } - - async init() { - if (this.db) return; // Already initialized - - this.db = await this.openDBStore(); - await this.clearDBStore(); - this.quota = await navigator.storage.estimate(); - } - - /* - * */ - async downloadAndUnzipFolder( - filesRequired, - folder, - onDownloadProgress, - onUnzipProgress, - ) { - let current_file; - try { - for (let i = 0; i < folder.length; i++) { - const file = folder[i]; - current_file = file.path; - if (filesRequired.includes(file.name) || file.unzip) { - const blob = await this.download(file.path, (value, total) => { - onDownloadProgress(value, total, file.name); - }); - if (file.unzip) { - const zipReader = new ZipReader(new BlobReader(blob)); - const filesEntries = await zipReader.getEntries(); - for (let i = 0; i < filesEntries.length; i++) { - const unzippedEntry = await this.getFileFromZip( - filesEntries[i], - (value, total) => { - onUnzipProgress(value, total, filesEntries[i].filename); - }, - ); - let filename = this.getMappedName( - filesEntries[i].filename, - file.mapping, - ); - if (filesRequired.includes(filename)) { - await this.setInDBStore(unzippedEntry.blob, filename); - this.stored[filename] = true; - } - } - await zipReader.close(); - } else { - await this.setInDBStore(blob, file.name); - this.stored[file.name] = true; - } - } - } - } catch (e) { - throw new Error( - `downloadAndUnzipFolder Error
current_file ${current_file}
${e.message || e}`, - ); - } - } - - async getFileFromZip(file, onProgress) { - const name = file.filename; - const blob = await file.getData(new BlobWriter(), { - onprogress: (value, total) => { - onProgress(value, total, name); - }, - useWebWorkers: false, - }); - return { - name, - blob, - }; - } - - getMappedName(filename, map) { - if (!map) { - return filename; - } - - console.log(map); - for (const [regex, newFilename] of Object.entries(map)) { - let re = new RegExp(regex); - if (filename.match(re)) { - return newFilename; - } - } - return filename; - } - - /** - * @param name - * @returns {} - * It does not launch download (downloads are launched with downloadFolder) - * this function retrieve the promise linked to the fileName - */ - async getFile(name) { - const file = this.stored[name]; - if (!file) { - throw new Error(`File ${name} was not previously downloaded`); - } - return await this.getFromDBStore(name); - } - - /* - * getData from a zip file - * */ - async getData(dbFile, fileEntry, onProgress) { - const _zip = new BlobWriter(); - const blob = await fileEntry.getData(_zip, { - onprogress: (value, total) => { - onProgress(value, total, dbFile); - }, - onend: () => {}, - useWebWorkers: true, - }); - return blob; - } - - async download(path, onProgress) { - try { - const buffers = await this.fetch( - { - url: path, - chunkSize: 16 * 1024 * 1024, - poolLimit: 1, - }, - onProgress, - ); - - //let totalSize = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0); - const ret = new Blob(buffers); - return ret; - } catch (e) { - throw new Error(`${e.message || e}`); - } - } - - async clearDBStore() { - const store = this.db - .transaction(DB_NAME, "readwrite") - .objectStore(DB_NAME); - store.clear(); - } - - async setInDBStore(blob, key) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction(DB_NAME, "readwrite"); - const store = transaction.objectStore(DB_NAME); - const request = store.put(blob, key); - - request.onsuccess = () => { - resolve(); - }; - - request.onerror = (event) => { - reject(event.target.error); - }; - }); - } - - async getFromDBStore(key) { - return new Promise((resolve, reject) => { - const transaction = this.db.transaction(DB_NAME, "readonly"); - const store = transaction.objectStore(DB_NAME); - const request = store.get(key); - request.onsuccess = function (event) { - const result = event.target.result; - if (result) { - resolve(result); - } else { - resolve(null); - } - }; - request.onerror = function (event) { - reject(event.target.error); - }; - }); - } - - async openDBStore() { - return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); - request.onerror = reject; - request.onupgradeneeded = function (event) { - const db = event.target.result; - db.createObjectStore(DB_NAME, { autoIncrement: false }); - }; - request.onsuccess = function (event) { - resolve(event.target.result); - }; - }); - } - - concatenate(arrays) { - if (!arrays.length) return null; - let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); - let result = new Uint8Array(totalLength); - let length = 0; - for (let array of arrays) { - result.set(array, length); - length += array.length; - } - return result; - } - - async getContentLength(url) { - const response = await ky.head(url); - const contentLength = response.headers.get("content-length"); - return parseInt(contentLength, 10); - } - - async fetch({ url, chunkSize }, onProgress) { - try { - const contentLength = await this.getContentLength(url); - const totalChunks = Math.ceil(contentLength / chunkSize); - const buffers = []; - - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize - 1, contentLength - 1); - try { - const response = await ky.get(url, { - headers: { - Range: `bytes=${start}-${end}`, - }, - }); - if (!response.ok) { - throw new Error( - `Cannot download chunk (1) ${i + 1}: ${response.status} ${response.statusText}`, - ); - } - - const chunk = await response.arrayBuffer(); - buffers.push(chunk); - onProgress(start + chunk.byteLength, contentLength); - } catch (chunkError) { - throw new Error( - `Cannot download chunk (2) ${i + 1} ${chunkError.message || chunkError}`, - ); - } - } - return buffers; - } catch (error) { - throw new Error(`Download fails ${error.message || error}`); - } - } -} +const DB_NAME = "MurenaBlobStore"; +const DB_VERSION = 1; + +import ky from "ky"; +import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"; + +/** + * Download Manager + * Download files from the device folder of [modelname].json + * Download with DBStore and unzip if necessary + * Blobs are in this.files[filename] + */ +export class Downloader { + constructor() { + this.db = null; + this.stored = {}; + } + + async init() { + if (this.db) return; // Already initialized + + this.db = await this.openDBStore(); + await this.clearDBStore(); + this.quota = await navigator.storage.estimate(); + } + + /* + * */ + async downloadAndUnzipFolder( + filesRequired, + folder, + onDownloadProgress, + onUnzipProgress, + ) { + let current_file; + try { + for (let i = 0; i < folder.length; i++) { + const file = folder[i]; + current_file = file.path; + if (filesRequired.includes(file.name) || file.unzip) { + const blob = await this.download(file.path, (value, total) => { + onDownloadProgress(value, total, file.name); + }); + if (file.unzip) { + const zipReader = new ZipReader(new BlobReader(blob)); + const filesEntries = await zipReader.getEntries(); + for (let i = 0; i < filesEntries.length; i++) { + const unzippedEntry = await this.getFileFromZip( + filesEntries[i], + (value, total) => { + onUnzipProgress(value, total, filesEntries[i].filename); + }, + ); + let filename = this.getMappedName( + filesEntries[i].filename, + file.mapping, + ); + if (filesRequired.includes(filename)) { + await this.setInDBStore(unzippedEntry.blob, filename); + this.stored[filename] = true; + } + } + await zipReader.close(); + } else { + await this.setInDBStore(blob, file.name); + this.stored[file.name] = true; + } + } + } + } catch (e) { + throw new Error( + `downloadAndUnzipFolder Error
current_file ${current_file}
${e.message || e}`, + ); + } + } + + async getFileFromZip(file, onProgress) { + const name = file.filename; + const blob = await file.getData(new BlobWriter(), { + onprogress: (value, total) => { + onProgress(value, total, name); + }, + useWebWorkers: false, + }); + return { + name, + blob, + }; + } + + getMappedName(filename, map) { + if (!map) { + return filename; + } + + console.log(map); + for (const [regex, newFilename] of Object.entries(map)) { + let re = new RegExp(regex); + if (filename.match(re)) { + return newFilename; + } + } + return filename; + } + + /** + * @param name + * @returns {} + * It does not launch download (downloads are launched with downloadFolder) + * this function retrieve the promise linked to the fileName + */ + async getFile(name) { + const file = this.stored[name]; + if (!file) { + throw new Error(`File ${name} was not previously downloaded`); + } + return await this.getFromDBStore(name); + } + + /* + * getData from a zip file + * */ + async getData(dbFile, fileEntry, onProgress) { + const _zip = new BlobWriter(); + const blob = await fileEntry.getData(_zip, { + onprogress: (value, total) => { + onProgress(value, total, dbFile); + }, + onend: () => {}, + useWebWorkers: true, + }); + return blob; + } + + async download(path, onProgress) { + try { + const buffers = await this.fetch( + { + url: path, + chunkSize: 16 * 1024 * 1024, + poolLimit: 1, + }, + onProgress, + ); + + //let totalSize = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0); + const ret = new Blob(buffers); + return ret; + } catch (e) { + throw new Error(`${e.message || e}`); + } + } + + async clearDBStore() { + const store = this.db + .transaction(DB_NAME, "readwrite") + .objectStore(DB_NAME); + store.clear(); + } + + async setInDBStore(blob, key) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction(DB_NAME, "readwrite"); + const store = transaction.objectStore(DB_NAME); + const request = store.put(blob, key); + + request.onsuccess = () => { + resolve(); + }; + + request.onerror = (event) => { + reject(event.target.error); + }; + }); + } + + async getFromDBStore(key) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction(DB_NAME, "readonly"); + const store = transaction.objectStore(DB_NAME); + const request = store.get(key); + request.onsuccess = function (event) { + const result = event.target.result; + if (result) { + resolve(result); + } else { + resolve(null); + } + }; + request.onerror = function (event) { + reject(event.target.error); + }; + }); + } + + async openDBStore() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(DB_NAME, DB_VERSION); + request.onerror = reject; + request.onupgradeneeded = function (event) { + const db = event.target.result; + db.createObjectStore(DB_NAME, { autoIncrement: false }); + }; + request.onsuccess = function (event) { + resolve(event.target.result); + }; + }); + } + + concatenate(arrays) { + if (!arrays.length) return null; + let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); + let result = new Uint8Array(totalLength); + let length = 0; + for (let array of arrays) { + result.set(array, length); + length += array.length; + } + return result; + } + + async getContentLength(url) { + const response = await ky.head(url); + const contentLength = response.headers.get("content-length"); + return parseInt(contentLength, 10); + } + + async fetch({ url, chunkSize }, onProgress) { + try { + const contentLength = await this.getContentLength(url); + const totalChunks = Math.ceil(contentLength / chunkSize); + const buffers = []; + + for (let i = 0; i < totalChunks; i++) { + const start = i * chunkSize; + const end = Math.min(start + chunkSize - 1, contentLength - 1); + try { + const response = await ky.get(url, { + headers: { + Range: `bytes=${start}-${end}`, + }, + }); + if (!response.ok) { + throw new Error( + `Cannot download chunk (1) ${i + 1}: ${response.status} ${response.statusText}`, + ); + } + + const chunk = await response.arrayBuffer(); + buffers.push(chunk); + onProgress(start + chunk.byteLength, contentLength); + } catch (chunkError) { + throw new Error( + `Cannot download chunk (2) ${i + 1} ${chunkError.message || chunkError}`, + ); + } + } + return buffers; + } catch (error) { + throw new Error(`Download fails ${error.message || error}`); + } + } +} -- GitLab From 5160a557e40e28be4265756bb3d285d499283b89 Mon Sep 17 00:00:00 2001 From: Nicolas Melin Date: Fri, 14 Feb 2025 11:04:28 +0100 Subject: [PATCH 15/19] ya-webadb 1.1.0 integration --- app/package-lock.json | 1 + app/package.json | 1 + app/src/controller/adb-sideload.js | 912 -------------------- app/src/controller/device/adb.class.js | 6 +- app/src/controller/device/recovery.class.js | 252 ++++-- 5 files changed, 203 insertions(+), 969 deletions(-) delete mode 100644 app/src/controller/adb-sideload.js diff --git a/app/package-lock.json b/app/package-lock.json index 9757828..929996e 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -13,6 +13,7 @@ "@yume-chan/adb": "1.1.0", "@yume-chan/adb-credential-web": "1.1.0", "@yume-chan/adb-daemon-webusb": "1.1.0", + "@yume-chan/stream-extra": "1.0.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" }, diff --git a/app/package.json b/app/package.json index de88fc5..a009990 100644 --- a/app/package.json +++ b/app/package.json @@ -23,6 +23,7 @@ "@yume-chan/adb": "1.1.0", "@yume-chan/adb-daemon-webusb": "1.1.0", "@yume-chan/adb-credential-web": "1.1.0", + "@yume-chan/stream-extra": "1.0.0", "@zip.js/zip.js": "^2.7.54", "ky": "^1.7.4" } diff --git a/app/src/controller/adb-sideload.js b/app/src/controller/adb-sideload.js deleted file mode 100644 index 8af8beb..0000000 --- a/app/src/controller/adb-sideload.js +++ /dev/null @@ -1,912 +0,0 @@ -import { AdbCommand, AutoResetEvent } from "@yume-chan/adb"; - -import { AsyncOperationManager } from "@yume-chan/async"; - -// ADB Message structures -// There are probably equivalents in the new adb library. - -export class MessageClass { - constructor(header, data) { - this.header = header; - this.data = data; - } - - static checksum(dataView) { - let sum = 0; - for (let i = 0; i < dataView.byteLength; i++) { - sum += dataView.getUint8(i); - } - return sum & 0xffffffff; - } -} - -export class MessageHeader { - constructor(cmd, arg0, arg1, length, checksum) { - this.cmd = cmd; - this.arg0 = arg0; - this.arg1 = arg1; - this.length = length; - this.checksum = checksum; - } - - toDataView() { - const view = new DataView(new ArrayBuffer(24)); - const rawCmd = this.encodeCmd(this.cmd); - const magic = rawCmd ^ 0xffffffff; - view.setUint32(0, rawCmd, true); - view.setUint32(4, this.arg0, true); - view.setUint32(8, this.arg1, true); - view.setUint32(12, this.length, true); - view.setUint32(16, this.checksum, true); - view.setUint32(20, magic, true); - return view; - } - - encodeCmd(cmd) { - const encoder = new TextEncoder(); - const buffer = encoder.encode(cmd).buffer; - const view = new DataView(buffer); - return view.getUint32(0, true); - } -} - -/* -Classes and utilities from an old adb lib. -There are probably equivalents in the new adb library. -It would be interesting to find them, but it may be rather difficult (name change and perhaps synthetics). -*/ - -const BackingField = Symbol("BackingField"); -function getBackingField(object, field) { - return object[BackingField][field]; -} -function setBackingField(object, field, value) { - object[BackingField][field] = value; -} -function defineSimpleAccessors(object, field) { - Object.defineProperty(object, field, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field); - }, - set(value) { - setBackingField(object, field, value); - }, - }); -} - -var Array; -(function (Array) { - let SubType; - (function (SubType) { - SubType[(SubType["ArrayBuffer"] = 0)] = "ArrayBuffer"; - SubType[(SubType["String"] = 1)] = "String"; - })((SubType = Array.SubType || (Array.SubType = {}))); - function initialize(object, field, value) { - switch (field.subType) { - case SubType.ArrayBuffer: - Object.defineProperty(object, field.name, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field.name).buffer; - }, - set(buffer) { - setBackingField(object, field.name, { buffer }); - }, - }); - break; - case SubType.String: - Object.defineProperty(object, field.name, { - configurable: true, - enumerable: true, - get() { - return getBackingField(object, field.name).string; - }, - set(string) { - setBackingField(object, field.name, { string }); - }, - }); - break; - default: - throw new Error("Unknown type"); - } - setBackingField(object, field.name, value); - } - Array.initialize = initialize; -})(Array || (Array = {})); - -const registry = {}; -function getFieldTypeDefinition(type) { - return registry[type]; -} -function registerFieldTypeDefinition(_field, _initExtra, methods) { - registry[methods.type] = methods; -} - -var FieldType; -(function (FieldType) { - FieldType[(FieldType["Number"] = 0)] = "Number"; - FieldType[(FieldType["FixedLengthArray"] = 1)] = "FixedLengthArray"; - FieldType[(FieldType["VariableLengthArray"] = 2)] = "VariableLengthArray"; -})(FieldType || (FieldType = {})); - -function placeholder() { - return undefined; -} - -registerFieldTypeDefinition(placeholder(), placeholder(), { - type: FieldType.FixedLengthArray, - async deserialize({ context, field }) { - const buffer = await context.read(field.options.length); - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: buffer }; - case Array.SubType.String: - return { - value: context.decodeUtf8(buffer), - extra: buffer, - }; - default: - throw new Error("Unknown type"); - } - }, - getSize({ field }) { - return field.options.length; - }, - initialize({ extra, field, object, value }) { - const backingField = {}; - if (typeof value === "string") { - backingField.string = value; - if (extra) { - backingField.buffer = extra; - } - } else { - backingField.buffer = value; - } - Array.initialize(object, field, backingField); - }, - serialize({ context, dataView, field, object, offset }) { - var _a; - const backingField = getBackingField(object, field.name); - (_a = backingField.buffer) !== null && _a !== void 0 - ? _a - : (backingField.buffer = context.encodeUtf8(backingField.string)); - new Uint8Array(dataView.buffer).set( - new Uint8Array(backingField.buffer), - offset, - ); - }, -}); - -var Number$1; -(function (Number) { - let SubType; - (function (SubType) { - SubType[(SubType["Uint8"] = 0)] = "Uint8"; - SubType[(SubType["Uint16"] = 1)] = "Uint16"; - SubType[(SubType["Int32"] = 2)] = "Int32"; - SubType[(SubType["Uint32"] = 3)] = "Uint32"; - SubType[(SubType["Uint64"] = 4)] = "Uint64"; - SubType[(SubType["Int64"] = 5)] = "Int64"; - })((SubType = Number.SubType || (Number.SubType = {}))); - Number.SizeMap = { - [SubType.Uint8]: 1, - [SubType.Uint16]: 2, - [SubType.Int32]: 4, - [SubType.Uint32]: 4, - [SubType.Uint64]: 8, - [SubType.Int64]: 8, - }; - Number.DataViewGetterMap = { - [SubType.Uint8]: "getUint8", - [SubType.Uint16]: "getUint16", - [SubType.Int32]: "getInt32", - [SubType.Uint32]: "getUint32", - [SubType.Uint64]: "getBigUint64", - [SubType.Int64]: "getBigInt64", - }; - Number.DataViewSetterMap = { - [SubType.Uint8]: "setUint8", - [SubType.Uint16]: "setUint16", - [SubType.Int32]: "setInt32", - [SubType.Uint32]: "setUint32", - [SubType.Uint64]: "setBigUint64", - [SubType.Int64]: "setBigInt64", - }; -})(Number$1 || (Number$1 = {})); -registerFieldTypeDefinition(placeholder(), undefined, { - type: FieldType.Number, - getSize({ field }) { - return Number$1.SizeMap[field.subType]; - }, - async deserialize({ context, field, options }) { - const buffer = await context.read(Number$1.SizeMap[field.subType]); - const view = new DataView(buffer); - const value = view[Number$1.DataViewGetterMap[field.subType]]( - 0, - options.littleEndian, - ); - return { value }; - }, - serialize({ dataView, field, object, offset, options }) { - dataView[Number$1.DataViewSetterMap[field.subType]]( - offset, - object[field.name], - options.littleEndian, - ); - }, -}); - -var VariableLengthArray; -(function (VariableLengthArray) { - let EmptyBehavior; - (function (EmptyBehavior) { - EmptyBehavior[(EmptyBehavior["Undefined"] = 0)] = "Undefined"; - EmptyBehavior[(EmptyBehavior["Empty"] = 1)] = "Empty"; - })( - (EmptyBehavior = - VariableLengthArray.EmptyBehavior || - (VariableLengthArray.EmptyBehavior = {})), - ); - function getLengthBackingField(object, field) { - return getBackingField(object, field.options.lengthField); - } - VariableLengthArray.getLengthBackingField = getLengthBackingField; - function setLengthBackingField(object, field, value) { - setBackingField(object, field.options.lengthField, value); - } - VariableLengthArray.setLengthBackingField = setLengthBackingField; - function initialize(object, field, value, context) { - Array.initialize(object, field, value); - const descriptor = Object.getOwnPropertyDescriptor(object, field.name); - delete object[field.name]; - switch (field.subType) { - case Array.SubType.ArrayBuffer: - Object.defineProperty(object, field.name, { - ...descriptor, - set(buffer) { - var _a; - descriptor.set.call(object, buffer); - setLengthBackingField( - object, - field, - (_a = - buffer === null || buffer === void 0 - ? void 0 - : buffer.byteLength) !== null && _a !== void 0 - ? _a - : 0, - ); - }, - }); - delete object[field.options.lengthField]; - Object.defineProperty(object, field.options.lengthField, { - configurable: true, - enumerable: true, - get() { - return getLengthBackingField(object, field); - }, - }); - break; - case Array.SubType.String: - Object.defineProperty(object, field.name, { - ...descriptor, - set(string) { - descriptor.set.call(object, string); - if (string) { - setLengthBackingField(object, field, undefined); - } else { - setLengthBackingField(object, field, 0); - } - }, - }); - delete object[field.options.lengthField]; - Object.defineProperty(object, field.options.lengthField, { - configurable: true, - enumerable: true, - get() { - let value = getLengthBackingField(object, field); - if (value === undefined) { - const backingField = getBackingField(object, field.name); - const buffer = context.encodeUtf8(backingField.string); - backingField.buffer = buffer; - value = buffer.byteLength; - setLengthBackingField(object, field, value); - } - return value; - }, - }); - break; - default: - throw new Error("Unknown type"); - } - setBackingField(object, field.name, value); - if (value.buffer) { - setLengthBackingField(object, field, value.buffer.byteLength); - } - } - VariableLengthArray.initialize = initialize; -})(VariableLengthArray || (VariableLengthArray = {})); -registerFieldTypeDefinition(placeholder(), placeholder(), { - type: FieldType.VariableLengthArray, - async deserialize({ context, field, object }) { - let length = object[field.options.lengthField]; - if (typeof length === "string") { - length = Number.parseInt(length, 10); - } - if (length === 0) { - if ( - field.options.emptyBehavior === VariableLengthArray.EmptyBehavior.Empty - ) { - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: new ArrayBuffer(0) }; - case Array.SubType.String: - return { value: "", extra: new ArrayBuffer(0) }; - default: - throw new Error("Unknown type"); - } - } else { - return { value: undefined }; - } - } - const buffer = await context.read(length); - switch (field.subType) { - case Array.SubType.ArrayBuffer: - return { value: buffer }; - case Array.SubType.String: - return { - value: context.decodeUtf8(buffer), - extra: buffer, - }; - default: - throw new Error("Unknown type"); - } - }, - getSize() { - return 0; - }, - getDynamicSize({ field, object }) { - return object[field.options.lengthField]; - }, - initialize({ context, extra, field, object, value }) { - const backingField = {}; - if (typeof value === "string") { - backingField.string = value; - if (extra) { - backingField.buffer = extra; - } - } else { - backingField.buffer = value; - } - Array.initialize(object, field, backingField); - VariableLengthArray.initialize(object, field, backingField, context); - }, - serialize({ dataView, field, object, offset }) { - const backingField = getBackingField(object, field.name); - new Uint8Array(dataView.buffer).set( - new Uint8Array(backingField.buffer), - offset, - ); - }, -}); - -const StructDefaultOptions = { - littleEndian: false, -}; - -class Struct { - constructor(options = StructDefaultOptions) { - this._size = 0; - this.fields = []; - this._extra = {}; - this.array = (name, type, options) => { - if ("length" in options) { - return this.field({ - type: FieldType.FixedLengthArray, - name, - subType: type, - options: options, - }); - } else { - return this.field({ - type: FieldType.VariableLengthArray, - name, - subType: type, - options: options, - }); - } - }; - this.arrayBuffer = (name, options) => { - return this.array(name, Array.SubType.ArrayBuffer, options); - }; - this.string = (name, options) => { - return this.array(name, Array.SubType.String, options); - }; - this.options = { ...StructDefaultOptions, ...options }; - } - get size() { - return this._size; - } - clone() { - const result = new Struct(this.options); - result.fields = this.fields.slice(); - result._size = this._size; - result._extra = this._extra; - result._afterParsed = this._afterParsed; - return result; - } - field(field) { - const result = this.clone(); - result.fields.push(field); - const definition = getFieldTypeDefinition(field.type); - const size = definition.getSize({ field, options: this.options }); - result._size += size; - return result; - } - number(name, type, options = {}, _typescriptType) { - return this.field({ - type: FieldType.Number, - name, - subType: type, - options, - }); - } - uint8(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint8, options, _typescriptType); - } - uint16(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint16, options, _typescriptType); - } - int32(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Int32, options, _typescriptType); - } - uint32(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint32, options, _typescriptType); - } - uint64(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Uint64, options, _typescriptType); - } - int64(name, options = {}, _typescriptType) { - return this.number(name, Number$1.SubType.Int64, options, _typescriptType); - } - extra(value) { - const result = this.clone(); - result._extra = { - ...result._extra, - ...Object.getOwnPropertyDescriptors(value), - }; - return result; - } - afterParsed(callback) { - const result = this.clone(); - result._afterParsed = callback; - return result; - } - initializeField(context, field, fieldTypeDefinition, object, value, extra) { - if (fieldTypeDefinition.initialize) { - fieldTypeDefinition.initialize({ - context, - extra, - field, - object, - options: this.options, - value, - }); - } else { - setBackingField(object, field.name, value); - defineSimpleAccessors(object, field.name); - } - } - create(init, context) { - const object = { - [BackingField]: {}, - }; - Object.defineProperties(object, this._extra); - for (const field of this.fields) { - const fieldTypeDefinition = getFieldTypeDefinition(field.type); - this.initializeField( - context, - field, - fieldTypeDefinition, - object, - init[field.name], - ); - } - return object; - } - async deserialize(context) { - const object = { - [BackingField]: {}, - }; - Object.defineProperties(object, this._extra); - for (const field of this.fields) { - const fieldTypeDefinition = getFieldTypeDefinition(field.type); - const { value, extra } = await fieldTypeDefinition.deserialize({ - context, - field, - object, - options: this.options, - }); - this.initializeField( - context, - field, - fieldTypeDefinition, - object, - value, - extra, - ); - } - if (this._afterParsed) { - const result = this._afterParsed.call(object, object); - if (result) { - return result; - } - } - return object; - } - serialize(init, context) { - const object = this.create(init, context); - let size = this._size; - let fieldSize = []; - for (let i = 0; i < this.fields.length; i += 1) { - const field = this.fields[i]; - const type = getFieldTypeDefinition(field.type); - if (type.getDynamicSize) { - fieldSize[i] = type.getDynamicSize({ - context, - field, - object, - options: this.options, - }); - size += fieldSize[i]; - } else { - fieldSize[i] = type.getSize({ field, options: this.options }); - } - } - const buffer = new ArrayBuffer(size); - const dataView = new DataView(buffer); - let offset = 0; - for (let i = 0; i < this.fields.length; i += 1) { - const field = this.fields[i]; - const type = getFieldTypeDefinition(field.type); - type.serialize({ - context, - dataView, - field, - object, - offset, - options: this.options, - }); - offset += fieldSize[i]; - } - return buffer; - } -} - -class BufferedStream { - constructor(stream) { - this.stream = stream; - } - async read(length) { - let array; - let index; - if (this.buffer) { - const buffer = this.buffer; - if (buffer.byteLength > length) { - this.buffer = buffer.subarray(length); - return buffer.slice(0, length).buffer; - } - array = new Uint8Array(length); - array.set(buffer); - index = buffer.byteLength; - this.buffer = undefined; - } else { - const buffer = await this.stream.read(length); - if (buffer.byteLength === length) { - return buffer; - } - if (buffer.byteLength > length) { - this.buffer = new Uint8Array(buffer, length); - return buffer.slice(0, length); - } - array = new Uint8Array(length); - array.set(new Uint8Array(buffer), 0); - index = buffer.byteLength; - } - while (index < length) { - const left = length - index; - const buffer = await this.stream.read(left); - if (buffer.byteLength > left) { - array.set(new Uint8Array(buffer, 0, left), index); - this.buffer = new Uint8Array(buffer, left); - return array.buffer; - } - array.set(new Uint8Array(buffer), index); - index += buffer.byteLength; - } - return array.buffer; - } - close() { - var _a, _b; - (_b = (_a = this.stream).close) === null || _b === void 0 - ? void 0 - : _b.call(_a); - } -} - -const AdbPacketWithoutPayload = new Struct({ littleEndian: true }) - .uint32("command", undefined) - .uint32("arg0") - .uint32("arg1") - .uint32("payloadLength") - .uint32("checksum") - .int32("magic"); -const AdbPacketStruct = AdbPacketWithoutPayload.arrayBuffer("payload", { - lengthField: "payloadLength", -}).afterParsed((value) => { - if (value[BackingField].magic !== value.magic) { - throw new Error("Invalid command"); - } -}); -let AdbPacket; -(function (AdbPacket) { - function create(init, calculateChecksum, backend) { - let checksum; - if (calculateChecksum && init.payload) { - const array = new Uint8Array(init.payload); - checksum = array.reduce((result, item) => result + item, 0); - } else { - checksum = 0; - } - return AdbPacketStruct.create( - { - ...init, - checksum, - magic: init.command ^ 0xffffffff, - }, - backend, - ); - } - AdbPacket.create = create; - async function read(backend) { - let buffer = await backend.read(24); - if (buffer.byteLength !== 24) { - // Maybe it's a payload from last connection. - // Ignore and try again - buffer = await backend.read(24); - } - let bufferUsed = false; - const stream = new BufferedStream({ - read(length) { - if (!bufferUsed) { - bufferUsed = true; - return buffer; - } - return backend.read(length); - }, - }); - return AdbPacketStruct.deserialize({ - read: stream.read.bind(stream), - decodeUtf8: backend.decodeUtf8.bind(backend), - encodeUtf8: backend.encodeUtf8.bind(backend), - }); - } - AdbPacket.read = read; - async function write(packet, backend) { - // Write payload separately to avoid an extra copy - await backend.write(AdbPacketWithoutPayload.serialize(packet, backend)); - if (packet.payload) { - await backend.write(packet.payload); - } - } - AdbPacket.write = write; -})(AdbPacket || (AdbPacket = {})); - -const WebUsbDeviceFilter = { - classCode: 0xff, - subclassCode: 0x42, - protocolCode: 1, -}; - -const Utf8Encoder = new TextEncoder(); -const Utf8Decoder = new TextDecoder(); -function encodeUtf8(input) { - return Utf8Encoder.encode(input); -} -function decodeUtf8(buffer) { - return Utf8Decoder.decode(buffer); -} - -//////////////////////////////////////////////////////// -// Dedicated adb and transport function for sideload. / -//////////////////////////////////////////////////////// - -class AdbWebBackendSideload { - constructor(device) { - this._device = device; - } - - async connect() { - var _a; - if (!this._device.opened) { - await this._device.open(); - } - - for (const configuration of this._device.configurations) { - for (const interface_ of configuration.interfaces) { - for (const alternate of interface_.alternates) { - if ( - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && - alternate.interfaceClass === WebUsbDeviceFilter.classCode && - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode - ) { - if ( - ((_a = this._device.configuration) === null || _a === void 0 - ? void 0 - : _a.configurationValue) !== configuration.configurationValue - ) { - await this._device.selectConfiguration( - configuration.configurationValue, - ); - } - if (!interface_.claimed) { - await this._device.claimInterface(interface_.interfaceNumber); - } - if ( - interface_.alternate.alternateSetting !== - alternate.alternateSetting - ) { - await this._device.selectAlternateInterface( - interface_.interfaceNumber, - alternate.alternateSetting, - ); - } - for (const endpoint of alternate.endpoints) { - switch (endpoint.direction) { - case "in": - this._inEndpointNumber = endpoint.endpointNumber; - if (this._outEndpointNumber !== undefined) { - return; - } - break; - case "out": - this._outEndpointNumber = endpoint.endpointNumber; - if (this._inEndpointNumber !== undefined) { - return; - } - break; - } - } - } - } - } - } - throw new Error("Unknown error"); - } - - async write(buffer) { - await this._device.transferOut(this._outEndpointNumber, buffer); - } - async read(length) { - const result = await this._device.transferIn( - this._inEndpointNumber, - length, - ); - if (result.status === "stall") { - await this._device.clearHalt("in", this._inEndpointNumber); - } - const { buffer } = result.data; - return buffer; - } - - encodeUtf8(input) { - return encodeUtf8(input); - } - decodeUtf8(buffer) { - return decodeUtf8(buffer); - } -} - -export class AdbSideload { - // This one is dedicated for adb sidelaod - constructor(backend) { - this._connected = false; - this.backend = new AdbWebBackendSideload(backend); - this.sendLock = new AutoResetEvent(); - this.initializers = new AsyncOperationManager(1); - } - get connected() { - return this._connected; - } - async connect() { - var _a, _b; - await ((_b = (_a = this.backend).connect) === null || _b === void 0 - ? void 0 - : _b.call(_a)); - this.calculateChecksum = true; - this.appendNullToServiceString = true; - const version = 0x01000001; - const maxPayloadSize = 0x100000; - - await this.sendPacket( - AdbCommand.Connect, - version, - maxPayloadSize, - "host::\0", - ); - const r = await AdbPacket.read(this.backend); - if (r.command == AdbCommand.Connect) { - //All is fine - } else { - throw new Error("Adb sideload connection error"); - } - } - - spawn(command, ...args) { - return this.createStream(`shell:${command} ${args.join(" ")}`); - } - exec(command, ...args) { - return this.createStreamAndReadAll(`shell:${command} ${args.join(" ")}`); - } - async getProp(key) { - const output = await this.exec("getprop", key); - return output.trim(); - } - - async createStream(service) { - const localId = 1; - service += "\0"; - let remoteId; - await this.sendPacket(AdbCommand.Open, localId, 0, service); - const r = await AdbPacket.read(this.backend); - if (r.command == AdbCommand.Okay) { - remoteId = r.arg0; - return { localId: localId, remoteId: remoteId }; - } else { - throw new Error("Adb sideload create stream error"); - } - } - - async dispose() { - this.packetDispatcher.dispose(); - await this.backend.dispose(); - } - async sendPacket(packetOrCommand, arg0, arg1, payload) { - var _a; - let init; - if (arguments.length === 1) { - init = packetOrCommand; - } else { - init = { - command: packetOrCommand, - arg0: arg0, - arg1: arg1, - payload: - typeof payload === "string" - ? this.backend.encodeUtf8(payload) - : payload, - }; - } - if (init.payload && init.payload.byteLength > this.maxPayloadSize) { - throw new Error("payload too large"); - } - try { - this.sendLock.wait(); - const packet = AdbPacket.create( - init, - this.calculateChecksum, - this.backend, - ); - await AdbPacket.write(packet, this.backend); - } catch (e) { - console.log("error send sendPacket ", e); - } finally { - this.sendLock.notifyOne(); - } - } -} - -//////////////////////////////// diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index c6a6b65..1e84468 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -32,7 +32,7 @@ export class ADB extends Device { try { console.log("debug adb connect"); - const adbDaemonWebUsbDevice = + let adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ if (typeof adbDaemonWebUsbDevice == "undefined") { throw new Error("No device connected (1)"); @@ -40,8 +40,7 @@ export class ADB extends Device { let connection; try { - connection = - await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ } catch (err) { const devices = await Manager.getDevices(); if (!devices.length) { @@ -70,6 +69,7 @@ export class ADB extends Device { WDebug.log(">Device (codename)", adb.transport.banner.device); // codemane WDebug.log("----------------------------------"); } catch (e) { + console.error(e); this.device = null; throw new Error(`Cannot connect ADB ${e.message || e}`); } diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index 8568a77..49418b1 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -1,8 +1,8 @@ -import { ADB } from "./adb.class.js"; +import { AdbCommand, calculateChecksum } from '@yume-chan/adb'; +import { Consumable } from "@yume-chan/stream-extra"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; - -import { AdbSideload, MessageClass, MessageHeader } from "../adb-sideload.js"; +import { ADB } from './adb.class.js'; export class Recovery extends Device { constructor(device) { @@ -31,15 +31,101 @@ export class Recovery extends Device { try { if (this.device && this.device.isConnected) { WDebug.log("Connect recovery the device is connected"); - } else { - const adbDaemonWebUsbDevice = - await ADB.Manager.requestDevice(); /*AdbDaemonWebUsbDevice*/ - const adbDevice = new AdbSideload(adbDaemonWebUsbDevice.raw, null); - WDebug.log("adbDevice = ", adbDevice); - await adbDevice.connect(); - - this.device = adbDaemonWebUsbDevice; - this.webusb = adbDevice; + } + else { + let adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); + if (typeof adbDaemonWebUsbDevice == "undefined") { + throw new Error("No device connected (1)"); + } + + try { + this.connection = await adbDaemonWebUsbDevice.connect(); + } + catch (err) { + const devices = await ADB.Manager.getDevices(); + if (!devices.length) { + throw new Error("No device connected (2)"); + } + adbDaemonWebUsbDevice = devices[0]; // Assume one device is connected + } + + this.adbDaemonWebUsbDevice = adbDaemonWebUsbDevice.raw; + + var _a; + const WebUsbDeviceFilter = { + classCode: 0xff, + subclassCode: 0x42, + protocolCode: 1, + }; + + // Need this code here to have a _inEndpointNumber & _outEndpointNumber defined + outerLoop: + for (const configuration of this.adbDaemonWebUsbDevice.configurations) { + for (const interface_ of configuration.interfaces) { + for (const alternate of interface_.alternates) { + if ( + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && + alternate.interfaceClass === WebUsbDeviceFilter.classCode && + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode + ) { + if ( + ((_a = this.adbDaemonWebUsbDevice.configuration) === null || _a === void 0 + ? void 0 + : _a.configurationValue) !== configuration.configurationValue + ) { + await this.adbDaemonWebUsbDevice.selectConfiguration( + configuration.configurationValue, + ); + } + if (!interface_.claimed) { + await this.adbDaemonWebUsbDevice.claimInterface(interface_.interfaceNumber); + } + if ( + interface_.alternate.alternateSetting !== + alternate.alternateSetting + ) { + await this.adbDaemonWebUsbDevice.selectAlternateInterface( + interface_.interfaceNumber, + alternate.alternateSetting, + ); + } + for (const endpoint of alternate.endpoints) { + switch (endpoint.direction) { + case "in": + this._inEndpointNumber = endpoint.endpointNumber; + if (this._outEndpointNumber !== undefined) { + break outerLoop; + } + break; + case "out": + this._outEndpointNumber = endpoint.endpointNumber; + if (this._inEndpointNumber !== undefined) { + break outerLoop; + } + break; + } + } + } + } + } + } + + const version = 0x01000001; + const maxPayloadSize = 0x100000; + await this.sendPacket({ + command: AdbCommand.Connect, + arg0: version, + arg1: maxPayloadSize, + payload: this.encodeUtf8("host::\0"), + }); + const r = await this.readOnDevice(); + + if (r.value.command == AdbCommand.Connect) { + //All is fine + } + else { + throw new Error("Adb sideload connection error"); + } } } catch (e) { this.device = null; @@ -47,6 +133,58 @@ export class Recovery extends Device { } } + encodeUtf8(input) { + return new TextEncoder().encode(input); + } + + decodeUtf8(buffer) { + return new TextDecoder().decode(buffer); + } + + async readOnDevice() { + const reader = await this.connection?.readable?.getReader(); + if(!reader) { + throw new Error("readOnDevice() : Unable to read on device"); + } + const r = await reader.read(); + reader.releaseLock(); + return r; + } + + async sendPacket(init) { + const writer = this.connection?.writable?.getWriter(); + if(!writer) { + throw new Error("sendPacket() : Unable to write on device"); + } + init.checksum = calculateChecksum(init.payload); + init.magic = init.command ^ 0xffffffff; + await Consumable.WritableStream.write( + writer, + init, + ); + writer.releaseLock(); + } + + async createStream(service) { + const localId = 1; // Assume one device is connected + service += "\0"; + let remoteId; + await this.sendPacket({ + command: AdbCommand.Open, + arg0: localId, + arg1: 0, + payload: this.encodeUtf8(service), + }); + const r = await this.readOnDevice(); + if (r.value.command == AdbCommand.Okay) { + remoteId = r.value.arg0; + return { localId: localId, remoteId: remoteId }; + } + else { + throw new Error("Adb sideload create stream error"); + } + } + async sideload(blob) { try { await this.adbOpen(blob); @@ -68,32 +206,32 @@ export class Recovery extends Device { } async adbOpen(blob) { - const encoder = new TextEncoder(); const decoder = new TextDecoder(); - const MAX_PAYLOAD = 0x40000; + const MAX_PAYLOAD = 0x40000; // This value commes from adb C function const fileSize = blob.size; const service = `sideload-host:${fileSize}:${MAX_PAYLOAD}`; //sideload-host:1381604186:262144 - let data = new DataView(encoder.encode("" + service + "\0").buffer); - this.stream = await this.webusb.createStream(service); // Send Open message and receive OKAY. - - let checksum = MessageClass.checksum(data); + this.stream = await this.createStream(service); // Send Open message and receive OKAY. const localId = this.stream.localId; const remoteId = this.stream.remoteId; - let header = new MessageHeader("OKAY", localId, remoteId, 0, checksum); - let receivedData, message; - await this.webusb.backend.write(header.toDataView().buffer); - let r = await this.webusb.backend.read(24); - let v = Array.from(new Uint8Array(r)) - .map((byte) => String.fromCharCode(byte)) - .join(""); - if (v[0] == "W" && v[1] == "R" && v[2] == "T" && v[3] == "E") { - receivedData = await this.webusb.backend.read(8); - message = new MessageClass(header, receivedData); - } else { + let message; + await this.sendPacket({ + command: AdbCommand.Okay, + arg0: localId, + arg1: remoteId, + payload: this.encodeUtf8(service), + }); + const r = await this.readOnDevice(); + + if (r.value.command == AdbCommand.Write) { + message = { + data: r.value.payload, + }; + } + else { throw new Error("Write OKAY Failed (init)"); } @@ -102,8 +240,10 @@ export class Recovery extends Device { const block = Number(res); if (isNaN(block) && res === "DONEDONE") { + WDebug.log("DONEDONE"); break; - } else { + } + else { if (block % 10 == 0) { WDebug.log("Sideloading " + block); } @@ -120,34 +260,38 @@ export class Recovery extends Device { } let slice = blob.slice(offset, offset + to_write); - header = new MessageHeader("WRTE", localId, remoteId, to_write, checksum); let buff = await slice.arrayBuffer(); - await this.webusb.backend.write(header.toDataView().buffer); - await this.webusb.backend.write(buff); - r = await this.webusb.backend.read(24); - v = Array.from(new Uint8Array(r)) - .map((byte) => String.fromCharCode(byte)) - .join(""); - //test OKAY - if (v[0] == "O" && v[1] == "K" && v[2] == "A" && v[3] == "Y") { - header = new MessageHeader("OKAY", localId, remoteId, 0, checksum); - await this.webusb.backend.write(header.toDataView().buffer); - r = await this.webusb.backend.read(24); - v = Array.from(new Uint8Array(r)) - .map((byte) => String.fromCharCode(byte)) - .join(""); - //Test WRTE - if (v[0] == "W" && v[1] == "R" && v[2] == "T" && v[3] == "E") { - receivedData = await this.webusb.backend.read(8); - message = new MessageClass(header, receivedData); - } else { - console.error("Error sideload (A)", v); + await this.sendPacket({ + command: AdbCommand.Write, + arg0: localId, + arg1: remoteId, + payload: new Uint8Array(buff), + }); + const r = await this.readOnDevice(); + + if (r.value.command == AdbCommand.Okay) { + await this.sendPacket({ + command: AdbCommand.Okay, + arg0: localId, + arg1: remoteId, + payload: this.encodeUtf8(service), + }); + const r = await this.readOnDevice(); + + if (r.value.command == AdbCommand.Write) { + message = { + data: r.value.payload, + }; + } + else { + console.error("Error sideload (A)", r); throw new Error(`WRTE Failed ${block}`); } - } else { - console.error("Error sideload (B)", v); - throw new Error(`OKAY Failed ${block}`); + } + else { + console.error("Error sideload (B)", r); + throw new Error("Write OKAY Failed (init)"); } } return true; -- GitLab From b09f8ba6696fd3b7934e0f2fc54b1779dfafc776 Mon Sep 17 00:00:00 2001 From: Nicolas Melin Date: Fri, 14 Feb 2025 11:12:11 +0100 Subject: [PATCH 16/19] Fix lint + prettier --- app/src/controller/device/adb.class.js | 6 ++- app/src/controller/device/recovery.class.js | 55 ++++++++++----------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index 1e84468..7ea5791 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -40,9 +40,11 @@ export class ADB extends Device { let connection; try { - connection = await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ + connection = + await adbDaemonWebUsbDevice.connect(); /*AdbDaemonWebUsbConnection*/ } catch (err) { - const devices = await Manager.getDevices(); + console.error(err); + const devices = await ADB.Manager.getDevices(); if (!devices.length) { throw new Error("No device connected (2)"); } diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index 49418b1..96bec9e 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -1,8 +1,8 @@ -import { AdbCommand, calculateChecksum } from '@yume-chan/adb'; +import { AdbCommand, calculateChecksum } from "@yume-chan/adb"; import { Consumable } from "@yume-chan/stream-extra"; import { Device } from "./device.class.js"; import { WDebug } from "../../debug.js"; -import { ADB } from './adb.class.js'; +import { ADB } from "./adb.class.js"; export class Recovery extends Device { constructor(device) { @@ -31,8 +31,7 @@ export class Recovery extends Device { try { if (this.device && this.device.isConnected) { WDebug.log("Connect recovery the device is connected"); - } - else { + } else { let adbDaemonWebUsbDevice = await ADB.Manager.requestDevice(); if (typeof adbDaemonWebUsbDevice == "undefined") { throw new Error("No device connected (1)"); @@ -40,8 +39,8 @@ export class Recovery extends Device { try { this.connection = await adbDaemonWebUsbDevice.connect(); - } - catch (err) { + } catch (err) { + console.error(err); const devices = await ADB.Manager.getDevices(); if (!devices.length) { throw new Error("No device connected (2)"); @@ -59,26 +58,31 @@ export class Recovery extends Device { }; // Need this code here to have a _inEndpointNumber & _outEndpointNumber defined - outerLoop: - for (const configuration of this.adbDaemonWebUsbDevice.configurations) { + outerLoop: for (const configuration of this.adbDaemonWebUsbDevice + .configurations) { for (const interface_ of configuration.interfaces) { for (const alternate of interface_.alternates) { if ( - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && + alternate.interfaceSubclass === + WebUsbDeviceFilter.subclassCode && alternate.interfaceClass === WebUsbDeviceFilter.classCode && alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode ) { if ( - ((_a = this.adbDaemonWebUsbDevice.configuration) === null || _a === void 0 + ((_a = this.adbDaemonWebUsbDevice.configuration) === null || + _a === void 0 ? void 0 - : _a.configurationValue) !== configuration.configurationValue + : _a.configurationValue) !== + configuration.configurationValue ) { await this.adbDaemonWebUsbDevice.selectConfiguration( configuration.configurationValue, ); } if (!interface_.claimed) { - await this.adbDaemonWebUsbDevice.claimInterface(interface_.interfaceNumber); + await this.adbDaemonWebUsbDevice.claimInterface( + interface_.interfaceNumber, + ); } if ( interface_.alternate.alternateSetting !== @@ -122,8 +126,7 @@ export class Recovery extends Device { if (r.value.command == AdbCommand.Connect) { //All is fine - } - else { + } else { throw new Error("Adb sideload connection error"); } } @@ -143,7 +146,7 @@ export class Recovery extends Device { async readOnDevice() { const reader = await this.connection?.readable?.getReader(); - if(!reader) { + if (!reader) { throw new Error("readOnDevice() : Unable to read on device"); } const r = await reader.read(); @@ -153,15 +156,12 @@ export class Recovery extends Device { async sendPacket(init) { const writer = this.connection?.writable?.getWriter(); - if(!writer) { + if (!writer) { throw new Error("sendPacket() : Unable to write on device"); } init.checksum = calculateChecksum(init.payload); init.magic = init.command ^ 0xffffffff; - await Consumable.WritableStream.write( - writer, - init, - ); + await Consumable.WritableStream.write(writer, init); writer.releaseLock(); } @@ -179,8 +179,7 @@ export class Recovery extends Device { if (r.value.command == AdbCommand.Okay) { remoteId = r.value.arg0; return { localId: localId, remoteId: remoteId }; - } - else { + } else { throw new Error("Adb sideload create stream error"); } } @@ -230,8 +229,7 @@ export class Recovery extends Device { message = { data: r.value.payload, }; - } - else { + } else { throw new Error("Write OKAY Failed (init)"); } @@ -242,8 +240,7 @@ export class Recovery extends Device { if (isNaN(block) && res === "DONEDONE") { WDebug.log("DONEDONE"); break; - } - else { + } else { if (block % 10 == 0) { WDebug.log("Sideloading " + block); } @@ -283,13 +280,11 @@ export class Recovery extends Device { message = { data: r.value.payload, }; - } - else { + } else { console.error("Error sideload (A)", r); throw new Error(`WRTE Failed ${block}`); } - } - else { + } else { console.error("Error sideload (B)", r); throw new Error("Write OKAY Failed (init)"); } -- GitLab From 246d57a4c088f20b15f234d1e6b024743a889db5 Mon Sep 17 00:00:00 2001 From: frankpreel Date: Fri, 14 Feb 2025 15:27:58 +0100 Subject: [PATCH 17/19] Remove commented code --- app/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/app/index.html b/app/index.html index 79eae68..b3f60db 100644 --- a/app/index.html +++ b/app/index.html @@ -10,7 +10,6 @@ /e/OS Installer - -- GitLab From ae163003e7e8d0faceed65cfff415f25744185be Mon Sep 17 00:00:00 2001 From: frankpreel Date: Fri, 14 Feb 2025 15:30:32 +0100 Subject: [PATCH 18/19] Remove unused --- app/src/controller/device/adb.class.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/controller/device/adb.class.js b/app/src/controller/device/adb.class.js index 7ea5791..04be405 100644 --- a/app/src/controller/device/adb.class.js +++ b/app/src/controller/device/adb.class.js @@ -59,8 +59,6 @@ export class ADB extends Device { }); const adb = new Adb(transport); - //const version = await adb.getProp("ro.build.version.release"); - this.device = adbDaemonWebUsbDevice; this.webusb = adb; /*Adb*/ -- GitLab From dc104448697aa50473b39e9b56567575eb696745 Mon Sep 17 00:00:00 2001 From: frankpreel Date: Mon, 17 Feb 2025 17:40:13 +0100 Subject: [PATCH 19/19] Dedicated function for in/out endpoints --- app/src/controller/device/recovery.class.js | 128 +++++++++++--------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/app/src/controller/device/recovery.class.js b/app/src/controller/device/recovery.class.js index 96bec9e..0dc7b3c 100644 --- a/app/src/controller/device/recovery.class.js +++ b/app/src/controller/device/recovery.class.js @@ -50,69 +50,14 @@ export class Recovery extends Device { this.adbDaemonWebUsbDevice = adbDaemonWebUsbDevice.raw; - var _a; + // Filter to identify Android device in adb mode. const WebUsbDeviceFilter = { classCode: 0xff, subclassCode: 0x42, protocolCode: 1, }; - // Need this code here to have a _inEndpointNumber & _outEndpointNumber defined - outerLoop: for (const configuration of this.adbDaemonWebUsbDevice - .configurations) { - for (const interface_ of configuration.interfaces) { - for (const alternate of interface_.alternates) { - if ( - alternate.interfaceSubclass === - WebUsbDeviceFilter.subclassCode && - alternate.interfaceClass === WebUsbDeviceFilter.classCode && - alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode - ) { - if ( - ((_a = this.adbDaemonWebUsbDevice.configuration) === null || - _a === void 0 - ? void 0 - : _a.configurationValue) !== - configuration.configurationValue - ) { - await this.adbDaemonWebUsbDevice.selectConfiguration( - configuration.configurationValue, - ); - } - if (!interface_.claimed) { - await this.adbDaemonWebUsbDevice.claimInterface( - interface_.interfaceNumber, - ); - } - if ( - interface_.alternate.alternateSetting !== - alternate.alternateSetting - ) { - await this.adbDaemonWebUsbDevice.selectAlternateInterface( - interface_.interfaceNumber, - alternate.alternateSetting, - ); - } - for (const endpoint of alternate.endpoints) { - switch (endpoint.direction) { - case "in": - this._inEndpointNumber = endpoint.endpointNumber; - if (this._outEndpointNumber !== undefined) { - break outerLoop; - } - break; - case "out": - this._outEndpointNumber = endpoint.endpointNumber; - if (this._inEndpointNumber !== undefined) { - break outerLoop; - } - break; - } - } - } - } - } - } + await this.getInOutEndpoints(WebUsbDeviceFilter); const version = 0x01000001; const maxPayloadSize = 0x100000; @@ -136,6 +81,75 @@ export class Recovery extends Device { } } + /** + * Finds and selects the input and output endpoints of a USB device matching a given filter. + * + * @async + * @param {Object} WebUsbDeviceFilter - Filter defining the criteria for selecting USB interfaces. + * @returns void. + * + * @description + * This function iterates through the configurations of the attached USB device (`adbDaemonWebUsbDevice`) + * to identify an interface that matches the `WebUsbDeviceFilter` criteria and exits + * as soon as both endpoints are found (in & out). + */ + async getInOutEndpoints(WebUsbDeviceFilter) { + let _a; + outerLoop: for (const configuration of this.adbDaemonWebUsbDevice + .configurations) { + for (const interface_ of configuration.interfaces) { + for (const alternate of interface_.alternates) { + if ( + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode && + alternate.interfaceClass === WebUsbDeviceFilter.classCode && + alternate.interfaceSubclass === WebUsbDeviceFilter.subclassCode + ) { + if ( + ((_a = this.adbDaemonWebUsbDevice.configuration) === null || + _a === void 0 + ? void 0 + : _a.configurationValue) !== configuration.configurationValue + ) { + await this.adbDaemonWebUsbDevice.selectConfiguration( + configuration.configurationValue, + ); + } + if (!interface_.claimed) { + await this.adbDaemonWebUsbDevice.claimInterface( + interface_.interfaceNumber, + ); + } + if ( + interface_.alternate.alternateSetting !== + alternate.alternateSetting + ) { + await this.adbDaemonWebUsbDevice.selectAlternateInterface( + interface_.interfaceNumber, + alternate.alternateSetting, + ); + } + for (const endpoint of alternate.endpoints) { + switch (endpoint.direction) { + case "in": + this._inEndpointNumber = endpoint.endpointNumber; + if (this._outEndpointNumber !== undefined) { + break outerLoop; + } + break; + case "out": + this._outEndpointNumber = endpoint.endpointNumber; + if (this._inEndpointNumber !== undefined) { + break outerLoop; + } + break; + } + } + } + } + } + } + } + encodeUtf8(input) { return new TextEncoder().encode(input); } -- GitLab