mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-30 23:03:02 +00:00 
			
		
		
		
	Changed backend for comic reader, now supports rar5
This commit is contained in:
		| @@ -1,449 +0,0 @@ | ||||
| /* alphanum.js (C) Brian Huisman | ||||
|  * Based on the Alphanum Algorithm by David Koelle | ||||
|  * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com | ||||
|  * | ||||
|  * Distributed under same license as original | ||||
|  * | ||||
|  * Released under the MIT License - https://opensource.org/licenses/MIT | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining | ||||
|  * a copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included | ||||
|  * in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||||
|  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||||
|  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||||
|  * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| /* ******************************************************************** | ||||
| * Alphanum sort() function version - case insensitive | ||||
| *  - Slower, but easier to modify for arrays of objects which contain | ||||
| *    string properties | ||||
| * | ||||
| */ | ||||
| /* exported alphanumCase */ | ||||
|  | ||||
|  | ||||
| function alphanumCase(a, b) { | ||||
|     function chunkify(t) { | ||||
|         var tz = new Array(); | ||||
|         var x = 0, y = -1, n = 0, i, j; | ||||
|  | ||||
|         while (i = (j = t.charAt(x++)).charCodeAt(0)) { | ||||
|             var m = (i === 46 || (i >= 48 && i <= 57)); | ||||
|             // Compare has to be with != otherwise fails | ||||
|             if (m != n) { | ||||
|                 tz[++y] = ""; | ||||
|                 n = m; | ||||
|             } | ||||
|             tz[y] += j; | ||||
|         } | ||||
|         return tz; | ||||
|     } | ||||
|  | ||||
|     var aa = chunkify(a.filename.toLowerCase()); | ||||
|     var bb = chunkify(b.filename.toLowerCase()); | ||||
|  | ||||
|     for (var x = 0; aa[x] && bb[x]; x++) { | ||||
|         if (aa[x] !== bb[x]) { | ||||
|             var c = Number(aa[x]), d = Number(bb[x]); | ||||
|             // Compare has to be with == otherwise fails | ||||
|             if (c == aa[x] && d == bb[x]) { | ||||
|                 return c - d; | ||||
|             } else { | ||||
|                 return (aa[x] > bb[x]) ? 1 : -1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return aa.length - bb.length; | ||||
| } | ||||
| // =========================================================================== | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * archive.js | ||||
|  * | ||||
|  * Provides base functionality for unarchiving. | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2011 Google Inc. | ||||
|  */ | ||||
|  | ||||
| /* global bitjs, Uint8Array */ | ||||
|  | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.archive = bitjs.archive || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|     // =========================================================================== | ||||
|     // Stolen from Closure because it's the best way to do Java-like inheritance. | ||||
|     bitjs.base = function(me, optMethodName, varArgs) { | ||||
|         var caller = arguments.callee.caller; | ||||
|         if (caller.superClass_) { | ||||
|             // This is a constructor. Call the superclass constructor. | ||||
|             return caller.superClass_.constructor.apply( | ||||
|                 me, Array.prototype.slice.call(arguments, 1)); | ||||
|         } | ||||
|  | ||||
|         var args = Array.prototype.slice.call(arguments, 2); | ||||
|         var foundCaller = false; | ||||
|         for (var ctor = me.constructor; ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { | ||||
|             if (ctor.prototype[optMethodName] === caller) { | ||||
|                 foundCaller = true; | ||||
|             } else if (foundCaller) { | ||||
|                 return ctor.prototype[optMethodName].apply(me, args); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // If we did not find the caller in the prototype chain, | ||||
|         // then one of two things happened: | ||||
|         // 1) The caller is an instance method. | ||||
|         // 2) This method was not called by the right caller. | ||||
|         if (me[optMethodName] === caller) { | ||||
|             return me.constructor.prototype[optMethodName].apply(me, args); | ||||
|         } else { | ||||
|             throw Error( | ||||
|                 "goog.base called from a method of one name " + | ||||
|                 "to a method of a different name"); | ||||
|         } | ||||
|     }; | ||||
|     bitjs.inherits = function(childCtor, parentCtor) { | ||||
|         /** @constructor */ | ||||
|         function TempCtor() {} | ||||
|         TempCtor.prototype = parentCtor.prototype; | ||||
|         childCtor.superClass_ = parentCtor.prototype; | ||||
|         childCtor.prototype = new TempCtor(); | ||||
|         childCtor.prototype.constructor = childCtor; | ||||
|     }; | ||||
|     // =========================================================================== | ||||
|  | ||||
|     /** | ||||
|      * An unarchive event. | ||||
|      * | ||||
|      * @param {string} type The event type. | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveEvent = function(type) { | ||||
|         /** | ||||
|          * The event type. | ||||
|          * | ||||
|          * @type {string} | ||||
|          */ | ||||
|         this.type = type; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The UnarchiveEvent types. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveEvent.Type = { | ||||
|         START: "start", | ||||
|         PROGRESS: "progress", | ||||
|         EXTRACT: "extract", | ||||
|         FINISH: "finish", | ||||
|         INFO: "info", | ||||
|         ERROR: "error" | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Useful for passing info up to the client (for debugging). | ||||
|      * | ||||
|      * @param {string} msg The info message. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveInfoEvent = function(msg) { | ||||
|         bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); | ||||
|  | ||||
|         /** | ||||
|          * The information message. | ||||
|          * | ||||
|          * @type {string} | ||||
|          */ | ||||
|         this.msg = msg; | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|     /** | ||||
|      * An unrecoverable error has occured. | ||||
|      * | ||||
|      * @param {string} msg The error message. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveErrorEvent = function(msg) { | ||||
|         bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); | ||||
|  | ||||
|         /** | ||||
|          * The information message. | ||||
|          * | ||||
|          * @type {string} | ||||
|          */ | ||||
|         this.msg = msg; | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|     /** | ||||
|      * Start event. | ||||
|      * | ||||
|      * @param {string} msg The info message. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveStartEvent = function() { | ||||
|         bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START); | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|     /** | ||||
|      * Finish event. | ||||
|      * | ||||
|      * @param {string} msg The info message. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveFinishEvent = function() { | ||||
|         bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH); | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|     /** | ||||
|      * Progress event. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveProgressEvent = function( | ||||
|         currentFilename, | ||||
|         currentFileNumber, | ||||
|         currentBytesUnarchivedInFile, | ||||
|         currentBytesUnarchived, | ||||
|         totalUncompressedBytesInArchive, | ||||
|         totalFilesInArchive) { | ||||
|         bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS); | ||||
|  | ||||
|         this.currentFilename = currentFilename; | ||||
|         this.currentFileNumber = currentFileNumber; | ||||
|         this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; | ||||
|         this.totalFilesInArchive = totalFilesInArchive; | ||||
|         this.currentBytesUnarchived = currentBytesUnarchived; | ||||
|         this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|     /** | ||||
|      * All extracted files returned by an Unarchiver will implement | ||||
|      * the following interface: | ||||
|      * | ||||
|      * interface UnarchivedFile { | ||||
|      *   string filename | ||||
|      *   TypedArray fileData | ||||
|      * } | ||||
|      * | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Extract event. | ||||
|      */ | ||||
|     bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { | ||||
|         bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); | ||||
|  | ||||
|         /** | ||||
|          * @type {UnarchivedFile} | ||||
|          */ | ||||
|         this.unarchivedFile = unarchivedFile; | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Base class for all Unarchivers. | ||||
|      * | ||||
|      * @param {ArrayBuffer} arrayBuffer The Array Buffer. | ||||
|      * @param {string} optPathToBitJS Optional string for where the BitJS files are located. | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver = function(arrayBuffer, optPathToBitJS) { | ||||
|         /** | ||||
|          * The ArrayBuffer object. | ||||
|          * @type {ArrayBuffer} | ||||
|          * @protected | ||||
|          */ | ||||
|         this.ab = arrayBuffer; | ||||
|  | ||||
|         /** | ||||
|          * The path to the BitJS files. | ||||
|          * @type {string} | ||||
|          * @private | ||||
|          */ | ||||
|         this.pathToBitJS_ = optPathToBitJS || "/"; | ||||
|  | ||||
|         /** | ||||
|          * A map from event type to an array of listeners. | ||||
|          * @type {Map.<string, Array>} | ||||
|          */ | ||||
|         this.listeners_ = {}; | ||||
|         for (var type in bitjs.archive.UnarchiveEvent.Type) { | ||||
|             this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Private web worker initialized during start(). | ||||
|      * @type {Worker} | ||||
|      * @private | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.worker_ = null; | ||||
|  | ||||
|     /** | ||||
|      * This method must be overridden by the subclass to return the script filename. | ||||
|      * @return {string} The script filename. | ||||
|      * @protected. | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.getScriptFileName = function() { | ||||
|         throw "Subclasses of AbstractUnarchiver must overload getScriptFileName()"; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Adds an event listener for UnarchiveEvents. | ||||
|      * | ||||
|      * @param {string} Event type. | ||||
|      * @param {function} An event handler function. | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) { | ||||
|         if (type in this.listeners_) { | ||||
|             if (this.listeners_[type].indexOf(listener) === -1) { | ||||
|                 this.listeners_[type].push(listener); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Removes an event listener. | ||||
|      * | ||||
|      * @param {string} Event type. | ||||
|      * @param {EventListener|function} An event listener or handler function. | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) { | ||||
|         if (type in this.listeners_) { | ||||
|             var index = this.listeners_[type].indexOf(listener); | ||||
|             if (index !== -1) { | ||||
|                 this.listeners_[type].splice(index, 1); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Receive an event and pass it to the listener functions. | ||||
|      * | ||||
|      * @param {bitjs.archive.UnarchiveEvent} e | ||||
|      * @private | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) { | ||||
|         if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && | ||||
|             this.listeners_[e.type] instanceof Array) { | ||||
|             this.listeners_[e.type].forEach(function (listener) { | ||||
|                 listener(e); | ||||
|             }); | ||||
|             if (e.type === bitjs.archive.UnarchiveEvent.Type.FINISH) { | ||||
|                 this.worker_.terminate(); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Starts the unarchive in a separate Web Worker thread and returns immediately. | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.start = function() { | ||||
|         var me = this; | ||||
|         var scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); | ||||
|         if (scriptFileName) { | ||||
|             this.worker_ = new Worker(scriptFileName); | ||||
|  | ||||
|             this.worker_.onerror = function(e) { | ||||
|                 throw e; | ||||
|             }; | ||||
|  | ||||
|             this.worker_.onmessage = function(e) { | ||||
|                 if (typeof e.data !== "string") { | ||||
|                     // Assume that it is an UnarchiveEvent.  Some browsers preserve the 'type' | ||||
|                     // so that instanceof UnarchiveEvent returns true, but others do not. | ||||
|                     me.handleWorkerEvent_(e.data); | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             this.worker_.postMessage({file: this.ab}); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Terminates the Web Worker for this Unarchiver and returns immediately. | ||||
|      */ | ||||
|     bitjs.archive.Unarchiver.prototype.stop = function() { | ||||
|         if (this.worker_) { | ||||
|             this.worker_.terminate(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Unzipper | ||||
|      * @extends {bitjs.archive.Unarchiver} | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.archive.Unzipper = function(arrayBuffer, optPathToBitJS) { | ||||
|         bitjs.base(this, arrayBuffer, optPathToBitJS); | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver); | ||||
|     bitjs.archive.Unzipper.prototype.getScriptFileName = function() { | ||||
|         return "unzip.js"; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Unrarrer | ||||
|      * @extends {bitjs.archive.Unarchiver} | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.archive.Unrarrer = function(arrayBuffer, optPathToBitJS) { | ||||
|         bitjs.base(this, arrayBuffer, optPathToBitJS); | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver); | ||||
|     bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { | ||||
|         return "unrar.js"; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Untarrer | ||||
|      * @extends {bitjs.archive.Unarchiver} | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.archive.Untarrer = function(arrayBuffer, optPathToBitJS) { | ||||
|         bitjs.base(this, arrayBuffer, optPathToBitJS); | ||||
|     }; | ||||
|     bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver); | ||||
|     bitjs.archive.Untarrer.prototype.getScriptFileName = function() { | ||||
|         return "untar.js"; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Factory method that creates an unarchiver based on the byte signature found | ||||
|      * in the arrayBuffer. | ||||
|      * @param {ArrayBuffer} ab | ||||
|      * @param {string=} optPathToBitJS Path to the unarchiver script files. | ||||
|      * @return {bitjs.archive.Unarchiver} | ||||
|      */ | ||||
|     bitjs.archive.GetUnarchiver = function(ab, optPathToBitJS) { | ||||
|         var unarchiver = null; | ||||
|         var pathToBitJS = optPathToBitJS || ""; | ||||
|         var h = new Uint8Array(ab, 0, 10); | ||||
|  | ||||
|         if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { // Rar! | ||||
|             unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); | ||||
|         } else if (h[0] === 80 && h[1] === 75) { // PK (Zip) | ||||
|             unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS); | ||||
|         } else { // Try with tar | ||||
|             unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); | ||||
|         } | ||||
|         return unarchiver; | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
| @@ -1,872 +0,0 @@ | ||||
| /** | ||||
|  * rarvm.js | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2017 Google Inc. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * CRC Implementation. | ||||
|  */ | ||||
| /* global Uint8Array, Uint32Array, bitjs, DataView, mem */ | ||||
| /* exported MAXWINMASK, UnpackFilter */ | ||||
|  | ||||
| function emptyArr(n, v) { | ||||
|     var arr = []; | ||||
|     for (var i = 0; i < n; i += 1) { | ||||
|         arr[i] = v; | ||||
|     } | ||||
|     return arr; | ||||
| } | ||||
|  | ||||
| var CRCTab = emptyArr(256, 0); | ||||
|  | ||||
| function initCRC() { | ||||
|     for (var i = 0; i < 256; ++i) { | ||||
|         var c = i; | ||||
|         for (var j = 0; j < 8; ++j) { | ||||
|             // Read http://stackoverflow.com/questions/6798111/bitwise-operations-on-32-bit-unsigned-ints | ||||
|             // for the bitwise operator issue (JS interprets operands as 32-bit signed | ||||
|             // integers and we need to deal with unsigned ones here). | ||||
|             c = ((c & 1) ? ((c >>> 1) ^ 0xEDB88320) : (c >>> 1)) >>> 0; | ||||
|         } | ||||
|         CRCTab[i] = c; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @param {number} startCRC | ||||
|  * @param {Uint8Array} arr | ||||
|  * @return {number} | ||||
|  */ | ||||
| function CRC(startCRC, arr) { | ||||
|     if (CRCTab[1] === 0) { | ||||
|         initCRC(); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     #if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) | ||||
|       while (Size>0 && ((long)Data & 7)) | ||||
|       { | ||||
|         StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8); | ||||
|         Size--; | ||||
|         Data++; | ||||
|       } | ||||
|       while (Size>=8) | ||||
|       { | ||||
|         StartCRC^=*(uint32 *)Data; | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC^=*(uint32 *)(Data+4); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); | ||||
|         Data+=8; | ||||
|         Size-=8; | ||||
|       } | ||||
|     #endif | ||||
|     */ | ||||
|  | ||||
|     for (var i = 0; i < arr.length; ++i) { | ||||
|         var byte = ((startCRC ^ arr[i]) >>> 0) & 0xff; | ||||
|         startCRC = (CRCTab[byte] ^ (startCRC >>> 8)) >>> 0; | ||||
|     } | ||||
|  | ||||
|     return startCRC; | ||||
| } | ||||
|  | ||||
| // ============================================================================================== // | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * RarVM Implementation. | ||||
|  */ | ||||
| var VM_MEMSIZE = 0x40000; | ||||
| var VM_MEMMASK = (VM_MEMSIZE - 1); | ||||
| var VM_GLOBALMEMADDR = 0x3C000; | ||||
| var VM_GLOBALMEMSIZE = 0x2000; | ||||
| var VM_FIXEDGLOBALSIZE = 64; | ||||
| var MAXWINSIZE = 0x400000; | ||||
| var MAXWINMASK = (MAXWINSIZE - 1); | ||||
|  | ||||
| /** | ||||
|  */ | ||||
| var VmCommands = { | ||||
|     VM_MOV: 0, | ||||
|     VM_CMP: 1, | ||||
|     VM_ADD: 2, | ||||
|     VM_SUB: 3, | ||||
|     VM_JZ: 4, | ||||
|     VM_JNZ: 5, | ||||
|     VM_INC: 6, | ||||
|     VM_DEC: 7, | ||||
|     VM_JMP: 8, | ||||
|     VM_XOR: 9, | ||||
|     VM_AND: 10, | ||||
|     VM_OR: 11, | ||||
|     VM_TEST: 12, | ||||
|     VM_JS: 13, | ||||
|     VM_JNS: 14, | ||||
|     VM_JB: 15, | ||||
|     VM_JBE: 16, | ||||
|     VM_JA: 17, | ||||
|     VM_JAE: 18, | ||||
|     VM_PUSH: 19, | ||||
|     VM_POP: 20, | ||||
|     VM_CALL: 21, | ||||
|     VM_RET: 22, | ||||
|     VM_NOT: 23, | ||||
|     VM_SHL: 24, | ||||
|     VM_SHR: 25, | ||||
|     VM_SAR: 26, | ||||
|     VM_NEG: 27, | ||||
|     VM_PUSHA: 28, | ||||
|     VM_POPA: 29, | ||||
|     VM_PUSHF: 30, | ||||
|     VM_POPF: 31, | ||||
|     VM_MOVZX: 32, | ||||
|     VM_MOVSX: 33, | ||||
|     VM_XCHG: 34, | ||||
|     VM_MUL: 35, | ||||
|     VM_DIV: 36, | ||||
|     VM_ADC: 37, | ||||
|     VM_SBB: 38, | ||||
|     VM_PRINT: 39, | ||||
|  | ||||
|     /* | ||||
|     #ifdef VM_OPTIMIZE | ||||
|       VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD, | ||||
|  | ||||
|       VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD, | ||||
|       VM_NEGB, VM_NEGD, | ||||
|     #endif | ||||
|     */ | ||||
|  | ||||
|     // TODO: This enum value would be much larger if VM_OPTIMIZE. | ||||
|     VM_STANDARD: 40, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  */ | ||||
| var VmStandardFilters = { | ||||
|     VMSF_NONE: 0, | ||||
|     VMSF_E8: 1, | ||||
|     VMSF_E8E9: 2, | ||||
|     VMSF_ITANIUM: 3, | ||||
|     VMSF_RGB: 4, | ||||
|     VMSF_AUDIO: 5, | ||||
|     VMSF_DELTA: 6, | ||||
|     VMSF_UPCASE: 7, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  */ | ||||
| var VmFlags = { | ||||
|     VM_FC: 1, | ||||
|     VM_FZ: 2, | ||||
|     VM_FS: 0x80000000, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  */ | ||||
| var VmOpType = { | ||||
|     VM_OPREG: 0, | ||||
|     VM_OPINT: 1, | ||||
|     VM_OPREGMEM: 2, | ||||
|     VM_OPNONE: 3, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Finds the key that maps to a given value in an object.  This function is useful in debugging | ||||
|  * variables that use the above enums. | ||||
|  * @param {Object} obj | ||||
|  * @param {number} val | ||||
|  * @return {string} The key/enum value as a string. | ||||
|  */ | ||||
| function findKeyForValue(obj, val) { | ||||
|     for (var key in obj) { | ||||
|         if (obj[key] === val) { | ||||
|             return key; | ||||
|         } | ||||
|     } | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| function getDebugString(obj, val) { | ||||
|     var s = "Unknown."; | ||||
|     if (obj === VmCommands) { | ||||
|         s = "VmCommands."; | ||||
|     } else if (obj === VmStandardFilters) { | ||||
|         s = "VmStandardFilters."; | ||||
|     } else if (obj === VmFlags) { | ||||
|         s = "VmOpType."; | ||||
|     } else if (obj === VmOpType) { | ||||
|         s = "VmOpType."; | ||||
|     } | ||||
|  | ||||
|     return s + findKeyForValue(obj, val); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @struct | ||||
|  * @constructor | ||||
|  */ | ||||
| var VmPreparedOperand = function() { | ||||
|     /** @type {VmOpType} */ | ||||
|     this.Type; | ||||
|  | ||||
|     /** @type {number} */ | ||||
|     this.Data = 0; | ||||
|  | ||||
|     /** @type {number} */ | ||||
|     this.Base = 0; | ||||
|  | ||||
|     // TODO: In C++ this is a uint* | ||||
|     /** @type {Array<number>} */ | ||||
|     this.Addr = null; | ||||
| }; | ||||
|  | ||||
| /** @return {string} */ | ||||
| VmPreparedOperand.prototype.toString = function() { | ||||
|     if (this.Type === null) { | ||||
|         return "Error: Type was null in VmPreparedOperand"; | ||||
|     } | ||||
|     return "{ " + | ||||
|         "Type: " + getDebugString(VmOpType, this.Type) + | ||||
|         ", Data: " + this.Data + | ||||
|         ", Base: " + this.Base + | ||||
|         " }"; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @struct | ||||
|  * @constructor | ||||
|  */ | ||||
| var VmPreparedCommand = function() { | ||||
|     /** @type {VmCommands} */ | ||||
|     this.OpCode; | ||||
|  | ||||
|     /** @type {boolean} */ | ||||
|     this.ByteMode = false; | ||||
|  | ||||
|     /** @type {VmPreparedOperand} */ | ||||
|     this.Op1 = new VmPreparedOperand(); | ||||
|  | ||||
|     /** @type {VmPreparedOperand} */ | ||||
|     this.Op2 = new VmPreparedOperand(); | ||||
| }; | ||||
|  | ||||
| /** @return {string} */ | ||||
| VmPreparedCommand.prototype.toString = function(indent) { | ||||
|     if (this.OpCode === null) { | ||||
|         return "Error: OpCode was null in VmPreparedCommand"; | ||||
|     } | ||||
|     indent = indent || ""; | ||||
|     return indent + "{\n" + | ||||
|         indent + "  OpCode: " + getDebugString(VmCommands, this.OpCode) + ",\n" + | ||||
|         indent + "  ByteMode: " + this.ByteMode + ",\n" + | ||||
|         indent + "  Op1: " + this.Op1.toString() + ",\n" + | ||||
|         indent + "  Op2: " + this.Op2.toString() + ",\n" + | ||||
|         indent + "}"; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @struct | ||||
|  * @constructor | ||||
|  */ | ||||
| var VmPreparedProgram = function() { | ||||
|     /** @type {Array<VmPreparedCommand>} */ | ||||
|     this.Cmd = []; | ||||
|  | ||||
|     /** @type {Array<VmPreparedCommand>} */ | ||||
|     this.AltCmd = null; | ||||
|  | ||||
|     /** @type {Uint8Array} */ | ||||
|     this.GlobalData = new Uint8Array(); | ||||
|  | ||||
|     /** @type {Uint8Array} */ | ||||
|     this.StaticData = new Uint8Array(); // static data contained in DB operators | ||||
|  | ||||
|     /** @type {Uint32Array} */ | ||||
|     this.InitR = new Uint32Array(7); | ||||
|  | ||||
|     /** | ||||
|      * A pointer to bytes that have been filtered by a program. | ||||
|      * @type {Uint8Array} | ||||
|      */ | ||||
|     this.FilteredData = null; | ||||
| }; | ||||
|  | ||||
| /** @return {string} */ | ||||
| VmPreparedProgram.prototype.toString = function() { | ||||
|     var s = "{\n  Cmd: [\n"; | ||||
|     for (var i = 0; i < this.Cmd.length; ++i) { | ||||
|         s += this.Cmd[i].toString("  ") + ",\n"; | ||||
|     } | ||||
|     s += "],\n"; | ||||
|     // TODO: Dump GlobalData, StaticData, InitR? | ||||
|     s += " }\n"; | ||||
|     return s; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @struct | ||||
|  * @constructor | ||||
|  */ | ||||
| var UnpackFilter = function() { | ||||
|     /** @type {number} */ | ||||
|     this.BlockStart = 0; | ||||
|  | ||||
|     /** @type {number} */ | ||||
|     this.BlockLength = 0; | ||||
|  | ||||
|     /** @type {number} */ | ||||
|     this.ExecCount = 0; | ||||
|  | ||||
|     /** @type {boolean} */ | ||||
|     this.NextWindow = false; | ||||
|  | ||||
|     // position of parent filter in Filters array used as prototype for filter | ||||
|     // in PrgStack array. Not defined for filters in Filters array. | ||||
|     /** @type {number} */ | ||||
|     this.ParentFilter = null; | ||||
|  | ||||
|     /** @type {VmPreparedProgram} */ | ||||
|     this.Prg = new VmPreparedProgram(); | ||||
| }; | ||||
|  | ||||
| var VMCF_OP0 = 0; | ||||
| var VMCF_OP1 = 1; | ||||
| var VMCF_OP2 = 2; | ||||
| var VMCF_OPMASK = 3; | ||||
| var VMCF_BYTEMODE = 4; | ||||
| var VMCF_JUMP = 8; | ||||
| var VMCF_PROC = 16; | ||||
| var VMCF_USEFLAGS = 32; | ||||
| var VMCF_CHFLAGS = 64; | ||||
|  | ||||
| var VmCmdFlags = [ | ||||
|     /* VM_MOV   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE, | ||||
|     /* VM_CMP   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_ADD   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_SUB   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_JZ    */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_JNZ   */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_INC   */ | ||||
|     VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_DEC   */ | ||||
|     VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_JMP   */ | ||||
|     VMCF_OP1 | VMCF_JUMP, | ||||
|     /* VM_XOR   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_AND   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_OR    */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_TEST  */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_JS    */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_JNS   */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_JB    */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_JBE   */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_JA    */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_JAE   */ | ||||
|     VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, | ||||
|     /* VM_PUSH  */ | ||||
|     VMCF_OP1, | ||||
|     /* VM_POP   */ | ||||
|     VMCF_OP1, | ||||
|     /* VM_CALL  */ | ||||
|     VMCF_OP1 | VMCF_PROC, | ||||
|     /* VM_RET   */ | ||||
|     VMCF_OP0 | VMCF_PROC, | ||||
|     /* VM_NOT   */ | ||||
|     VMCF_OP1 | VMCF_BYTEMODE, | ||||
|     /* VM_SHL   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_SHR   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_SAR   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_NEG   */ | ||||
|     VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, | ||||
|     /* VM_PUSHA */ | ||||
|     VMCF_OP0, | ||||
|     /* VM_POPA  */ | ||||
|     VMCF_OP0, | ||||
|     /* VM_PUSHF */ | ||||
|     VMCF_OP0 | VMCF_USEFLAGS, | ||||
|     /* VM_POPF  */ | ||||
|     VMCF_OP0 | VMCF_CHFLAGS, | ||||
|     /* VM_MOVZX */ | ||||
|     VMCF_OP2, | ||||
|     /* VM_MOVSX */ | ||||
|     VMCF_OP2, | ||||
|     /* VM_XCHG  */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE, | ||||
|     /* VM_MUL   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE, | ||||
|     /* VM_DIV   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE, | ||||
|     /* VM_ADC   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, | ||||
|     /* VM_SBB   */ | ||||
|     VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, | ||||
|     /* VM_PRINT */ | ||||
|     VMCF_OP0, | ||||
| ]; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * @param {number} length | ||||
|  * @param {number} crc | ||||
|  * @param {VmStandardFilters} type | ||||
|  * @struct | ||||
|  * @constructor | ||||
|  */ | ||||
| var StandardFilterSignature = function(length, crc, type) { | ||||
|     /** @type {number} */ | ||||
|     this.Length = length; | ||||
|  | ||||
|     /** @type {number} */ | ||||
|     this.CRC = crc; | ||||
|  | ||||
|     /** @type {VmStandardFilters} */ | ||||
|     this.Type = type; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @type {Array<StandardFilterSignature>} | ||||
|  */ | ||||
| var StdList = [ | ||||
|     new StandardFilterSignature(53, 0xad576887, VmStandardFilters.VMSF_E8), | ||||
|     new StandardFilterSignature(57, 0x3cd7e57e, VmStandardFilters.VMSF_E8E9), | ||||
|     new StandardFilterSignature(120, 0x3769893f, VmStandardFilters.VMSF_ITANIUM), | ||||
|     new StandardFilterSignature(29, 0x0e06077d, VmStandardFilters.VMSF_DELTA), | ||||
|     new StandardFilterSignature(149, 0x1c2c5dc8, VmStandardFilters.VMSF_RGB), | ||||
|     new StandardFilterSignature(216, 0xbc85e701, VmStandardFilters.VMSF_AUDIO), | ||||
|     new StandardFilterSignature(40, 0x46b9c560, VmStandardFilters.VMSF_UPCASE), | ||||
| ]; | ||||
|  | ||||
| /** | ||||
|  * @constructor | ||||
|  */ | ||||
| var RarVM = function() { | ||||
|     /** @private {Uint8Array} */ | ||||
|     this.mem_ = null; | ||||
|  | ||||
|     /** @private {Uint32Array<number>} */ | ||||
|     this.R_ = new Uint32Array(8); | ||||
|  | ||||
|     /** @private {number} */ | ||||
|     this.flags_ = 0; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Initializes the memory of the VM. | ||||
|  */ | ||||
| RarVM.prototype.init = function() { | ||||
|     if (!this.mem_) { | ||||
|         this.mem_ = new Uint8Array(VM_MEMSIZE); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {Uint8Array} code | ||||
|  * @return {VmStandardFilters} | ||||
|  */ | ||||
| RarVM.prototype.isStandardFilter = function(code) { | ||||
|     var codeCRC = (CRC(0xffffffff, code, code.length) ^ 0xffffffff) >>> 0; | ||||
|     for (var i = 0; i < StdList.length; ++i) { | ||||
|         if (StdList[i].CRC === codeCRC && StdList[i].Length === code.length) { | ||||
|             return StdList[i].Type; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return VmStandardFilters.VMSF_NONE; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {VmPreparedOperand} op | ||||
|  * @param {boolean} byteMode | ||||
|  * @param {bitjs.io.BitStream} bstream A rtl bit stream. | ||||
|  */ | ||||
| RarVM.prototype.decodeArg = function(op, byteMode, bstream) { | ||||
|     var data = bstream.peekBits(16); | ||||
|     if (data & 0x8000) { | ||||
|         op.Type = VmOpType.VM_OPREG; // Operand is register (R[0]..R[7]) | ||||
|         bstream.readBits(1); // 1 flag bit and... | ||||
|         op.Data = bstream.readBits(3); // ... 3 register number bits | ||||
|         op.Addr = [this.R_[op.Data]]; // TODO &R[Op.Data] // Register address | ||||
|     } else { | ||||
|         if ((data & 0xc000) === 0) { | ||||
|             op.Type = VmOpType.VM_OPINT; // Operand is integer | ||||
|             bstream.readBits(2); // 2 flag bits | ||||
|             if (byteMode) { | ||||
|                 op.Data = bstream.readBits(8); // Byte integer. | ||||
|             } else { | ||||
|                 op.Data = RarVM.readData(bstream); // 32 bit integer. | ||||
|             } | ||||
|         } else { | ||||
|             // Operand is data addressed by register data, base address or both. | ||||
|             op.Type = VmOpType.VM_OPREGMEM; | ||||
|             if ((data & 0x2000) === 0) { | ||||
|                 bstream.readBits(3); // 3 flag bits | ||||
|                 // Base address is zero, just use the address from register. | ||||
|                 op.Data = bstream.readBits(3); // (Data>>10)&7 | ||||
|                 op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data] | ||||
|                 op.Base = 0; | ||||
|             } else { | ||||
|                 bstream.readBits(4); // 4 flag bits | ||||
|                 if ((data & 0x1000) === 0) { | ||||
|                     // Use both register and base address. | ||||
|                     op.Data = bstream.readBits(3); | ||||
|                     op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data] | ||||
|                 } else { | ||||
|                     // Use base address only. Access memory by fixed address. | ||||
|                     op.Data = 0; | ||||
|                 } | ||||
|                 op.Base = RarVM.readData(bstream); // Read base address. | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {VmPreparedProgram} prg | ||||
|  */ | ||||
| RarVM.prototype.execute = function(prg) { | ||||
|     this.R_.set(prg.InitR); | ||||
|  | ||||
|     var globalSize = Math.min(prg.GlobalData.length, VM_GLOBALMEMSIZE); | ||||
|     if (globalSize) { | ||||
|         this.mem_.set(prg.GlobalData.subarray(0, globalSize), VM_GLOBALMEMADDR); | ||||
|     } | ||||
|  | ||||
|     var staticSize = Math.min(prg.StaticData.length, VM_GLOBALMEMSIZE - globalSize); | ||||
|     if (staticSize) { | ||||
|         this.mem_.set(prg.StaticData.subarray(0, staticSize), VM_GLOBALMEMADDR + globalSize); | ||||
|     } | ||||
|  | ||||
|     this.R_[7] = VM_MEMSIZE; | ||||
|     this.flags_ = 0; | ||||
|  | ||||
|     var preparedCodes = prg.AltCmd ? prg.AltCmd : prg.Cmd; | ||||
|     if (prg.Cmd.length > 0 && !this.executeCode(preparedCodes)) { | ||||
|         // Invalid VM program. Let's replace it with 'return' command. | ||||
|         preparedCodes.OpCode = VmCommands.VM_RET; | ||||
|     } | ||||
|  | ||||
|     var dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); | ||||
|     var newBlockPos = dataView.getUint32(0x20, true /* little endian */ ) & VM_MEMMASK; | ||||
|     var newBlockSize = dataView.getUint32(0x1c, true /* little endian */ ) & VM_MEMMASK; | ||||
|     if (newBlockPos + newBlockSize >= VM_MEMSIZE) { | ||||
|         newBlockPos = newBlockSize = 0; | ||||
|     } | ||||
|     prg.FilteredData = this.mem_.subarray(newBlockPos, newBlockPos + newBlockSize); | ||||
|  | ||||
|     prg.GlobalData = new Uint8Array(0); | ||||
|  | ||||
|     var dataSize = Math.min(dataView.getUint32(0x30), | ||||
|         (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)); | ||||
|     if (dataSize !== 0) { | ||||
|         var len = dataSize + VM_FIXEDGLOBALSIZE; | ||||
|         prg.GlobalData = new Uint8Array(len); | ||||
|         prg.GlobalData.set(mem.subarray(VM_GLOBALMEMADDR, VM_GLOBALMEMADDR + len)); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {Array<VmPreparedCommand>} preparedCodes | ||||
|  * @return {boolean} | ||||
|  */ | ||||
| RarVM.prototype.executeCode = function(preparedCodes) { | ||||
|     var codeIndex = 0; | ||||
|     var cmd = preparedCodes[codeIndex]; | ||||
|     // TODO: Why is this an infinite loop instead of just returning | ||||
|     // when a VM_RET is hit? | ||||
|     while (1) { | ||||
|         switch (cmd.OpCode) { | ||||
|             case VmCommands.VM_RET: | ||||
|                 if (this.R_[7] >= VM_MEMSIZE) { | ||||
|                     return true; | ||||
|                 } | ||||
|                 //SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); | ||||
|                 this.R_[7] += 4; | ||||
|                 continue; | ||||
|  | ||||
|             case VmCommands.VM_STANDARD: | ||||
|                 this.executeStandardFilter(cmd.Op1.Data); | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 console.error("RarVM OpCode not supported: " + getDebugString(VmCommands, cmd.OpCode)); | ||||
|                 break; | ||||
|         } // switch (cmd.OpCode) | ||||
|         codeIndex++; | ||||
|         cmd = preparedCodes[codeIndex]; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {number} filterType | ||||
|  */ | ||||
| RarVM.prototype.executeStandardFilter = function(filterType) { | ||||
|     switch (filterType) { | ||||
|         case VmStandardFilters.VMSF_DELTA: | ||||
|             var dataSize = this.R_[4]; | ||||
|             var channels = this.R_[0]; | ||||
|             var srcPos = 0; | ||||
|             var border = dataSize * 2; | ||||
|  | ||||
|             //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); | ||||
|             var dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR); | ||||
|             dataView.setUint32(0x20, dataSize, true /* little endian */ ); | ||||
|  | ||||
|             if (dataSize >= VM_GLOBALMEMADDR / 2) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             // Bytes from same channels are grouped to continual data blocks, | ||||
|             // so we need to place them back to their interleaving positions. | ||||
|             for (var curChannel = 0; curChannel < channels; ++curChannel) { | ||||
|                 var prevByte = 0; | ||||
|                 for (var destPos = dataSize + curChannel; destPos < border; destPos += channels) { | ||||
|                     prevByte = (prevByte - this.mem_[srcPos++]) & 0xff; | ||||
|                     this.mem_[destPos] = prevByte; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             console.error("RarVM Standard Filter not supported: " + getDebugString(VmStandardFilters, filterType)); | ||||
|             break; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {Uint8Array} code | ||||
|  * @param {VmPreparedProgram} prg | ||||
|  */ | ||||
| RarVM.prototype.prepare = function(code, prg) { | ||||
|     var codeSize = code.length; | ||||
|     var i; | ||||
|     var curCmd; | ||||
|  | ||||
|     //InitBitInput(); | ||||
|     //memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); | ||||
|     var bstream = new bitjs.io.BitStream(code.buffer, true /* rtl */ ); | ||||
|  | ||||
|     // Calculate the single byte XOR checksum to check validity of VM code. | ||||
|     var xorSum = 0; | ||||
|     for (i = 1; i < codeSize; ++i) { | ||||
|         xorSum ^= code[i]; | ||||
|     } | ||||
|  | ||||
|     bstream.readBits(8); | ||||
|  | ||||
|     prg.Cmd = []; // TODO: Is this right?  I don't see it being done in rarvm.cpp. | ||||
|  | ||||
|     // VM code is valid if equal. | ||||
|     if (xorSum === code[0]) { | ||||
|         var filterType = this.isStandardFilter(code); | ||||
|         if (filterType !== VmStandardFilters.VMSF_NONE) { | ||||
|             // VM code is found among standard filters. | ||||
|             curCmd = new VmPreparedCommand(); | ||||
|             prg.Cmd.push(curCmd); | ||||
|  | ||||
|             curCmd.OpCode = VmCommands.VM_STANDARD; | ||||
|             curCmd.Op1.Data = filterType; | ||||
|             // TODO: Addr=&CurCmd->Op1.Data | ||||
|             curCmd.Op1.Addr = [curCmd.Op1.Data]; | ||||
|             curCmd.Op2.Addr = [null]; // &CurCmd->Op2.Data; | ||||
|             curCmd.Op1.Type = VmOpType.VM_OPNONE; | ||||
|             curCmd.Op2.Type = VmOpType.VM_OPNONE; | ||||
|             codeSize = 0; | ||||
|         } | ||||
|  | ||||
|         var dataFlag = bstream.readBits(1); | ||||
|  | ||||
|         // Read static data contained in DB operators. This data cannot be | ||||
|         // changed, it is a part of VM code, not a filter parameter. | ||||
|  | ||||
|         if (dataFlag & 0x8000) { | ||||
|             var dataSize = RarVM.readData(bstream) + 1; | ||||
|             // TODO: This accesses the byte pointer of the bstream directly.  Is that ok? | ||||
|             for (i = 0; i < bstream.bytePtr < codeSize && i < dataSize; ++i) { | ||||
|                 // Append a byte to the program's static data. | ||||
|                 var newStaticData = new Uint8Array(prg.StaticData.length + 1); | ||||
|                 newStaticData.set(prg.StaticData); | ||||
|                 newStaticData[newStaticData.length - 1] = bstream.readBits(8); | ||||
|                 prg.StaticData = newStaticData; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         while (bstream.bytePtr < codeSize) { | ||||
|             curCmd = new VmPreparedCommand(); | ||||
|             prg.Cmd.push(curCmd); // Prg->Cmd.Add(1) | ||||
|             var flag = bstream.peekBits(1); | ||||
|             if (!flag) { // (Data&0x8000)==0 | ||||
|                 curCmd.OpCode = bstream.readBits(4); | ||||
|             } else { | ||||
|                 curCmd.OpCode = (bstream.readBits(6) - 24); | ||||
|             } | ||||
|  | ||||
|             if (VmCmdFlags[curCmd.OpCode] & VMCF_BYTEMODE) { | ||||
|                 curCmd.ByteMode = (bstream.readBits(1) !== 0); | ||||
|             } else { | ||||
|                 curCmd.ByteMode = 0; | ||||
|             } | ||||
|             curCmd.Op1.Type = VmOpType.VM_OPNONE; | ||||
|             curCmd.Op2.Type = VmOpType.VM_OPNONE; | ||||
|             var opNum = (VmCmdFlags[curCmd.OpCode] & VMCF_OPMASK); | ||||
|             curCmd.Op1.Addr = null; | ||||
|             curCmd.Op2.Addr = null; | ||||
|             if (opNum > 0) { | ||||
|                 this.decodeArg(curCmd.Op1, curCmd.ByteMode, bstream); // reading the first operand | ||||
|                 if (opNum === 2) { | ||||
|                     this.decodeArg(curCmd.Op2, curCmd.ByteMode, bstream); // reading the second operand | ||||
|                 } else { | ||||
|                     if (curCmd.Op1.Type === VmOpType.VM_OPINT && (VmCmdFlags[curCmd.OpCode] & (VMCF_JUMP | VMCF_PROC))) { | ||||
|                         // Calculating jump distance. | ||||
|                         var distance = curCmd.Op1.Data; | ||||
|                         if (distance >= 256) { | ||||
|                             distance -= 256; | ||||
|                         } else { | ||||
|                             if (distance >= 136) { | ||||
|                                 distance -= 264; | ||||
|                             } else { | ||||
|                                 if (distance >= 16) { | ||||
|                                     distance -= 8; | ||||
|                                 } else { | ||||
|                                     if (distance >= 8) { | ||||
|                                         distance -= 16; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             distance += prg.Cmd.length; | ||||
|                         } | ||||
|                         curCmd.Op1.Data = distance; | ||||
|                     } | ||||
|                 } | ||||
|             } // if (OpNum>0) | ||||
|         } // while ((uint)InAddr<CodeSize) | ||||
|     } // if (XorSum==Code[0]) | ||||
|  | ||||
|     curCmd = new VmPreparedCommand(); | ||||
|     prg.Cmd.push(curCmd); | ||||
|     curCmd.OpCode = VmCommands.VM_RET; | ||||
|     // TODO: Addr=&CurCmd->Op1.Data | ||||
|     curCmd.Op1.Addr = [curCmd.Op1.Data]; | ||||
|     curCmd.Op2.Addr = [curCmd.Op2.Data]; | ||||
|     curCmd.Op1.Type = VmOpType.VM_OPNONE; | ||||
|     curCmd.Op2.Type = VmOpType.VM_OPNONE; | ||||
|  | ||||
|     // If operand 'Addr' field has not been set by DecodeArg calls above, | ||||
|     // let's set it to point to operand 'Data' field. It is necessary for | ||||
|     // VM_OPINT type operands (usual integers) or maybe if something was | ||||
|     // not set properly for other operands. 'Addr' field is required | ||||
|     // for quicker addressing of operand data. | ||||
|     for (i = 0; i < prg.Cmd.length; ++i) { | ||||
|         var cmd = prg.Cmd[i]; | ||||
|         if (cmd.Op1.Addr === null) { | ||||
|             cmd.Op1.Addr = [cmd.Op1.Data]; | ||||
|         } | ||||
|         if (cmd.Op2.Addr === null) { | ||||
|             cmd.Op2.Addr = [cmd.Op2.Data]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     #ifdef VM_OPTIMIZE | ||||
|       if (CodeSize!=0) | ||||
|         Optimize(Prg); | ||||
|     #endif | ||||
|       */ | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @param {Uint8Array} arr The byte array to set a value in. | ||||
|  * @param {number} value The unsigned 32-bit value to set. | ||||
|  * @param {number} offset Offset into arr to start setting the value, defaults to 0. | ||||
|  */ | ||||
| RarVM.prototype.setLowEndianValue = function(arr, value, offset) { | ||||
|     var i = offset || 0; | ||||
|     arr[i] = value & 0xff; | ||||
|     arr[i + 1] = (value >>> 8) & 0xff; | ||||
|     arr[i + 2] = (value >>> 16) & 0xff; | ||||
|     arr[i + 3] = (value >>> 24) & 0xff; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Sets a number of bytes of the VM memory at the given position from a | ||||
|  * source buffer of bytes. | ||||
|  * @param {number} pos The position in the VM memory to start writing to. | ||||
|  * @param {Uint8Array} buffer The source buffer of bytes. | ||||
|  * @param {number} dataSize The number of bytes to set. | ||||
|  */ | ||||
| RarVM.prototype.setMemory = function(pos, buffer, dataSize) { | ||||
|     if (pos < VM_MEMSIZE) { | ||||
|         var numBytes = Math.min(dataSize, VM_MEMSIZE - pos); | ||||
|         for (var i = 0; i < numBytes; ++i) { | ||||
|             this.mem_[pos + i] = buffer[i]; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Static function that reads in the next set of bits for the VM | ||||
|  * (might return 4, 8, 16 or 32 bits). | ||||
|  * @param {bitjs.io.BitStream} bstream A RTL bit stream. | ||||
|  * @return {number} The value of the bits read. | ||||
|  */ | ||||
| RarVM.readData = function(bstream) { | ||||
|     // Read in the first 2 bits. | ||||
|     var flags = bstream.readBits(2); | ||||
|     switch (flags) { // Data&0xc000 | ||||
|         // Return the next 4 bits. | ||||
|         case 0: | ||||
|             return bstream.readBits(4); // (Data>>10)&0xf | ||||
|  | ||||
|         case 1: // 0x4000 | ||||
|             // 0x3c00 => 0011 1100 0000 0000 | ||||
|             if (bstream.peekBits(4) === 0) { // (Data&0x3c00)==0 | ||||
|                 // Skip the 4 zero bits. | ||||
|                 bstream.readBits(4); | ||||
|                 // Read in the next 8 and pad with 1s to 32 bits. | ||||
|                 return (0xffffff00 | bstream.readBits(8)) >>> 0; // ((Data>>2)&0xff) | ||||
|             } | ||||
|  | ||||
|             // Else, read in the next 8. | ||||
|             return bstream.readBits(8); | ||||
|  | ||||
|             // Read in the next 16. | ||||
|         case 2: // 0x8000 | ||||
|             var val = bstream.getBits(); | ||||
|             bstream.readBits(16); | ||||
|             return val; //bstream.readBits(16); | ||||
|  | ||||
|             // case 3 | ||||
|         default: | ||||
|             return (bstream.readBits(16) << 16) | bstream.readBits(16); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| // ============================================================================================== // | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,179 +0,0 @@ | ||||
| /** | ||||
|  * untar.js | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2011 Google Inc. | ||||
|  * | ||||
|  * Reference Documentation: | ||||
|  * | ||||
|  * TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html | ||||
|  */ | ||||
|  | ||||
| /* global bitjs, importScripts, Uint8Array */ | ||||
|  | ||||
| // This file expects to be invoked as a Worker (see onmessage below). | ||||
| importScripts("../io/bytestream.js"); | ||||
| importScripts("archive.js"); | ||||
|  | ||||
| // Progress variables. | ||||
| var currentFilename = ""; | ||||
| var currentFileNumber = 0; | ||||
| var currentBytesUnarchivedInFile = 0; | ||||
| var currentBytesUnarchived = 0; | ||||
| var totalUncompressedBytesInArchive = 0; | ||||
| var totalFilesInArchive = 0; | ||||
| var allLocalFiles = []; | ||||
|  | ||||
| // Helper functions. | ||||
| var info = function(str) { | ||||
|     postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); | ||||
| }; | ||||
| var err = function(str) { | ||||
|     postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); | ||||
| }; | ||||
|  | ||||
| // Removes all characters from the first zero-byte in the string onwards. | ||||
| var readCleanString = function(bstr, numBytes) { | ||||
|     var str = bstr.readString(numBytes); | ||||
|     var zIndex = str.indexOf(String.fromCharCode(0)); | ||||
|     return zIndex != -1 ? str.substr(0, zIndex) : str; | ||||
| }; | ||||
|  | ||||
| var postProgress = function() { | ||||
|     postMessage(new bitjs.archive.UnarchiveProgressEvent( | ||||
|         currentFilename, | ||||
|         currentFileNumber, | ||||
|         currentBytesUnarchivedInFile, | ||||
|         currentBytesUnarchived, | ||||
|         totalUncompressedBytesInArchive, | ||||
|         totalFilesInArchive | ||||
|     )); | ||||
| }; | ||||
|  | ||||
| // takes a ByteStream and parses out the local file information | ||||
| var TarLocalFile = function(bstream) { | ||||
|     this.isValid = false; | ||||
|  | ||||
|     var bytesRead = 0; | ||||
|  | ||||
|     // Read in the header block | ||||
|     this.name = readCleanString(bstream, 100); | ||||
|     this.mode = readCleanString(bstream, 8); | ||||
|     this.uid = readCleanString(bstream, 8); | ||||
|     this.gid = readCleanString(bstream, 8); | ||||
|     this.size = parseInt(readCleanString(bstream, 12), 8); | ||||
|     this.mtime = readCleanString(bstream, 12); | ||||
|     this.chksum = readCleanString(bstream, 8); | ||||
|     this.typeflag = readCleanString(bstream, 1); | ||||
|     this.linkname = readCleanString(bstream, 100); | ||||
|     this.maybeMagic = readCleanString(bstream, 6); | ||||
|  | ||||
|     if (this.maybeMagic === "ustar") { | ||||
|         this.version = readCleanString(bstream, 2); | ||||
|         this.uname = readCleanString(bstream, 32); | ||||
|         this.gname = readCleanString(bstream, 32); | ||||
|         this.devmajor = readCleanString(bstream, 8); | ||||
|         this.devminor = readCleanString(bstream, 8); | ||||
|         this.prefix = readCleanString(bstream, 155); | ||||
|  | ||||
|         if (this.prefix.length) { | ||||
|             this.name = this.prefix + this.name; | ||||
|         } | ||||
|         bstream.readBytes(12); // 512 - 500 | ||||
|     } else { | ||||
|         bstream.readBytes(255); // 512 - 257 | ||||
|     } | ||||
|  | ||||
|     bytesRead += 512; | ||||
|  | ||||
|     // Done header, now rest of blocks are the file contents. | ||||
|     this.filename = this.name; | ||||
|     this.fileData = null; | ||||
|  | ||||
|     info("Untarring file '" + this.filename + "'"); | ||||
|     info("  size = " + this.size); | ||||
|     info("  typeflag = " + this.typeflag); | ||||
|  | ||||
|     // A regular file. | ||||
|     if (this.typeflag == 0) { | ||||
|         info("  This is a regular file."); | ||||
|         var sizeInBytes = parseInt(this.size); | ||||
|         this.fileData = new Uint8Array(bstream.readBytes(sizeInBytes)); | ||||
|         bytesRead += sizeInBytes; | ||||
|         if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) { | ||||
|             this.isValid = true; | ||||
|         } | ||||
|  | ||||
|         // Round up to 512-byte blocks. | ||||
|         var remaining = 512 - (bytesRead % 512); | ||||
|         if (remaining > 0 && remaining < 512) { | ||||
|             bstream.readBytes(remaining); | ||||
|         } | ||||
|     } else if (this.typeflag == 5) { | ||||
|         info("  This is a directory."); | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| var untar = function(arrayBuffer) { | ||||
|     postMessage(new bitjs.archive.UnarchiveStartEvent()); | ||||
|     currentFilename = ""; | ||||
|     currentFileNumber = 0; | ||||
|     currentBytesUnarchivedInFile = 0; | ||||
|     currentBytesUnarchived = 0; | ||||
|     totalUncompressedBytesInArchive = 0; | ||||
|     totalFilesInArchive = 0; | ||||
|     allLocalFiles = []; | ||||
|  | ||||
|     var bstream = new bitjs.io.ByteStream(arrayBuffer); | ||||
|     postProgress(); | ||||
|     /* | ||||
|     // go through whole file, read header of each block and memorize, filepointer | ||||
|     */ | ||||
|     while (bstream.peekNumber(4) !== 0) { | ||||
|         var localFile = new TarLocalFile(bstream); | ||||
|         allLocalFiles.push(localFile); | ||||
|         postProgress(); | ||||
|     } | ||||
|     // got all local files, now sort them | ||||
|     allLocalFiles.sort(alphanumCase); | ||||
|  | ||||
|     allLocalFiles.forEach(function(oneLocalFile) { | ||||
|         // While we don't encounter an empty block, keep making TarLocalFiles. | ||||
|         if (oneLocalFile && oneLocalFile.isValid) { | ||||
|             // If we make it to this point and haven't thrown an error, we have successfully | ||||
|             // read in the data for a local file, so we can update the actual bytestream. | ||||
|             totalUncompressedBytesInArchive += oneLocalFile.size; | ||||
|  | ||||
|             // update progress | ||||
|             currentFilename = oneLocalFile.filename; | ||||
|             currentFileNumber = totalFilesInArchive++; | ||||
|             currentBytesUnarchivedInFile = oneLocalFile.size; | ||||
|             currentBytesUnarchived += oneLocalFile.size; | ||||
|             postMessage(new bitjs.archive.UnarchiveExtractEvent(oneLocalFile)); | ||||
|             postProgress(); | ||||
|         } | ||||
|     }); | ||||
|     totalFilesInArchive = allLocalFiles.length; | ||||
|  | ||||
|     postProgress(); | ||||
|     postMessage(new bitjs.archive.UnarchiveFinishEvent()); | ||||
| }; | ||||
|  | ||||
| // event.data.file has the first ArrayBuffer. | ||||
| // event.data.bytes has all subsequent ArrayBuffers. | ||||
| onmessage = function(event) { | ||||
|     try { | ||||
|         untar(event.data.file, true); | ||||
|     } catch (e) { | ||||
|         if (typeof e === "string" && e.startsWith("Error!  Overflowed")) { | ||||
|             // Overrun the buffer. | ||||
|             // unarchiveState = UnarchiveState.WAITING; | ||||
|         } else { | ||||
|             err("Found an error while untarring"); | ||||
|             err(e); | ||||
|             throw e; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| @@ -1,660 +0,0 @@ | ||||
| /** | ||||
|  * unzip.js | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2011 Google Inc. | ||||
|  * Copyright(c) 2011 antimatter15 | ||||
|  * | ||||
|  * Reference Documentation: | ||||
|  * | ||||
|  * ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT | ||||
|  * DEFLATE format: http://tools.ietf.org/html/rfc1951 | ||||
|  */ | ||||
| /* global bitjs, importScripts, Uint8Array*/ | ||||
|  | ||||
| // This file expects to be invoked as a Worker (see onmessage below). | ||||
| importScripts("../io/bitstream.js"); | ||||
| importScripts("../io/bytebuffer.js"); | ||||
| importScripts("../io/bytestream.js"); | ||||
| importScripts("archive.js"); | ||||
|  | ||||
| // Progress variables. | ||||
| var currentFilename = ""; | ||||
| var currentFileNumber = 0; | ||||
| var currentBytesUnarchivedInFile = 0; | ||||
| var currentBytesUnarchived = 0; | ||||
| var totalUncompressedBytesInArchive = 0; | ||||
| var totalFilesInArchive = 0; | ||||
|  | ||||
| // Helper functions. | ||||
| var info = function(str) { | ||||
|     postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); | ||||
| }; | ||||
| var err = function(str) { | ||||
|     postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); | ||||
| }; | ||||
| var postProgress = function() { | ||||
|     postMessage(new bitjs.archive.UnarchiveProgressEvent( | ||||
|         currentFilename, | ||||
|         currentFileNumber, | ||||
|         currentBytesUnarchivedInFile, | ||||
|         currentBytesUnarchived, | ||||
|         totalUncompressedBytesInArchive, | ||||
|         totalFilesInArchive)); | ||||
| }; | ||||
|  | ||||
| var zLocalFileHeaderSignature = 0x04034b50; | ||||
| var zArchiveExtraDataSignature = 0x08064b50; | ||||
| var zCentralFileHeaderSignature = 0x02014b50; | ||||
| var zDigitalSignatureSignature = 0x05054b50; | ||||
|  | ||||
| // takes a ByteStream and parses out the local file information | ||||
| var ZipLocalFile = function(bstream) { | ||||
|     if (typeof bstream !== typeof {} || !bstream.readNumber || typeof bstream.readNumber !== typeof function() {}) { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     bstream.readNumber(4); // swallow signature | ||||
|     this.version = bstream.readNumber(2); | ||||
|     this.generalPurpose = bstream.readNumber(2); | ||||
|     this.compressionMethod = bstream.readNumber(2); | ||||
|     this.lastModFileTime = bstream.readNumber(2); | ||||
|     this.lastModFileDate = bstream.readNumber(2); | ||||
|     this.crc32 = bstream.readNumber(4); | ||||
|     this.compressedSize = bstream.readNumber(4); | ||||
|     this.uncompressedSize = bstream.readNumber(4); | ||||
|     this.fileNameLength = bstream.readNumber(2); | ||||
|     this.extraFieldLength = bstream.readNumber(2); | ||||
|  | ||||
|     this.filename = null; | ||||
|     if (this.fileNameLength > 0) { | ||||
|         this.filename = bstream.readString(this.fileNameLength); | ||||
|     } | ||||
|  | ||||
|     this.extraField = null; | ||||
|     if (this.extraFieldLength > 0) { | ||||
|         this.extraField = bstream.readString(this.extraFieldLength); | ||||
|         info(" extra field=" + this.extraField); | ||||
|     } | ||||
|  | ||||
|     // read in the compressed data | ||||
|     this.fileData = null; | ||||
|     if (this.compressedSize > 0) { | ||||
|         this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); | ||||
|         bstream.ptr += this.compressedSize; | ||||
|     } | ||||
|  | ||||
|     // TODO: deal with data descriptor if present (we currently assume no data descriptor!) | ||||
|     // "This descriptor exists only if bit 3 of the general purpose bit flag is set" | ||||
|     // But how do you figure out how big the file data is if you don't know the compressedSize | ||||
|     // from the header?!? | ||||
|     if ((this.generalPurpose & bitjs.BIT[3]) !== 0) { | ||||
|         this.crc32 = bstream.readNumber(4); | ||||
|         this.compressedSize = bstream.readNumber(4); | ||||
|         this.uncompressedSize = bstream.readNumber(4); | ||||
|     } | ||||
|  | ||||
|     // Now that we have all the bytes for this file, we can print out some information. | ||||
|     info("Zip Local File Header:"); | ||||
|     info(" version=" + this.version); | ||||
|     info(" general purpose=" + this.generalPurpose); | ||||
|     info(" compression method=" + this.compressionMethod); | ||||
|     info(" last mod file time=" + this.lastModFileTime); | ||||
|     info(" last mod file date=" + this.lastModFileDate); | ||||
|     info(" crc32=" + this.crc32); | ||||
|     info(" compressed size=" + this.compressedSize); | ||||
|     info(" uncompressed size=" + this.uncompressedSize); | ||||
|     info(" file name length=" + this.fileNameLength); | ||||
|     info(" extra field length=" + this.extraFieldLength); | ||||
|     info(" filename = '" + this.filename + "'"); | ||||
|  | ||||
| }; | ||||
|  | ||||
| // determine what kind of compressed data we have and decompress | ||||
| ZipLocalFile.prototype.unzip = function() { | ||||
|  | ||||
|     // Zip Version 1.0, no compression (store only) | ||||
|     if (this.compressionMethod === 0 ) { | ||||
|         info("ZIP v" + this.version + ", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); | ||||
|         currentBytesUnarchivedInFile = this.compressedSize; | ||||
|         currentBytesUnarchived += this.compressedSize; | ||||
|         this.fileData = zeroCompression(this.fileData, this.uncompressedSize); | ||||
|     } else if (this.compressionMethod === 8) { | ||||
|         // version == 20, compression method == 8 (DEFLATE) | ||||
|         info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); | ||||
|         this.fileData = inflate(this.fileData, this.uncompressedSize); | ||||
|     } else { | ||||
|         err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); | ||||
|         this.fileData = null; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| // Takes an ArrayBuffer of a zip file in | ||||
| // returns null on error | ||||
| // returns an array of DecompressedFile objects on success | ||||
| // ToDo This function differs | ||||
| var unzip = function(arrayBuffer) { | ||||
|     postMessage(new bitjs.archive.UnarchiveStartEvent()); | ||||
|  | ||||
|     currentFilename = ""; | ||||
|     currentFileNumber = 0; | ||||
|     currentBytesUnarchivedInFile = 0; | ||||
|     currentBytesUnarchived = 0; | ||||
|     totalUncompressedBytesInArchive = 0; | ||||
|     totalFilesInArchive = 0; | ||||
|     currentBytesUnarchived = 0; | ||||
|  | ||||
|     var bstream = new bitjs.io.ByteStream(arrayBuffer); | ||||
|     // detect local file header signature or return null | ||||
|     if (bstream.peekNumber(4) === zLocalFileHeaderSignature) { | ||||
|         var localFiles = []; | ||||
|         // loop until we don't see any more local files | ||||
|         while (bstream.peekNumber(4) === zLocalFileHeaderSignature) { | ||||
|             var oneLocalFile = new ZipLocalFile(bstream); | ||||
|             // this should strip out directories/folders | ||||
|             if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { | ||||
|                 localFiles.push(oneLocalFile); | ||||
|                 totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; | ||||
|             } | ||||
|         } | ||||
|         totalFilesInArchive = localFiles.length; | ||||
|  | ||||
|         // got all local files, now sort them | ||||
|         localFiles.sort(alphanumCase); | ||||
|  | ||||
|         // archive extra data record | ||||
|         if (bstream.peekNumber(4) === zArchiveExtraDataSignature) { | ||||
|             info(" Found an Archive Extra Data Signature"); | ||||
|  | ||||
|             // skipping this record for now | ||||
|             bstream.readNumber(4); | ||||
|             var archiveExtraFieldLength = bstream.readNumber(4); | ||||
|             bstream.readString(archiveExtraFieldLength); | ||||
|         } | ||||
|  | ||||
|         // central directory structure | ||||
|         // TODO: handle the rest of the structures (Zip64 stuff) | ||||
|         if (bstream.peekNumber(4) === zCentralFileHeaderSignature) { | ||||
|             info(" Found a Central File Header"); | ||||
|  | ||||
|             // read all file headers | ||||
|             while (bstream.peekNumber(4) === zCentralFileHeaderSignature) { | ||||
|                 bstream.readNumber(4); // signature | ||||
|                 bstream.readNumber(2); // version made by | ||||
|                 bstream.readNumber(2); // version needed to extract | ||||
|                 bstream.readNumber(2); // general purpose bit flag | ||||
|                 bstream.readNumber(2); // compression method | ||||
|                 bstream.readNumber(2); // last mod file time | ||||
|                 bstream.readNumber(2); // last mod file date | ||||
|                 bstream.readNumber(4); // crc32 | ||||
|                 bstream.readNumber(4); // compressed size | ||||
|                 bstream.readNumber(4); // uncompressed size | ||||
|                 var fileNameLength = bstream.readNumber(2); // file name length | ||||
|                 var extraFieldLength = bstream.readNumber(2); // extra field length | ||||
|                 var fileCommentLength = bstream.readNumber(2); // file comment length | ||||
|                 bstream.readNumber(2); // disk number start | ||||
|                 bstream.readNumber(2); // internal file attributes | ||||
|                 bstream.readNumber(4); // external file attributes | ||||
|                 bstream.readNumber(4); // relative offset of local header | ||||
|  | ||||
|                 bstream.readString(fileNameLength); // file name | ||||
|                 bstream.readString(extraFieldLength); // extra field | ||||
|                 bstream.readString(fileCommentLength); // file comment | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // digital signature | ||||
|         if (bstream.peekNumber(4) === zDigitalSignatureSignature) { | ||||
|             info(" Found a Digital Signature"); | ||||
|  | ||||
|             bstream.readNumber(4); | ||||
|             var sizeOfSignature = bstream.readNumber(2); | ||||
|             bstream.readString(sizeOfSignature); // digital signature data | ||||
|         } | ||||
|  | ||||
|         // report # files and total length | ||||
|         if (localFiles.length > 0) { | ||||
|             postProgress(); | ||||
|         } | ||||
|  | ||||
|         // now do the unzipping of each file | ||||
|         for (var i = 0; i < localFiles.length; ++i) { | ||||
|             var localfile = localFiles[i]; | ||||
|  | ||||
|             // update progress | ||||
|             currentFilename = localfile.filename; | ||||
|             currentFileNumber = i; | ||||
|             currentBytesUnarchivedInFile = 0; | ||||
|  | ||||
|             // actually do the unzipping | ||||
|             localfile.unzip(); | ||||
|  | ||||
|             if (localfile.fileData !== null) { | ||||
|                 postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); | ||||
|                 postProgress(); | ||||
|             } | ||||
|         } | ||||
|         postProgress(); | ||||
|         postMessage(new bitjs.archive.UnarchiveFinishEvent()); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| // returns a table of Huffman codes | ||||
| // each entry's index is its code and its value is a JavaScript object | ||||
| // containing {length: 6, symbol: X} | ||||
| function getHuffmanCodes(bitLengths) { | ||||
|     // ensure bitLengths is an array containing at least one element | ||||
|     if (typeof bitLengths !== typeof [] || bitLengths.length < 1) { | ||||
|         err("Error! getHuffmanCodes() called with an invalid array"); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     // Reference: http://tools.ietf.org/html/rfc1951#page-8 | ||||
|     var numLengths = bitLengths.length; | ||||
|     var blCount = []; | ||||
|     var MAX_BITS = 1; | ||||
|  | ||||
|     // Step 1: count up how many codes of each length we have | ||||
|     for (var i = 0; i < numLengths; ++i) { | ||||
|         var length = bitLengths[i]; | ||||
|         // test to ensure each bit length is a positive, non-zero number | ||||
|         if (typeof length !== typeof 1 || length < 0) { | ||||
|             err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); | ||||
|             return null; | ||||
|         } | ||||
|         // increment the appropriate bitlength count | ||||
|         if (typeof blCount[length] === "undefined") blCount[length] = 0; | ||||
|         // a length of zero means this symbol is not participating in the huffman coding | ||||
|         if (length > 0) blCount[length]++; | ||||
|  | ||||
|         if (length > MAX_BITS) MAX_BITS = length; | ||||
|     } | ||||
|  | ||||
|     // Step 2: Find the numerical value of the smallest code for each code length | ||||
|     var nextCode = []; | ||||
|     var code = 0; | ||||
|     for (var bits = 1; bits <= MAX_BITS; ++bits) { | ||||
|         var length2 = bits - 1; | ||||
|         // ensure undefined lengths are zero | ||||
|         if (typeof blCount[length2] === "undefined") blCount[length2] = 0; | ||||
|         code = (code + blCount[bits - 1]) << 1; | ||||
|         nextCode [bits] = code; | ||||
|     } | ||||
|  | ||||
|     // Step 3: Assign numerical values to all codes | ||||
|     var table = {}; | ||||
|     var tableLength = 0; | ||||
|     for (var n = 0; n < numLengths; ++n) { | ||||
|         var len = bitLengths[n]; | ||||
|         if (len !== 0) { | ||||
|             table[nextCode [len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(nextCode [len],len) }; | ||||
|             tableLength++; | ||||
|             nextCode [len]++; | ||||
|         } | ||||
|     } | ||||
|     table.maxLength = tableLength; | ||||
|  | ||||
|     return table; | ||||
| } | ||||
|  | ||||
| /* | ||||
|      The Huffman codes for the two alphabets are fixed, and are not | ||||
|      represented explicitly in the data.  The Huffman code lengths | ||||
|      for the literal/length alphabet are: | ||||
|  | ||||
|                Lit Value    Bits        Codes | ||||
|                ---------    ----        ----- | ||||
|                  0 - 143     8          00110000 through | ||||
|                                         10111111 | ||||
|                144 - 255     9          110010000 through | ||||
|                                         111111111 | ||||
|                256 - 279     7          0000000 through | ||||
|                                         0010111 | ||||
|                280 - 287     8          11000000 through | ||||
|                                         11000111 | ||||
| */ | ||||
| // fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits | ||||
| var fixedHCtoLiteral = null; | ||||
| var fixedHCtoDistance = null; | ||||
|  | ||||
| function getFixedLiteralTable() { | ||||
|     // create once | ||||
|     if (!fixedHCtoLiteral) { | ||||
|         var bitlengths = new Array(288); | ||||
|         var i; | ||||
|         for (i = 0; i <= 143; ++i) bitlengths[i] = 8; | ||||
|         for (i = 144; i <= 255; ++i) bitlengths[i] = 9; | ||||
|         for (i = 256; i <= 279; ++i) bitlengths[i] = 7; | ||||
|         for (i = 280; i <= 287; ++i) bitlengths[i] = 8; | ||||
|  | ||||
|         // get huffman code table | ||||
|         fixedHCtoLiteral = getHuffmanCodes(bitlengths); | ||||
|     } | ||||
|     return fixedHCtoLiteral; | ||||
| } | ||||
|  | ||||
| function getFixedDistanceTable() { | ||||
|     // create once | ||||
|     if (!fixedHCtoDistance) { | ||||
|         var bitlengths = new Array(32); | ||||
|         for (var i = 0; i < 32; ++i) { | ||||
|             bitlengths[i] = 5; | ||||
|         } | ||||
|  | ||||
|         // get huffman code table | ||||
|         fixedHCtoDistance = getHuffmanCodes(bitlengths); | ||||
|     } | ||||
|     return fixedHCtoDistance; | ||||
| } | ||||
|  | ||||
| // extract one bit at a time until we find a matching Huffman Code | ||||
| // then return that symbol | ||||
| function decodeSymbol(bstream, hcTable) { | ||||
|     var code = 0; | ||||
|     var len = 0; | ||||
|  | ||||
|     // loop until we match | ||||
|     for (;;) { | ||||
|         // read in next bit | ||||
|         var bit = bstream.readBits(1); | ||||
|         code = (code << 1) | bit; | ||||
|         ++len; | ||||
|  | ||||
|         // check against Huffman Code table and break if found | ||||
|         if (hcTable.hasOwnProperty(code) && hcTable[code].length === len) { | ||||
|             break; | ||||
|         } | ||||
|         if (len > hcTable.maxLength) { | ||||
|             err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + | ||||
|                 " and table only max code length of " + hcTable.maxLength); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return hcTable[code].symbol; | ||||
| } | ||||
|  | ||||
|  | ||||
| var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; | ||||
| /* | ||||
|      Extra               Extra               Extra | ||||
| Code Bits Length(s) Code Bits Lengths   Code Bits Length(s) | ||||
| ---- ---- ------     ---- ---- -------   ---- ---- ------- | ||||
|  257   0     3       267   1   15,16     277   4   67-82 | ||||
|  258   0     4       268   1   17,18     278   4   83-98 | ||||
|  259   0     5       269   2   19-22     279   4   99-114 | ||||
|  260   0     6       270   2   23-26     280   4  115-130 | ||||
|  261   0     7       271   2   27-30     281   5  131-162 | ||||
|  262   0     8       272   2   31-34     282   5  163-194 | ||||
|  263   0     9       273   3   35-42     283   5  195-226 | ||||
|  264   0    10       274   3   43-50     284   5  227-257 | ||||
|  265   1  11,12      275   3   51-58     285   0    258 | ||||
|  266   1  13,14      276   3   59-66 | ||||
| */ | ||||
| var LengthLookupTable = [ | ||||
|     [0, 3], | ||||
|     [0, 4], | ||||
|     [0, 5], | ||||
|     [0, 6], | ||||
|     [0, 7], | ||||
|     [0, 8], | ||||
|     [0, 9], | ||||
|     [0, 10], | ||||
|     [1, 11], | ||||
|     [1, 13], | ||||
|     [1, 15], | ||||
|     [1, 17], | ||||
|     [2, 19], | ||||
|     [2, 23], | ||||
|     [2, 27], | ||||
|     [2, 31], | ||||
|     [3, 35], | ||||
|     [3, 43], | ||||
|     [3, 51], | ||||
|     [3, 59], | ||||
|     [4, 67], | ||||
|     [4, 83], | ||||
|     [4, 99], | ||||
|     [4, 115], | ||||
|     [5, 131], | ||||
|     [5, 163], | ||||
|     [5, 195], | ||||
|     [5, 227], | ||||
|     [0, 258] | ||||
| ]; | ||||
| /* | ||||
|       Extra           Extra                Extra | ||||
|  Code Bits Dist  Code Bits   Dist     Code Bits Distance | ||||
|  ---- ---- ----  ---- ----  ------    ---- ---- -------- | ||||
|    0   0    1     10   4     33-48    20    9   1025-1536 | ||||
|    1   0    2     11   4     49-64    21    9   1537-2048 | ||||
|    2   0    3     12   5     65-96    22   10   2049-3072 | ||||
|    3   0    4     13   5     97-128   23   10   3073-4096 | ||||
|    4   1   5,6    14   6    129-192   24   11   4097-6144 | ||||
|    5   1   7,8    15   6    193-256   25   11   6145-8192 | ||||
|    6   2   9-12   16   7    257-384   26   12  8193-12288 | ||||
|    7   2  13-16   17   7    385-512   27   12 12289-16384 | ||||
|    8   3  17-24   18   8    513-768   28   13 16385-24576 | ||||
|    9   3  25-32   19   8   769-1024   29   13 24577-32768 | ||||
| */ | ||||
| var DistLookupTable = [ | ||||
|     [0, 1], | ||||
|     [0, 2], | ||||
|     [0, 3], | ||||
|     [0, 4], | ||||
|     [1, 5], | ||||
|     [1, 7], | ||||
|     [2, 9], | ||||
|     [2, 13], | ||||
|     [3, 17], | ||||
|     [3, 25], | ||||
|     [4, 33], | ||||
|     [4, 49], | ||||
|     [5, 65], | ||||
|     [5, 97], | ||||
|     [6, 129], | ||||
|     [6, 193], | ||||
|     [7, 257], | ||||
|     [7, 385], | ||||
|     [8, 513], | ||||
|     [8, 769], | ||||
|     [9, 1025], | ||||
|     [9, 1537], | ||||
|     [10, 2049], | ||||
|     [10, 3073], | ||||
|     [11, 4097], | ||||
|     [11, 6145], | ||||
|     [12, 8193], | ||||
|     [12, 12289], | ||||
|     [13, 16385], | ||||
|     [13, 24577] | ||||
| ]; | ||||
|  | ||||
| function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { | ||||
|     /* | ||||
|           loop (until end of block code recognized) | ||||
|              decode literal/length value from input stream | ||||
|              if value < 256 | ||||
|                 copy value (literal byte) to output stream | ||||
|              otherwise | ||||
|                 if value = end of block (256) | ||||
|                    break from loop | ||||
|                 otherwise (value = 257..285) | ||||
|                    decode distance from input stream | ||||
|  | ||||
|                    move backwards distance bytes in the output | ||||
|                    stream, and copy length bytes from this | ||||
|                    position to the output stream. | ||||
|     */ | ||||
|     var blockSize = 0; | ||||
|     for (;;) { | ||||
|         var symbol = decodeSymbol(bstream, hcLiteralTable); | ||||
|         if (symbol < 256) { | ||||
|             // copy literal byte to output | ||||
|             buffer.insertByte(symbol); | ||||
|             blockSize++; | ||||
|         } else { | ||||
|             // end of block reached | ||||
|             if (symbol === 256) { | ||||
|                 break; | ||||
|             } else { | ||||
|                 var lengthLookup = LengthLookupTable[symbol - 257]; | ||||
|                 var length = lengthLookup[1] + bstream.readBits(lengthLookup[0]); | ||||
|                 var distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)]; | ||||
|                 var distance = distLookup[1] + bstream.readBits(distLookup[0]); | ||||
|  | ||||
|                 // now apply length and distance appropriately and copy to output | ||||
|  | ||||
|                 // TODO: check that backward distance < data.length? | ||||
|  | ||||
|                 // http://tools.ietf.org/html/rfc1951#page-11 | ||||
|                 // "Note also that the referenced string may overlap the current | ||||
|                 //  position; for example, if the last 2 bytes decoded have values | ||||
|                 //  X and Y, a string reference with <length = 5, distance = 2> | ||||
|                 //  adds X,Y,X,Y,X to the output stream." | ||||
|                 // | ||||
|                 // loop for each character | ||||
|                 var ch = buffer.ptr - distance; | ||||
|                 blockSize += length; | ||||
|                 if (length > distance) { | ||||
|                     var data = buffer.data; | ||||
|                     while (length--) { | ||||
|                         buffer.insertByte(data[ch++]); | ||||
|                     } | ||||
|                 } else { | ||||
|                     buffer.insertBytes(buffer.data.subarray(ch, ch + length)); | ||||
|                 } | ||||
|             } // length-distance pair | ||||
|         } // length-distance pair or end-of-block | ||||
|     } // loop until we reach end of block | ||||
|     return blockSize; | ||||
| } | ||||
|  | ||||
| function zeroCompression(compressedData, numDecompressedBytes) { | ||||
|     var bstream = new bitjs.io.BitStream(compressedData.buffer, | ||||
|         false /* rtl */, | ||||
|         compressedData.byteOffset, | ||||
|         compressedData.byteLength); | ||||
|     var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); | ||||
|     buffer.insertBytes(bstream.readBytes(numDecompressedBytes)); | ||||
|     return buffer.data; | ||||
| } | ||||
|  | ||||
| // {Uint8Array} compressedData A Uint8Array of the compressed file data. | ||||
| // compression method 8 | ||||
| // deflate: http://tools.ietf.org/html/rfc1951 | ||||
| function inflate(compressedData, numDecompressedBytes) { | ||||
|     // Bit stream representing the compressed data. | ||||
|     var bstream = new bitjs.io.BitStream(compressedData.buffer, | ||||
|         false /* rtl */, | ||||
|         compressedData.byteOffset, | ||||
|         compressedData.byteLength); | ||||
|     var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); | ||||
|     var blockSize = 0; | ||||
|  | ||||
|     // block format: http://tools.ietf.org/html/rfc1951#page-9 | ||||
|     var bFinal = 0; | ||||
|     do { | ||||
|         bFinal = bstream.readBits(1); | ||||
|         var bType = bstream.readBits(2); | ||||
|         blockSize = 0; | ||||
|         // ++numBlocks; | ||||
|         // no compression | ||||
|         if (bType === 0) { | ||||
|             // skip remaining bits in this byte | ||||
|             while (bstream.bitPtr !== 0) bstream.readBits(1); | ||||
|             var len = bstream.readBits(16); | ||||
|             bstream.readBits(16); | ||||
|             // TODO: check if nlen is the ones-complement of len? | ||||
|  | ||||
|             if (len > 0) buffer.insertBytes(bstream.readBytes(len)); | ||||
|             blockSize = len; | ||||
|         } else if (bType === 1) { | ||||
|             // fixed Huffman codes | ||||
|             blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); | ||||
|         } else if (bType === 2) { | ||||
|             // dynamic Huffman codes | ||||
|             var numLiteralLengthCodes = bstream.readBits(5) + 257; | ||||
|             var numDistanceCodes = bstream.readBits(5) + 1, | ||||
|                 numCodeLengthCodes = bstream.readBits(4) + 4; | ||||
|  | ||||
|             // populate the array of code length codes (first de-compaction) | ||||
|             var codeLengthsCodeLengths = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | ||||
|             for (var i = 0; i < numCodeLengthCodes; ++i) { | ||||
|                 codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); | ||||
|             } | ||||
|  | ||||
|             // get the Huffman Codes for the code lengths | ||||
|             var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); | ||||
|  | ||||
|             // now follow this mapping | ||||
|             /* | ||||
|                0 - 15: Represent code lengths of 0 - 15 | ||||
|                    16: Copy the previous code length 3 - 6 times. | ||||
|                        The next 2 bits indicate repeat length | ||||
|                              (0 = 3, ... , 3 = 6) | ||||
|                           Example:  Codes 8, 16 (+2 bits 11), | ||||
|                                     16 (+2 bits 10) will expand to | ||||
|                                     12 code lengths of 8 (1 + 6 + 5) | ||||
|                    17: Repeat a code length of 0 for 3 - 10 times. | ||||
|                        (3 bits of length) | ||||
|                    18: Repeat a code length of 0 for 11 - 138 times | ||||
|                        (7 bits of length) | ||||
|             */ | ||||
|             // to generate the true code lengths of the Huffman Codes for the literal | ||||
|             // and distance tables together | ||||
|             var literalCodeLengths = []; | ||||
|             var prevCodeLength = 0; | ||||
|             while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { | ||||
|                 var symbol = decodeSymbol(bstream, codeLengthsCodes); | ||||
|                 if (symbol <= 15) { | ||||
|                     literalCodeLengths.push(symbol); | ||||
|                     prevCodeLength = symbol; | ||||
|                 } else if (symbol === 16) { | ||||
|                     var repeat = bstream.readBits(2) + 3; | ||||
|                     while (repeat--) { | ||||
|                         literalCodeLengths.push(prevCodeLength); | ||||
|                     } | ||||
|                 } else if (symbol === 17) { | ||||
|                     var repeat1 = bstream.readBits(3) + 3; | ||||
|                     while (repeat1--) { | ||||
|                         literalCodeLengths.push(0); | ||||
|                     } | ||||
|                 } else if (symbol === 18) { | ||||
|                     var repeat2 = bstream.readBits(7) + 11; | ||||
|                     while (repeat2--) { | ||||
|                         literalCodeLengths.push(0); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // now split the distance code lengths out of the literal code array | ||||
|             var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); | ||||
|  | ||||
|             // now generate the true Huffman Code tables using these code lengths | ||||
|             var hcLiteralTable = getHuffmanCodes(literalCodeLengths); | ||||
|             var hcDistanceTable = getHuffmanCodes(distanceCodeLengths); | ||||
|             blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); | ||||
|         } else { | ||||
|             // error | ||||
|             err("Error! Encountered deflate block of type 3"); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // update progress | ||||
|         currentBytesUnarchivedInFile += blockSize; | ||||
|         currentBytesUnarchived += blockSize; | ||||
|         postProgress(); | ||||
|  | ||||
|     } while (bFinal !== 1); | ||||
|     // we are done reading blocks if the bFinal bit was set for this block | ||||
|  | ||||
|     // return the buffer data bytes | ||||
|     return buffer.data; | ||||
| } | ||||
|  | ||||
| // event.data.file has the ArrayBuffer. | ||||
| onmessage = function(event) { | ||||
|     unzip(event.data.file, true); | ||||
| }; | ||||
							
								
								
									
										9155
									
								
								cps/static/js/compress/jszip.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9155
									
								
								cps/static/js/compress/jszip.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										434
									
								
								cps/static/js/compress/libunrar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								cps/static/js/compress/libunrar.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								cps/static/js/compress/libunrar.js.mem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cps/static/js/compress/libunrar.js.mem
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										90
									
								
								cps/static/js/compress/libuntar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								cps/static/js/compress/libuntar.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| // Copyright (c) 2017 Matthew Brennan Jones <matthew.brennan.jones@gmail.com> | ||||
| // This software is licensed under a MIT License | ||||
| // https://github.com/workhorsy/uncompress.js | ||||
|  | ||||
| "use strict"; | ||||
|  | ||||
| // Based on the information from: | ||||
| // https://en.wikipedia.org/wiki/Tar_(computing) | ||||
|  | ||||
|  | ||||
| (function() { | ||||
|  | ||||
| const TAR_TYPE_FILE = 0; | ||||
| const TAR_TYPE_DIR = 5; | ||||
|  | ||||
| const TAR_HEADER_SIZE = 512; | ||||
| const TAR_TYPE_OFFSET = 156; | ||||
| const TAR_TYPE_SIZE = 1; | ||||
| const TAR_SIZE_OFFSET = 124; | ||||
| const TAR_SIZE_SIZE = 12; | ||||
| const TAR_NAME_OFFSET = 0; | ||||
| const TAR_NAME_SIZE = 100; | ||||
|  | ||||
| function _tarRead(view, offset, size) { | ||||
| 	return view.slice(offset, offset + size); | ||||
| } | ||||
|  | ||||
| function tarGetEntries(filename, array_buffer) { | ||||
| 	let view = new Uint8Array(array_buffer); | ||||
| 	let offset = 0; | ||||
| 	let entries = []; | ||||
|  | ||||
| 	while (offset + TAR_HEADER_SIZE < view.byteLength) { | ||||
| 		// Get entry name | ||||
| 		let entry_name = saneMap(_tarRead(view, offset + TAR_NAME_OFFSET, TAR_NAME_SIZE), String.fromCharCode); | ||||
| 		entry_name = entry_name.join('').replace(/\0/g, ''); | ||||
|  | ||||
| 		// No entry name, so probably the last block | ||||
| 		if (entry_name.length === 0) { | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		// Get entry size | ||||
| 		let entry_size = parseInt(saneJoin(saneMap(_tarRead(view, offset + TAR_SIZE_OFFSET, TAR_SIZE_SIZE), String.fromCharCode), ''), 8); | ||||
| 		let entry_type = saneMap(_tarRead(view, offset + TAR_TYPE_OFFSET, TAR_TYPE_SIZE), String.fromCharCode) | 0; | ||||
|  | ||||
| 		// Save this as en entry if it is a file or directory | ||||
| 		if (entry_type === TAR_TYPE_FILE || entry_type === TAR_TYPE_DIR) { | ||||
| 			let entry = { | ||||
| 				name: entry_name, | ||||
| 				size: entry_size, | ||||
| 				is_file: entry_type == TAR_TYPE_FILE, | ||||
| 				offset: offset | ||||
| 			}; | ||||
| 			entries.push(entry); | ||||
| 		} | ||||
|  | ||||
| 		// Round the offset up to be divisible by TAR_HEADER_SIZE | ||||
| 		offset += (entry_size + TAR_HEADER_SIZE); | ||||
| 		if (offset % TAR_HEADER_SIZE > 0) { | ||||
| 			let even = (offset / TAR_HEADER_SIZE) | 0; // number of times it goes evenly into TAR_HEADER_SIZE | ||||
| 			offset = (even + 1) * TAR_HEADER_SIZE; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return entries; | ||||
| } | ||||
|  | ||||
| function tarGetEntryData(entry, array_buffer) { | ||||
| 	let view = new Uint8Array(array_buffer); | ||||
| 	let offset = entry.offset; | ||||
| 	let size = entry.size; | ||||
|  | ||||
| 	// Get entry data | ||||
| 	let entry_data = _tarRead(view, offset + TAR_HEADER_SIZE, size); | ||||
| 	return entry_data; | ||||
| } | ||||
|  | ||||
| // Figure out if we are running in a Window or Web Worker | ||||
| let scope = null; | ||||
| if (typeof window === 'object') { | ||||
| 	scope = window; | ||||
| } else if (typeof importScripts === 'function') { | ||||
| 	scope = self; | ||||
| } | ||||
|  | ||||
| // Set exports | ||||
| scope.tarGetEntries = tarGetEntries; | ||||
| scope.tarGetEntryData = tarGetEntryData; | ||||
| })(); | ||||
							
								
								
									
										420
									
								
								cps/static/js/compress/uncompress.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								cps/static/js/compress/uncompress.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,420 @@ | ||||
| // Copyright (c) 2017 Matthew Brennan Jones <matthew.brennan.jones@gmail.com> | ||||
| // This software is licensed under a MIT License | ||||
| // https://github.com/workhorsy/uncompress.js | ||||
|  | ||||
| "use strict"; | ||||
|  | ||||
|  | ||||
| function loadScript(url, cb) { | ||||
| 	// Window | ||||
| 	if (typeof window === 'object') { | ||||
| 		let script = document.createElement('script'); | ||||
| 		script.type = "text/javascript"; | ||||
| 		script.src = url; | ||||
| 		script.onload = function() { | ||||
| 			if (cb) cb(); | ||||
| 		}; | ||||
| 		document.head.appendChild(script); | ||||
| 	// Web Worker | ||||
| 	} else if (typeof importScripts === 'function') { | ||||
| 		importScripts(url); | ||||
| 		if (cb) cb(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function currentScriptPath() { | ||||
| 	// NOTE: document.currentScript does not work in a Web Worker | ||||
| 	// So we have to parse a stack trace maually | ||||
| 	try { | ||||
| 		throw new Error(''); | ||||
| 	} catch(e) { | ||||
| 		let stack = e.stack; | ||||
| 		let line = null; | ||||
|  | ||||
| 		// Chrome and IE | ||||
| 		if (stack.indexOf('@') !== -1) { | ||||
| 			line = stack.split('@')[1].split('\n')[0]; | ||||
| 		// Firefox | ||||
| 		} else { | ||||
| 			line = stack.split('(')[1].split(')')[0]; | ||||
| 		} | ||||
| 		line = line.substring(0, line.lastIndexOf('/')) + '/'; | ||||
| 		return line; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // This is used by libunrar.js to load libunrar.js.mem | ||||
| let unrarMemoryFileLocation = null; | ||||
| let g_on_loaded_cb = null; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
| let _loaded_archive_formats = []; | ||||
|  | ||||
| // Polyfill for missing array slice method (IE 11) | ||||
| if (typeof Uint8Array !== 'undefined') { | ||||
| if (! Uint8Array.prototype.slice) { | ||||
| 	Uint8Array.prototype.slice = function(start, end) { | ||||
| 		let retval = new Uint8Array(end - start); | ||||
| 		let j = 0; | ||||
| 		for (let i=start; i<end; ++i) { | ||||
| 			retval[j] = this[i]; | ||||
| 			j++; | ||||
| 		} | ||||
| 		return retval; | ||||
| 	}; | ||||
| } | ||||
| } | ||||
|  | ||||
| // FIXME: This function is super inefficient | ||||
| function saneJoin(array, separator) { | ||||
| 	let retval = ''; | ||||
| 	for (let i=0; i<array.length; ++i) { | ||||
| 		if (i === 0) { | ||||
| 			retval += array[i]; | ||||
| 		} else { | ||||
| 			retval += separator + array[i]; | ||||
| 		} | ||||
| 	} | ||||
| 	return retval; | ||||
| } | ||||
|  | ||||
| function saneMap(array, cb) { | ||||
| 	let retval = new Array(array.length); | ||||
| 	for (let i=0; i<retval.length; ++i) { | ||||
| 		retval[i] = cb(array[i]); | ||||
| 	} | ||||
| 	return retval; | ||||
| } | ||||
|  | ||||
| function loadArchiveFormats(formats, cb) { | ||||
| 	// Get the path of the current script | ||||
| 	let path = currentScriptPath(); | ||||
| 	let load_counter = 0; | ||||
|  | ||||
| 	let checkForLoadDone = function() { | ||||
| 		load_counter++; | ||||
|  | ||||
| 		// Get the total number of loads before we are done loading | ||||
| 		// If loading RAR in a Window, have 1 extra load. | ||||
| 		let load_total = formats.length; | ||||
| 		if (formats.indexOf('rar') !== -1 && typeof window === 'object') { | ||||
| 			load_total++; | ||||
| 		} | ||||
|  | ||||
| 		// run the callback if the last script has loaded | ||||
| 		if (load_counter === load_total) { | ||||
| 			cb(); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	g_on_loaded_cb = checkForLoadDone; | ||||
|  | ||||
| 	// Load the formats | ||||
| 	formats.forEach(function(archive_format) { | ||||
| 		// Skip this format if it is already loaded | ||||
| 		if (_loaded_archive_formats.indexOf(archive_format) !== -1) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		// Load the archive format | ||||
| 		switch (archive_format) { | ||||
| 			case 'rar': | ||||
| 				unrarMemoryFileLocation = path + 'libunrar.js.mem'; | ||||
| 				loadScript(path + 'libunrar.js', checkForLoadDone); | ||||
| 				_loaded_archive_formats.push(archive_format); | ||||
| 				break; | ||||
| 			case 'zip': | ||||
| 				loadScript(path + 'jszip.js', checkForLoadDone); | ||||
| 				_loaded_archive_formats.push(archive_format); | ||||
| 				break; | ||||
| 			case 'tar': | ||||
| 				loadScript(path + 'libuntar.js', checkForLoadDone); | ||||
| 				_loaded_archive_formats.push(archive_format); | ||||
| 				break; | ||||
| 			default: | ||||
| 				throw new Error("Unknown archive format '" + archive_format + "'."); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function archiveOpenFile(array_buffer, cb) { | ||||
| 	let file_name = "Hugo"; //file.name; | ||||
| 	let password = null; | ||||
|  | ||||
|     try { | ||||
|         let archive = archiveOpenArrayBuffer(file_name, password, array_buffer); | ||||
|         cb(archive, null); | ||||
|     } catch(e) { | ||||
|         cb(null, e); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function archiveOpenArrayBuffer(file_name, password, array_buffer) { | ||||
| 	// Get the archive type | ||||
| 	let archive_type = null; | ||||
| 	if (isRarFile(array_buffer)) { | ||||
| 		archive_type = 'rar'; | ||||
| 	} else if(isZipFile(array_buffer)) { | ||||
| 		archive_type = 'zip'; | ||||
| 	} else if(isTarFile(array_buffer)) { | ||||
| 		archive_type = 'tar'; | ||||
| 	} else { | ||||
| 		throw new Error("The archive type is unknown"); | ||||
| 	} | ||||
|  | ||||
| 	// Make sure the archive format is loaded | ||||
| 	if (_loaded_archive_formats.indexOf(archive_type) === -1) { | ||||
| 		throw new Error("The archive format '" + archive_type + "' is not loaded."); | ||||
| 	} | ||||
|  | ||||
| 	// Get the entries | ||||
| 	let handle = null; | ||||
| 	let entries = []; | ||||
| 	try { | ||||
| 		switch (archive_type) { | ||||
| 			case 'rar': | ||||
| 				handle = _rarOpen(file_name, password, array_buffer); | ||||
| 				entries = _rarGetEntries(handle); | ||||
| 				break; | ||||
| 			case 'zip': | ||||
| 				handle = _zipOpen(file_name, password, array_buffer); | ||||
| 				entries = _zipGetEntries(handle); | ||||
| 				break; | ||||
| 			case 'tar': | ||||
| 				handle = _tarOpen(file_name, password, array_buffer); | ||||
| 				entries = _tarGetEntries(handle); | ||||
| 				break; | ||||
| 		} | ||||
| 	} catch(e) { | ||||
| 		throw new Error("Failed to open '" + archive_type + "' archive."); | ||||
| 	} | ||||
|  | ||||
| 	// Sort the entries by name | ||||
| 	entries.sort(function(a, b) { | ||||
| 		if(a.name < b.name) return -1; | ||||
| 		if(a.name > b.name) return 1; | ||||
| 		return 0; | ||||
| 	}); | ||||
|  | ||||
| 	// Return the archive object | ||||
| 	return { | ||||
| 		file_name: file_name, | ||||
| 		archive_type: archive_type, | ||||
| 		array_buffer: array_buffer, | ||||
| 		entries: entries, | ||||
| 		handle: handle | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| function archiveClose(archive) { | ||||
| 	archive.file_name = null; | ||||
| 	archive.archive_type = null; | ||||
| 	archive.array_buffer = null; | ||||
| 	archive.entries = null; | ||||
| 	archive.handle = null; | ||||
| } | ||||
|  | ||||
| function _rarOpen(file_name, password, array_buffer) { | ||||
| 	// Create an array of rar files | ||||
| 	let rar_files = [{ | ||||
| 		name: file_name, | ||||
| 		size: array_buffer.byteLength, | ||||
| 		type: '', | ||||
| 		content: new Uint8Array(array_buffer) | ||||
| 	}]; | ||||
|  | ||||
| 	// Return rar handle | ||||
| 	return { | ||||
| 		file_name: file_name, | ||||
| 		array_buffer: array_buffer, | ||||
| 		password: password, | ||||
| 		rar_files: rar_files | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| function _zipOpen(file_name, password, array_buffer) { | ||||
| 	let zip = new JSZip(array_buffer); | ||||
|  | ||||
| 	// Return zip handle | ||||
| 	return { | ||||
| 		file_name: file_name, | ||||
| 		array_buffer: array_buffer, | ||||
| 		password: password, | ||||
| 		zip: zip | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| function _tarOpen(file_name, password, array_buffer) { | ||||
| 	// Return tar handle | ||||
| 	return { | ||||
| 		file_name: file_name, | ||||
| 		array_buffer: array_buffer, | ||||
| 		password: password | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| function _rarGetEntries(rar_handle) { | ||||
| 	// Get the entries | ||||
| 	let info = readRARFileNames(rar_handle.rar_files, rar_handle.password); | ||||
| 	let entries = []; | ||||
| 	Object.keys(info).forEach(function(i) { | ||||
| 		let name = info[i].name; | ||||
| 		let is_file = info[i].is_file; | ||||
|         if (is_file) { | ||||
|             entries.push({ | ||||
|                 name: name, | ||||
|                 is_file: is_file, // info[i].is_file, | ||||
|                 size_compressed: info[i].size_compressed, | ||||
|                 size_uncompressed: info[i].size_uncompressed, | ||||
|                 readData: function (cb) { | ||||
|                     setTimeout(function () { | ||||
|                         if (is_file) { | ||||
|                             try { | ||||
|                                 readRARContent(rar_handle.rar_files, rar_handle.password, name, cb); | ||||
|                             } catch (e) { | ||||
|                                 cb(null, e); | ||||
|                             } | ||||
|                         } else { | ||||
|                             cb(null, null); | ||||
|                         } | ||||
|                     }, 0); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 	}); | ||||
|  | ||||
| 	return entries; | ||||
| } | ||||
|  | ||||
| function _zipGetEntries(zip_handle) { | ||||
| 	let zip = zip_handle.zip; | ||||
|  | ||||
| 	// Get all the entries | ||||
| 	let entries = []; | ||||
| 	Object.keys(zip.files).forEach(function(i) { | ||||
| 		let zip_entry = zip.files[i]; | ||||
| 		let name = zip_entry.name; | ||||
| 		let is_file = ! zip_entry.dir; | ||||
| 		let size_compressed = zip_entry._data ? zip_entry._data.compressedSize : 0; | ||||
| 		let size_uncompressed = zip_entry._data ? zip_entry._data.uncompressedSize : 0; | ||||
|         if (is_file) { | ||||
|             entries.push({ | ||||
|                 name: name, | ||||
|                 is_file: is_file, | ||||
|                 size_compressed: size_compressed, | ||||
|                 size_uncompressed: size_uncompressed, | ||||
|                 readData: function (cb) { | ||||
|                     setTimeout(function () { | ||||
|                         if (is_file) { | ||||
|                             let data = zip_entry.asArrayBuffer(); | ||||
|                             cb(data, null); | ||||
|                         } else { | ||||
|                             cb(null, null); | ||||
|                         } | ||||
|                     }, 0); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 	}); | ||||
|  | ||||
| 	return entries; | ||||
| } | ||||
|  | ||||
| function _tarGetEntries(tar_handle) { | ||||
| 	let tar_entries = tarGetEntries(tar_handle.file_name, tar_handle.array_buffer); | ||||
|  | ||||
| 	// Get all the entries | ||||
| 	let entries = []; | ||||
| 	tar_entries.forEach(function(entry) { | ||||
| 		let name = entry.name; | ||||
| 		let is_file = entry.is_file; | ||||
| 		let size = entry.size; | ||||
|         if (is_file) { | ||||
|             entries.push({ | ||||
|                 name: name, | ||||
|                 is_file: is_file, | ||||
|                 size_compressed: size, | ||||
|                 size_uncompressed: size, | ||||
|                 readData: function (cb) { | ||||
|                     setTimeout(function () { | ||||
|                         if (is_file) { | ||||
|                             let data = tarGetEntryData(entry, tar_handle.array_buffer); | ||||
|                             cb(data.buffer, null); | ||||
|                         } else { | ||||
|                             cb(null, null); | ||||
|                         } | ||||
|                     }, 0); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 	}); | ||||
|  | ||||
| 	return entries; | ||||
| } | ||||
|  | ||||
| function isRarFile(array_buffer) { | ||||
| 	// The three styles of RAR headers | ||||
| 	let rar_header1 = saneJoin([0x52, 0x45, 0x7E, 0x5E], ', '); // old | ||||
| 	let rar_header2 = saneJoin([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00], ', '); // 1.5 to 4.0 | ||||
| 	let rar_header3 = saneJoin([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00], ', '); // 5.0 | ||||
|  | ||||
| 	// Just return false if the file is smaller than the header | ||||
| 	if (array_buffer.byteLength < 8) { | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// Return true if the header matches one of the RAR headers | ||||
| 	let header1 = saneJoin(new Uint8Array(array_buffer).slice(0, 4), ', '); | ||||
| 	let header2 = saneJoin(new Uint8Array(array_buffer).slice(0, 7), ', '); | ||||
| 	let header3 = saneJoin(new Uint8Array(array_buffer).slice(0, 8), ', '); | ||||
| 	return (header1 === rar_header1 || header2 === rar_header2 || header3 === rar_header3); | ||||
| } | ||||
|  | ||||
| function isZipFile(array_buffer) { | ||||
| 	// The ZIP header | ||||
| 	let zip_header = saneJoin([0x50, 0x4b, 0x03, 0x04], ', '); | ||||
|  | ||||
| 	// Just return false if the file is smaller than the header | ||||
| 	if (array_buffer.byteLength < 4) { | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// Return true if the header matches the ZIP header | ||||
| 	let header = saneJoin(new Uint8Array(array_buffer).slice(0, 4), ', '); | ||||
| 	return (header === zip_header); | ||||
| } | ||||
|  | ||||
| function isTarFile(array_buffer) { | ||||
| 	// The TAR header | ||||
| 	let tar_header = saneJoin(['u', 's', 't', 'a', 'r'], ', '); | ||||
|  | ||||
| 	// Just return false if the file is smaller than the header size | ||||
| 	if (array_buffer.byteLength < 512) { | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// Return true if the header matches the TAR header | ||||
| 	let header = saneJoin(saneMap(new Uint8Array(array_buffer).slice(257, 257 + 5), String.fromCharCode), ', '); | ||||
| 	return (header === tar_header); | ||||
| } | ||||
|  | ||||
| // Figure out if we are running in a Window or Web Worker | ||||
| let scope = null; | ||||
| if (typeof window === 'object') { | ||||
| 	scope = window; | ||||
| } else if (typeof importScripts === 'function') { | ||||
| 	scope = self; | ||||
| } | ||||
|  | ||||
| // Set exports | ||||
| scope.loadArchiveFormats = loadArchiveFormats; | ||||
| scope.archiveOpenFile = archiveOpenFile; | ||||
| scope.archiveOpenArrayBuffer = archiveOpenArrayBuffer; | ||||
| scope.archiveClose = archiveClose; | ||||
| scope.isRarFile = isRarFile; | ||||
| scope.isZipFile = isZipFile; | ||||
| scope.isTarFile = isTarFile; | ||||
| scope.saneJoin = saneJoin; | ||||
| scope.saneMap = saneMap; | ||||
| })(); | ||||
| @@ -1,237 +0,0 @@ | ||||
| /* | ||||
|  * bitstream.js | ||||
|  * | ||||
|  * Provides readers for bitstreams. | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2011 Google Inc. | ||||
|  * Copyright(c) 2011 antimatter15 | ||||
|  */ | ||||
|  | ||||
| /* global bitjs, Uint8Array */ | ||||
|  | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.io = bitjs.io || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|     // mask for getting the Nth bit (zero-based) | ||||
|     bitjs.BIT = [0x01, 0x02, 0x04, 0x08, | ||||
|         0x10, 0x20, 0x40, 0x80, | ||||
|         0x100, 0x200, 0x400, 0x800, | ||||
|         0x1000, 0x2000, 0x4000, 0x8000 | ||||
|     ]; | ||||
|  | ||||
|     // mask for getting N number of bits (0-8) | ||||
|     var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This bit stream peeks and consumes bits out of a binary stream. | ||||
|      * | ||||
|      * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. | ||||
|      * @param {boolean} rtl Whether the stream reads bits from the byte starting | ||||
|      *     from bit 7 to 0 (true) or bit 0 to 7 (false). | ||||
|      * @param {Number} optOffset The offset into the ArrayBuffer | ||||
|      * @param {Number} optLength The length of this BitStream | ||||
|      */ | ||||
|     bitjs.io.BitStream = function(ab, rtl, optOffset, optLength) { | ||||
|         if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { | ||||
|             throw "Error! BitArray constructed with an invalid ArrayBuffer object"; | ||||
|         } | ||||
|  | ||||
|         var offset = optOffset || 0; | ||||
|         var length = optLength || ab.byteLength; | ||||
|         this.bytes = new Uint8Array(ab, offset, length); | ||||
|         this.bytePtr = 0; // tracks which byte we are on | ||||
|         this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) | ||||
|         this.peekBits = rtl ? this.peekBitsRtl : this.peekBitsLtr; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      *   byte0      byte1      byte2      byte3 | ||||
|      * 7......0 | 7......0 | 7......0 | 7......0 | ||||
|      * | ||||
|      * The bit pointer starts at bit0 of byte0 and moves left until it reaches | ||||
|      * bit7 of byte0, then jumps to bit0 of byte1, etc. | ||||
|      * @param {number} n The number of bits to peek. | ||||
|      * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||
|      * @return {number} The peeked bits, as an unsigned number. | ||||
|      */ | ||||
|     bitjs.io.BitStream.prototype.peekBitsLtr = function(n, movePointers) { | ||||
|         if (n <= 0 || typeof n !== typeof 1) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         var movePointers = movePointers || false, | ||||
|             bytePtr = this.bytePtr, | ||||
|             bitPtr = this.bitPtr, | ||||
|             result = 0, | ||||
|             bitsIn = 0, | ||||
|             bytes = this.bytes; | ||||
|  | ||||
|         // keep going until we have no more bits left to peek at | ||||
|         // TODO: Consider putting all bits from bytes we will need into a variable and then | ||||
|         //       shifting/masking it to just extract the bits we want. | ||||
|         //       This could be considerably faster when reading more than 3 or 4 bits at a time. | ||||
|         while (n > 0) { | ||||
|             if (bytePtr >= bytes.length) { | ||||
|                 throw "Error!  Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + | ||||
|                     bytes.length + ", bitPtr=" + bitPtr; | ||||
|                 // return -1; | ||||
|             } | ||||
|  | ||||
|             var numBitsLeftInThisByte = (8 - bitPtr); | ||||
|             var mask; | ||||
|             if (n >= numBitsLeftInThisByte) { | ||||
|                 mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); | ||||
|                 result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); | ||||
|  | ||||
|                 bytePtr++; | ||||
|                 bitPtr = 0; | ||||
|                 bitsIn += numBitsLeftInThisByte; | ||||
|                 n -= numBitsLeftInThisByte; | ||||
|             } else { | ||||
|                 mask = (BITMASK[n] << bitPtr); | ||||
|                 result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); | ||||
|  | ||||
|                 bitPtr += n; | ||||
|                 bitsIn += n; | ||||
|                 n = 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (movePointers) { | ||||
|             this.bitPtr = bitPtr; | ||||
|             this.bytePtr = bytePtr; | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      *   byte0      byte1      byte2      byte3 | ||||
|      * 7......0 | 7......0 | 7......0 | 7......0 | ||||
|      * | ||||
|      * The bit pointer starts at bit7 of byte0 and moves right until it reaches | ||||
|      * bit0 of byte0, then goes to bit7 of byte1, etc. | ||||
|      * @param {number} n The number of bits to peek. | ||||
|      * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||
|      * @return {number} The peeked bits, as an unsigned number. | ||||
|      */ | ||||
|     bitjs.io.BitStream.prototype.peekBitsRtl = function(n, movePointers) { | ||||
|         if (n <= 0 || typeof n !== typeof 1) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         var movePointers = movePointers || false, | ||||
|             bytePtr = this.bytePtr, | ||||
|             bitPtr = this.bitPtr, | ||||
|             result = 0, | ||||
|             bytes = this.bytes; | ||||
|  | ||||
|         // keep going until we have no more bits left to peek at | ||||
|         // TODO: Consider putting all bits from bytes we will need into a variable and then | ||||
|         //       shifting/masking it to just extract the bits we want. | ||||
|         //       This could be considerably faster when reading more than 3 or 4 bits at a time. | ||||
|         while (n > 0) { | ||||
|  | ||||
|             if (bytePtr >= bytes.length) { | ||||
|                 throw "Error!  Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + | ||||
|                     bytes.length + ", bitPtr=" + bitPtr; | ||||
|                 // return -1; | ||||
|             } | ||||
|  | ||||
|             var numBitsLeftInThisByte = (8 - bitPtr); | ||||
|             if (n >= numBitsLeftInThisByte) { | ||||
|                 result <<= numBitsLeftInThisByte; | ||||
|                 result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); | ||||
|                 bytePtr++; | ||||
|                 bitPtr = 0; | ||||
|                 n -= numBitsLeftInThisByte; | ||||
|             } else { | ||||
|                 result <<= n; | ||||
|                 result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr)); | ||||
|  | ||||
|                 bitPtr += n; | ||||
|                 n = 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (movePointers) { | ||||
|             this.bitPtr = bitPtr; | ||||
|             this.bytePtr = bytePtr; | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Peek at 16 bits from current position in the buffer. | ||||
|      * Bit at (bytePtr,bitPtr) has the highest position in returning data. | ||||
|      * Taken from getbits.hpp in unrar. | ||||
|      * TODO: Move this out of BitStream and into unrar. | ||||
|      */ | ||||
|     bitjs.io.BitStream.prototype.getBits = function() { | ||||
|         return (((((this.bytes[this.bytePtr] & 0xff) << 16) + | ||||
|             ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + | ||||
|             ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Reads n bits out of the stream, consuming them (moving the bit pointer). | ||||
|      * @param {number} n The number of bits to read. | ||||
|      * @return {number} The read bits, as an unsigned number. | ||||
|      */ | ||||
|     bitjs.io.BitStream.prototype.readBits = function(n) { | ||||
|         return this.peekBits(n, true); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This returns n bytes as a sub-array, advancing the pointer if movePointers | ||||
|      * is true.  Only use this for uncompressed blocks as this throws away remaining | ||||
|      * bits in the current byte. | ||||
|      * @param {number} n The number of bytes to peek. | ||||
|      * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||
|      * @return {Uint8Array} The subarray. | ||||
|      */ | ||||
|     bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { | ||||
|         if (n <= 0 || typeof n !== typeof 1) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         // from http://tools.ietf.org/html/rfc1951#page-11 | ||||
|         // "Any bits of input up to the next byte boundary are ignored." | ||||
|         while (this.bitPtr !== 0) { | ||||
|             this.readBits(1); | ||||
|         } | ||||
|  | ||||
|         var movePointers = movePointers || false; | ||||
|         var bytePtr = this.bytePtr; | ||||
|         // bitPtr = this.bitPtr; | ||||
|  | ||||
|         var result = this.bytes.subarray(bytePtr, bytePtr + n); | ||||
|  | ||||
|         if (movePointers) { | ||||
|             this.bytePtr += n; | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {Uint8Array} The subarray. | ||||
|      */ | ||||
|     bitjs.io.BitStream.prototype.readBytes = function(n) { | ||||
|         return this.peekBytes(n, true); | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
| @@ -1,124 +0,0 @@ | ||||
| /* | ||||
|  * bytestream.js | ||||
|  * | ||||
|  * Provides a writer for bytes. | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2011 Google Inc. | ||||
|  * Copyright(c) 2011 antimatter15 | ||||
|  */ | ||||
|  | ||||
| /* global bitjs, Uint8Array */ | ||||
|  | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.io = bitjs.io || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. | ||||
|      * @param {number} numBytes The number of bytes to allocate. | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.io.ByteBuffer = function(numBytes) { | ||||
|         if (typeof numBytes !== typeof 1 || numBytes <= 0) { | ||||
|             throw "Error! ByteBuffer initialized with '" + numBytes + "'"; | ||||
|         } | ||||
|         this.data = new Uint8Array(numBytes); | ||||
|         this.ptr = 0; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param {number} b The byte to insert. | ||||
|      */ | ||||
|     bitjs.io.ByteBuffer.prototype.insertByte = function(b) { | ||||
|         // TODO: throw if byte is invalid? | ||||
|         this.data[this.ptr++] = b; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert. | ||||
|      */ | ||||
|     bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) { | ||||
|         // TODO: throw if bytes is invalid? | ||||
|         this.data.set(bytes, this.ptr); | ||||
|         this.ptr += bytes.length; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Writes an unsigned number into the next n bytes.  If the number is too large | ||||
|      * to fit into n bytes or is negative, an error is thrown. | ||||
|      * @param {number} num The unsigned number to write. | ||||
|      * @param {number} numBytes The number of bytes to write the number into. | ||||
|      */ | ||||
|     bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { | ||||
|         if (numBytes < 1) { | ||||
|             throw "Trying to write into too few bytes: " + numBytes; | ||||
|         } | ||||
|         if (num < 0) { | ||||
|             throw "Trying to write a negative number (" + num + | ||||
|                 ") as an unsigned number to an ArrayBuffer"; | ||||
|         } | ||||
|         if (num > (Math.pow(2, numBytes * 8) - 1)) { | ||||
|             throw "Trying to write " + num + " into only " + numBytes + " bytes"; | ||||
|         } | ||||
|  | ||||
|         // Roll 8-bits at a time into an array of bytes. | ||||
|         var bytes = []; | ||||
|         while (numBytes-- > 0) { | ||||
|             var eightBits = num & 255; | ||||
|             bytes.push(eightBits); | ||||
|             num >>= 8; | ||||
|         } | ||||
|  | ||||
|         this.insertBytes(bytes); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Writes a signed number into the next n bytes.  If the number is too large | ||||
|      * to fit into n bytes, an error is thrown. | ||||
|      * @param {number} num The signed number to write. | ||||
|      * @param {number} numBytes The number of bytes to write the number into. | ||||
|      */ | ||||
|     bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { | ||||
|         if (numBytes < 1) { | ||||
|             throw "Trying to write into too few bytes: " + numBytes; | ||||
|         } | ||||
|  | ||||
|         var HALF = Math.pow(2, (numBytes * 8) - 1); | ||||
|         if (num >= HALF || num < -HALF) { | ||||
|             throw "Trying to write " + num + " into only " + numBytes + " bytes"; | ||||
|         } | ||||
|  | ||||
|         // Roll 8-bits at a time into an array of bytes. | ||||
|         var bytes = []; | ||||
|         while (numBytes-- > 0) { | ||||
|             var eightBits = num & 255; | ||||
|             bytes.push(eightBits); | ||||
|             num >>= 8; | ||||
|         } | ||||
|  | ||||
|         this.insertBytes(bytes); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param {string} str The ASCII string to write. | ||||
|      */ | ||||
|     bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) { | ||||
|         for (var i = 0; i < str.length; ++i) { | ||||
|             var curByte = str.charCodeAt(i); | ||||
|             if (curByte < 0 || curByte > 255) { | ||||
|                 throw "Trying to write a non-ASCII string!"; | ||||
|             } | ||||
|             this.insertByte(curByte); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
| @@ -1,195 +0,0 @@ | ||||
| /* | ||||
|  * bytestream.js | ||||
|  * | ||||
|  * Provides readers for byte streams. | ||||
|  * | ||||
|  * Licensed under the MIT License | ||||
|  * | ||||
|  * Copyright(c) 2011 Google Inc. | ||||
|  * Copyright(c) 2011 antimatter15 | ||||
|  */ | ||||
|  | ||||
| /* global bitjs, Uint8Array */ | ||||
|  | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.io = bitjs.io || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * This object allows you to peek and consume bytes as numbers and strings | ||||
|      * out of an ArrayBuffer.  In this buffer, everything must be byte-aligned. | ||||
|      * | ||||
|      * @param {ArrayBuffer} ab The ArrayBuffer object. | ||||
|      * @param {number=} optOffset The offset into the ArrayBuffer | ||||
|      * @param {number=} optLength The length of this BitStream | ||||
|      * @constructor | ||||
|      */ | ||||
|     bitjs.io.ByteStream = function(ab, optOffset, optLength) { | ||||
|         var offset = optOffset || 0; | ||||
|         var length = optLength || ab.byteLength; | ||||
|         this.bytes = new Uint8Array(ab, offset, length); | ||||
|         this.ptr = 0; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Peeks at the next n bytes as an unsigned number but does not advance the | ||||
|      * pointer | ||||
|      * TODO: This apparently cannot read more than 4 bytes as a number? | ||||
|      * @param {number} n The number of bytes to peek at. | ||||
|      * @return {number} The n bytes interpreted as an unsigned number. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.peekNumber = function(n) { | ||||
|         // TODO: return error if n would go past the end of the stream? | ||||
|         if (n <= 0 || typeof n !== typeof 1) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         var result = 0; | ||||
|         // read from last byte to first byte and roll them in | ||||
|         var curByte = this.ptr + n - 1; | ||||
|         while (curByte >= this.ptr) { | ||||
|             result <<= 8; | ||||
|             result |= this.bytes[curByte]; | ||||
|             --curByte; | ||||
|         } | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Returns the next n bytes as an unsigned number (or -1 on error) | ||||
|      * and advances the stream pointer n bytes. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {number} The n bytes interpreted as an unsigned number. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.readNumber = function(n) { | ||||
|         var num = this.peekNumber(n); | ||||
|         this.ptr += n; | ||||
|         return num; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Returns the next n bytes as a signed number but does not advance the | ||||
|      * pointer. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {number} The bytes interpreted as a signed number. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) { | ||||
|         var num = this.peekNumber(n); | ||||
|         var HALF = Math.pow(2, (n * 8) - 1); | ||||
|         var FULL = HALF * 2; | ||||
|  | ||||
|         if (num >= HALF) num -= FULL; | ||||
|  | ||||
|         return num; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Returns the next n bytes as a signed number and advances the stream pointer. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {number} The bytes interpreted as a signed number. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.readSignedNumber = function(n) { | ||||
|         var num = this.peekSignedNumber(n); | ||||
|         this.ptr += n; | ||||
|         return num; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * ToDo: Returns the next n bytes as a signed number and advances the stream pointer. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {number} The bytes interpreted as a signed number. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.movePointer = function(n) { | ||||
|         this.ptr += n; | ||||
|         // end of buffer reached | ||||
|         if ((this.bytes.byteLength - this.ptr) < 0 ) { | ||||
|             this.ptr =  this.bytes.byteLength; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * ToDo: Returns the next n bytes as a signed number and advances the stream pointer. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {number} The bytes interpreted as a signed number. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.moveTo = function(n) { | ||||
|         if ( n < 0 ) { | ||||
|             n = 0; | ||||
|         } | ||||
|         this.ptr = n; | ||||
|         // end of buffer reached | ||||
|         if ((this.bytes.byteLength - this.ptr) < 0 ) { | ||||
|             this.ptr =  this.bytes.byteLength; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This returns n bytes as a sub-array, advancing the pointer if movePointers | ||||
|      * is true. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @param {boolean} movePointers Whether to move the pointers. | ||||
|      * @return {Uint8Array} The subarray. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { | ||||
|         if (n <= 0 || typeof n !== typeof 1) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         var result = this.bytes.subarray(this.ptr, this.ptr + n); | ||||
|  | ||||
|         if (movePointers) { | ||||
|             this.ptr += n; | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Reads the next n bytes as a sub-array. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {Uint8Array} The subarray. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.readBytes = function(n) { | ||||
|         return this.peekBytes(n, true); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Peeks at the next n bytes as a string but does not advance the pointer. | ||||
|      * @param {number} n The number of bytes to peek at. | ||||
|      * @return {string} The next n bytes as a string. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.peekString = function(n) { | ||||
|         if (n <= 0 || typeof n !== typeof 1) { | ||||
|             return ""; | ||||
|         } | ||||
|  | ||||
|         var result = ""; | ||||
|         for (var p = this.ptr, end = this.ptr + n; p < end; ++p) { | ||||
|             result += String.fromCharCode(this.bytes[p]); | ||||
|         } | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Returns the next n bytes as an ASCII string and advances the stream pointer | ||||
|      * n bytes. | ||||
|      * @param {number} n The number of bytes to read. | ||||
|      * @return {string} The next n bytes as a string. | ||||
|      */ | ||||
|     bitjs.io.ByteStream.prototype.readString = function(n) { | ||||
|         var strToReturn = this.peekString(n); | ||||
|         this.ptr += n; | ||||
|         return strToReturn; | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
| @@ -102,9 +102,8 @@ kthoom.setSettings = function() { | ||||
| }; | ||||
|  | ||||
| var createURLFromArray = function(array, mimeType) { | ||||
|     var offset = array.byteOffset; | ||||
|     var offset = 0; // array.byteOffset; | ||||
|     var len = array.byteLength; | ||||
|     // var url; | ||||
|     var blob; | ||||
|  | ||||
|     if (mimeType === "image/xml+svg") { | ||||
| @@ -164,11 +163,9 @@ kthoom.ImageFile = function(file) { | ||||
|     } | ||||
|     if ( this.mimeType !== undefined) { | ||||
|         this.dataURI = createURLFromArray(file.fileData, this.mimeType); | ||||
|         this.data = file; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| function initProgressClick() { | ||||
|     $("#progress").click(function(e) { | ||||
|         var offset = $(this).offset(); | ||||
| @@ -185,72 +182,46 @@ function loadFromArrayBuffer(ab) { | ||||
|     var h = new Uint8Array(ab, 0, 10); | ||||
|     var pathToBitJS = "../../static/js/archive/"; | ||||
|     var lastCompletion = 0; | ||||
|     if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar! | ||||
|         unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); | ||||
|     } else if (h[0] === 80 && h[1] === 75) { //PK (Zip) | ||||
|         unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS); | ||||
|     } else if (h[0] === 255 && h[1] === 216) { // JPEG | ||||
|         // ToDo: check | ||||
|         updateProgress(100); | ||||
|         lastCompletion = 100; | ||||
|         return; | ||||
|     } else { // Try with tar | ||||
|         unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); | ||||
|     } | ||||
|     // Listen for UnarchiveEvents. | ||||
|     if (unarchiver) { | ||||
|         unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.PROGRESS, | ||||
|             function(e) { | ||||
|                 var percentage = e.currentBytesUnarchived / e.totalUncompressedBytesInArchive; | ||||
|                 if (totalImages === 0) { | ||||
|                     totalImages = e.totalFilesInArchive; | ||||
|                 } | ||||
|                 updateProgress(percentage * 100); | ||||
|                 lastCompletion = percentage * 100; | ||||
|             }); | ||||
|         unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.INFO, | ||||
|             function(e) { | ||||
|                 // console.log(e.msg);  // Enable debug output here | ||||
|             }); | ||||
|         unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.EXTRACT, | ||||
|             function(e) { | ||||
|                 // convert DecompressedFile into a bunch of ImageFiles | ||||
|                 if (e.unarchivedFile) { | ||||
|                     var f = e.unarchivedFile; | ||||
|                     // add any new pages based on the filename | ||||
|                     if (imageFilenames.indexOf(f.filename) === -1) { | ||||
|                         var test = new kthoom.ImageFile(f); | ||||
|                         if ( test.mimeType !== undefined) { | ||||
|                             imageFilenames.push(f.filename); | ||||
|                             imageFiles.push(test); | ||||
|                             // add thumbnails to the TOC list | ||||
|                             $("#thumbnails").append( | ||||
|                                 "<li>" + | ||||
|                                     "<a data-page='" + imageFiles.length + "'>" + | ||||
|     loadArchiveFormats(['rar', 'zip', 'tar'], function() { | ||||
|         // Open the file as an archive | ||||
|         archiveOpenFile(ab, function (archive, err) { | ||||
|             if (archive) { | ||||
|                 totalImages = archive.entries.length | ||||
|                 console.info('Uncompressing ' + archive.archive_type + ' ...'); | ||||
|                 archive.entries.forEach(function(e, i) { | ||||
|                     updateProgress( (i + 1)/ totalImages * 100); | ||||
|                     if (e.is_file) { | ||||
|                         var f = e.readData(function(d) { | ||||
|                             // add any new pages based on the filename | ||||
|                             if (imageFilenames.indexOf(e.name) === -1) { | ||||
|                                 let data = {filename: e.name, fileData: d}; | ||||
|                                 var test = new kthoom.ImageFile(data); | ||||
|                                 if (test.mimeType !== undefined) { | ||||
|                                     imageFilenames.push(e.name); | ||||
|                                     imageFiles.push(test); | ||||
|                                     // add thumbnails to the TOC list | ||||
|                                     $("#thumbnails").append( | ||||
|                                         "<li>" + | ||||
|                                         "<a data-page='" + imageFiles.length + "'>" + | ||||
|                                         "<img src='" + imageFiles[imageFiles.length - 1].dataURI + "'/>" + | ||||
|                                         "<span>" + imageFiles.length + "</span>" + | ||||
|                                     "</a>" + | ||||
|                                 "</li>" | ||||
|                             ); | ||||
|                             // display first page if we haven't yet | ||||
|                             if (imageFiles.length === currentImage + 1) { | ||||
|                                 updatePage(lastCompletion); | ||||
|                                         "</a>" + | ||||
|                                         "</li>" | ||||
|                                     ); | ||||
|                                     // display first page if we haven't yet | ||||
|                                     if (imageFiles.length === currentImage + 1) { | ||||
|                                         updatePage(lastCompletion); | ||||
|                                     } | ||||
|                                 } else { | ||||
|                                     totalImages--; | ||||
|                                 } | ||||
|                             } | ||||
|                         } else { | ||||
|                             totalImages--; | ||||
|                         } | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.FINISH, | ||||
|             function() { | ||||
|                 var diff = ((new Date).getTime() - start) / 1000; | ||||
|                 console.log("Unarchiving done in " + diff + "s"); | ||||
|             }); | ||||
|         unarchiver.start(); | ||||
|     } else { | ||||
|         alert("Some error"); | ||||
|     } | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function scrollTocToActive() { | ||||
| @@ -314,7 +285,6 @@ function updateProgress(loadPercentage) { | ||||
|                 .find(".load").text(""); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Set page progress bar | ||||
|     $("#progress .bar-read").css({ width: totalImages === 0 ? 0 : Math.round((currentImage + 1) / totalImages * 100) + "%"}); | ||||
| } | ||||
| @@ -564,26 +534,6 @@ function keyHandler(evt) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /*function ImageLoadCallback() { | ||||
|     var jso = this.response; | ||||
|     // Unable to decompress file, or no response from server | ||||
|     if (jso === null) { | ||||
|         setImage("error"); | ||||
|     } else { | ||||
|         // IE 11 sometimes sees the response as a string | ||||
|         if (typeof jso !== "object") { | ||||
|             jso = JSON.parse(jso); | ||||
|         } | ||||
|  | ||||
|         if (jso.page !== jso.last) { | ||||
|             this.open("GET", this.fileid + "/" + (jso.page + 1)); | ||||
|             this.addEventListener("load", ImageLoadCallback); | ||||
|             this.send(); | ||||
|         } | ||||
|  | ||||
|         loadFromArrayBuffer(jso); | ||||
|     } | ||||
| }*/ | ||||
| function init(filename) { | ||||
|     var request = new XMLHttpRequest(); | ||||
|     request.open("GET", filename); | ||||
|   | ||||
| @@ -15,8 +15,8 @@ | ||||
|   <script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script> | ||||
|   <script src="{{ url_for('static', filename='js/libs/plugins.js') }}"></script> | ||||
|   <script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script> | ||||
|   <script src="{{ url_for('static', filename='js/compress/uncompress.js') }}"></script> | ||||
|   <script src="{{ url_for('static', filename='js/kthoom.js') }}"></script> | ||||
|   <script src="{{ url_for('static', filename='js/archive/archive.js') }}"></script> | ||||
|   <script> | ||||
|     var updateArrows = function() { | ||||
|       if ($('input[name="direction"]:checked').val() === "0") { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Ozzie Isaacs
					Ozzie Isaacs