Commit 3e5c9443 authored by Ozzie Isaacs's avatar Ozzie Isaacs

Changed backend for comic reader, now supports rar5

parent d9583830
/* 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;
};
})();
/**
* 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);
}
};
// ============================================================================================== //
/**
* unrar.js
*
* Licensed under the MIT License
*
* Copyright(c) 2011 Google Inc.
* Copyright(c) 2011 antimatter15
*
* Reference Documentation:
*
* http://kthoom.googlecode.com/hg/docs/unrar.html
*/
/* global bitjs, importScripts, RarVM, Uint8Array, UnpackFilter */
/* global VM_FIXEDGLOBALSIZE, VM_GLOBALMEMSIZE, MAXWINMASK, VM_GLOBALMEMADDR, MAXWINSIZE */
// This file expects to be invoked as a Worker (see onmessage below).
importScripts("../io/bitstream.js");
importScripts("../io/bytebuffer.js");
importScripts("archive.js");
importScripts("rarvm.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));
};
// shows a byte value as its hex representation
var nibble = "0123456789ABCDEF";
var byteValueToHexString = function(num) {
return nibble[num >> 4] + nibble[num & 0xF];
};
var twoByteValueToHexString = function(num) {
return nibble[(num >> 12) & 0xF] + nibble[(num >> 8) & 0xF] + nibble[(num >> 4) & 0xF] + nibble[num & 0xF];
};
// Volume Types
// MARK_HEAD = 0x72;
var MAIN_HEAD = 0x73,
FILE_HEAD = 0x74,
// COMM_HEAD = 0x75,
// AV_HEAD = 0x76,
// SUB_HEAD = 0x77,
// PROTECT_HEAD = 0x78,
// SIGN_HEAD = 0x79,
// NEWSUB_HEAD = 0x7a,
ENDARC_HEAD = 0x7b;
// ============================================================================================== //
/**
* @param {bitjs.io.BitStream} bstream
* @constructor
*/
var RarVolumeHeader = function(bstream) {
var headPos = bstream.bytePtr;
// byte 1,2
info("Rar Volume Header @" + bstream.bytePtr);
this.crc = bstream.readBits(16);
info(" crc=" + this.crc);
// byte 3
this.headType = bstream.readBits(8);
info(" headType=" + this.headType);
// Get flags
// bytes 4,5
this.flags = {};
this.flags.value = bstream.peekBits(16);
info(" flags=" + twoByteValueToHexString(this.flags.value));
switch (this.headType) {
case MAIN_HEAD:
this.flags.MHD_VOLUME = !!bstream.readBits(1);
this.flags.MHD_COMMENT = !!bstream.readBits(1);
this.flags.MHD_LOCK = !!bstream.readBits(1);
this.flags.MHD_SOLID = !!bstream.readBits(1);
this.flags.MHD_PACK_COMMENT = !!bstream.readBits(1);
this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT;
this.flags.MHD_AV = !!bstream.readBits(1);
this.flags.MHD_PROTECT = !!bstream.readBits(1);
this.flags.MHD_PASSWORD = !!bstream.readBits(1);
this.flags.MHD_FIRSTVOLUME = !!bstream.readBits(1);
this.flags.MHD_ENCRYPTVER = !!bstream.readBits(1);
bstream.readBits(6); // unused
break;
case FILE_HEAD:
this.flags.LHD_SPLIT_BEFORE = !!bstream.readBits(1); // 0x0001
this.flags.LHD_SPLIT_AFTER = !!bstream.readBits(1); // 0x0002
this.flags.LHD_PASSWORD = !!bstream.readBits(1); // 0x0004
this.flags.LHD_COMMENT = !!bstream.readBits(1); // 0x0008
this.flags.LHD_SOLID = !!bstream.readBits(1); // 0x0010
bstream.readBits(3); // unused
this.flags.LHD_LARGE = !!bstream.readBits(1); // 0x0100
this.flags.LHD_UNICODE = !!bstream.readBits(1); // 0x0200
this.flags.LHD_SALT = !!bstream.readBits(1); // 0x0400
this.flags.LHD_VERSION = !!bstream.readBits(1); // 0x0800
this.flags.LHD_EXTTIME = !!bstream.readBits(1); // 0x1000
this.flags.LHD_EXTFLAGS = !!bstream.readBits(1); // 0x2000
bstream.readBits(2); // unused
info(" LHD_SPLIT_BEFORE = " + this.flags.LHD_SPLIT_BEFORE);
break;
default:
bstream.readBits(16);
}
// byte 6,7
this.headSize = bstream.readBits(16);
info(" headSize=" + this.headSize);
switch (this.headType) {
case MAIN_HEAD:
this.highPosAv = bstream.readBits(16);
this.posAv = bstream.readBits(32);
if (this.flags.MHD_ENCRYPTVER) {
this.encryptVer = bstream.readBits(8);
}
info("Found MAIN_HEAD with highPosAv=" + this.highPosAv + ", posAv=" + this.posAv);
break;
case FILE_HEAD:
this.packSize = bstream.readBits(32);
this.unpackedSize = bstream.readBits(32);
this.hostOS = bstream.readBits(8);
this.fileCRC = bstream.readBits(32);
this.fileTime = bstream.readBits(32);
this.unpVer = bstream.readBits(8);
this.method = bstream.readBits(8);
this.nameSize = bstream.readBits(16);
this.fileAttr = bstream.readBits(32);
if (this.flags.LHD_LARGE) {
info("Warning: Reading in LHD_LARGE 64-bit size values");
this.HighPackSize = bstream.readBits(32);
this.HighUnpSize = bstream.readBits(32);
} else {
this.HighPackSize = 0;
this.HighUnpSize = 0;
if (this.unpackedSize === 0xffffffff) {
this.HighUnpSize = 0x7fffffff;
this.unpackedSize = 0xffffffff;
}
}
this.fullPackSize = 0;
this.fullUnpackSize = 0;
this.fullPackSize |= this.HighPackSize;
this.fullPackSize <<= 32;
this.fullPackSize |= this.packSize;
// read in filename
this.filename = bstream.readBytes(this.nameSize);
var _s = "";
for (var _i = 0; _i < this.filename.length ; _i++) {
_s += String.fromCharCode(this.filename[_i]);
}
this.filename = _s;
if (this.flags.LHD_SALT) {
info("Warning: Reading in 64-bit salt value");
this.salt = bstream.readBits(64); // 8 bytes
}
if (this.flags.LHD_EXTTIME) {
// 16-bit flags
var extTimeFlags = bstream.readBits(16);
// this is adapted straight out of arcread.cpp, Archive::ReadHeader()
for (var I = 0; I < 4; ++I) {
var rmode = extTimeFlags >> ((3 - I) * 4);
if ((rmode & 8) === 0) {
continue;
}
if (I !== 0) {
bstream.readBits(16);
}
var count = (rmode & 3);
for (var J = 0; J < count; ++J) {
bstream.readBits(8);
}
}
}
if (this.flags.LHD_COMMENT) {
info("Found a LHD_COMMENT");
}
while (headPos + this.headSize > bstream.bytePtr) {
bstream.readBits(1);
}
// If Info line is commented in firefox fails if server on same computer than browser with error "expected expression, got default"
//info("Found FILE_HEAD with packSize=" + this.packSize + ", unpackedSize= " + this.unpackedSize + ", hostOS=" + this.hostOS + ", unpVer=" + this.unpVer + ", method=" + this.method + ", filename=" + this.filename);
break;
default:
info("Found a header of type 0x" + byteValueToHexString(this.headType));
// skip the rest of the header bytes (for now)
bstream.readBytes(this.headSize - 7);
break;
}
};
//var BLOCK_LZ = 0;
var rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224],
rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5],
rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12],
rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192],
rSDBits = [2, 2, 3, 4, 5, 6, 6, 6];
var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072,
4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304,
131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824,
655360, 720896, 786432, 851968, 917504, 983040
];
var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
];
var rLowDistRepCount = 16;
var rNC = 299,
rDC = 60,
rLDC = 17,
rRC = 28,
rBC = 20,
rHuffTableSize = (rNC + rDC + rRC + rLDC);
//var UnpBlockType = BLOCK_LZ;
var UnpOldTable = new Array(rHuffTableSize);
var BD = { //bitdecode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rBC)
};
var LD = { //litdecode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rNC)
};
var DD = { //distdecode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rDC)
};
var LDD = { //low dist decode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rLDC)
};
var RD = { //rep decode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rRC)
};
/**
* @type {Array<bitjs.io.ByteBuffer>}
*/
var rOldBuffers = [];
/**
* The current buffer we are unpacking to.
* @type {bitjs.io.ByteBuffer}
*/
var rBuffer;
/**
* The buffer of the final bytes after filtering (only used in Unpack29).
* @type {bitjs.io.ByteBuffer}
*/
var wBuffer;
var lowDistRepCount = 0;
var prevLowDist = 0;
var rOldDist = [0, 0, 0, 0];
var lastDist;
var lastLength;
/**
* In unpack.cpp, UnpPtr keeps track of what bytes have been unpacked
* into the Window buffer and WrPtr keeps track of what bytes have been
* actually written to disk after the unpacking and optional filtering
* has been done.
*
* In our case, rBuffer is the buffer for the unpacked bytes and wBuffer is
* the final output bytes.
*/
/**
* Read in Huffman tables for RAR
* @param {bitjs.io.BitStream} bstream
*/
function rarReadTables(bstream) {
var BitLength = new Array(rBC);
var Table = new Array(rHuffTableSize);
var i;
// before we start anything we need to get byte-aligned
bstream.readBits((8 - bstream.bitPtr) & 0x7);
if (bstream.readBits(1)) {
info("Error! PPM not implemented yet");
return;
}
if (!bstream.readBits(1)) { //discard old table
for (i = UnpOldTable.length; i--;) {
UnpOldTable[i] = 0;
}
}
// read in bit lengths
for (var I = 0; I < rBC; ++I) {
var Length = bstream.readBits(4);
if (Length === 15) {
var ZeroCount = bstream.readBits(4);
if (ZeroCount === 0) {
BitLength[I] = 15;
} else {
ZeroCount += 2;
while (ZeroCount-- > 0 && I < rBC) {
BitLength[I++] = 0;
}
--I;
}
} else {
BitLength[I] = Length;
}
}
// now all 20 bit lengths are obtained, we construct the Huffman Table:
rarMakeDecodeTables(BitLength, 0, BD, rBC);
var TableSize = rHuffTableSize;
//console.log(DecodeLen, DecodePos, DecodeNum);
for (i = 0; i < TableSize;) {
var N;
var num = rarDecodeNumber(bstream, BD);
if (num < 16) {
Table[i] = (num + UnpOldTable[i]) & 0xf;
i++;
} else if (num < 18) {
N = (num === 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
while (N-- > 0 && i < TableSize) {
Table[i] = Table[i - 1];
i++;
}
} else {
N = (num === 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
while (N-- > 0 && i < TableSize) {
Table[i++] = 0;
}
}
}
rarMakeDecodeTables(Table, 0, LD, rNC);
rarMakeDecodeTables(Table, rNC, DD, rDC);
rarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC);
rarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC);
for (i = UnpOldTable.length; i--;) {
UnpOldTable[i] = Table[i];
}
return true;
}
function rarDecodeNumber(bstream, dec) {
var DecodeLen = dec.DecodeLen,
DecodePos = dec.DecodePos,
DecodeNum = dec.DecodeNum;
var bitField = bstream.getBits() & 0xfffe;
//some sort of rolled out binary search
var bits = ((bitField < DecodeLen[8]) ?
((bitField < DecodeLen[4]) ?
((bitField < DecodeLen[2]) ?
((bitField < DecodeLen[1]) ? 1 : 2) :
((bitField < DecodeLen[3]) ? 3 : 4)) :
(bitField < DecodeLen[6]) ?
((bitField < DecodeLen[5]) ? 5 : 6) :
((bitField < DecodeLen[7]) ? 7 : 8)) :
((bitField < DecodeLen[12]) ?
((bitField < DecodeLen[10]) ?
((bitField < DecodeLen[9]) ? 9 : 10) :
((bitField < DecodeLen[11]) ? 11 : 12)) :
(bitField < DecodeLen[14]) ?
((bitField < DecodeLen[13]) ? 13 : 14) :
15));
bstream.readBits(bits);
var N = DecodePos[bits] + ((bitField - DecodeLen[bits - 1]) >>> (16 - bits));
return DecodeNum[N];
}
function rarMakeDecodeTables(BitLength, offset, dec, size) {
var DecodeLen = dec.DecodeLen;
var DecodePos = dec.DecodePos;
var DecodeNum = dec.DecodeNum;
var LenCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var TmpPos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var N = 0;
var M = 0;
var i;
for (i = DecodeNum.length; i--;) {
DecodeNum[i] = 0;
}
for (i = 0; i < size; i++) {
LenCount[BitLength[i + offset] & 0xF]++;
}
LenCount[0] = 0;
TmpPos[0] = 0;
DecodePos[0] = 0;
DecodeLen[0] = 0;
var I;
for (I = 1; I < 16; ++I) {
N = 2 * (N + LenCount[I]);
M = (N << (15 - I));
if (M > 0xFFFF) {
M = 0xFFFF;
}
DecodeLen[I] = M;
DecodePos[I] = DecodePos[I - 1] + LenCount[I - 1];
TmpPos[I] = DecodePos[I];
}
for (I = 0; I < size; ++I) {
if (BitLength[I + offset] !== 0) {
DecodeNum[TmpPos[BitLength[offset + I] & 0xF]++] = I;
}
}
}
// TODO: implement
/**
* @param {bitjs.io.BitStream} bstream
* @param {boolean} Solid
*/
function unpack15() { //bstream, Solid) {
info("ERROR! RAR 1.5 compression not supported");
}
/**
* Unpacks the bit stream into rBuffer using the Unpack20 algorithm.
* @param {bitjs.io.BitStream} bstream
* @param {boolean} Solid
*/
function unpack20(bstream) { //, Solid) {
var destUnpSize = rBuffer.data.length;
var oldDistPtr = 0;
var Length;
var Distance;
rarReadTables20(bstream);
while (destUnpSize > rBuffer.ptr) {
var num = rarDecodeNumber(bstream, LD);
var Bits;
if (num < 256) {
rBuffer.insertByte(num);
continue;
}
if (num > 269) {
Length = rLDecode[num -= 270] + 3;
if ((Bits = rLBits[num]) > 0) {
Length += bstream.readBits(Bits);
}
var DistNumber = rarDecodeNumber(bstream, DD);
Distance = rDDecode[DistNumber] + 1;
if ((Bits = rDBits[DistNumber]) > 0) {
Distance += bstream.readBits(Bits);
}
if (Distance >= 0x2000) {
Length++;
if (Distance >= 0x40000) {
Length++;
}
}
lastLength = Length;
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
rarCopyString(Length, Distance);
continue;
}
if (num === 269) {
rarReadTables20(bstream);
rarUpdateProgress();
continue;
}
if (num === 256) {
lastDist = rOldDist[oldDistPtr++ & 3] = lastDist;
rarCopyString(lastLength, lastDist);
continue;
}
if (num < 261) {
Distance = rOldDist[(oldDistPtr - (num - 256)) & 3];
var LengthNumber = rarDecodeNumber(bstream, RD);
Length = rLDecode[LengthNumber] + 2;
if ((Bits = rLBits[LengthNumber]) > 0) {
Length += bstream.readBits(Bits);
}
if (Distance >= 0x101) {
Length++;
if (Distance >= 0x2000) {
Length++;
if (Distance >= 0x40000) {
Length++;
}
}
}
lastLength = Length;
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
rarCopyString(Length, Distance);
continue;
}
if (num < 270) {
Distance = rSDDecode[num -= 261] + 1;
if ((Bits = rSDBits[num]) > 0) {
Distance += bstream.readBits(Bits);
}
lastLength = 2;
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
rarCopyString(2, Distance);
continue;
}
}
rarUpdateProgress();
}
function rarUpdateProgress() {
var change = rBuffer.ptr - currentBytesUnarchivedInFile;
currentBytesUnarchivedInFile = rBuffer.ptr;
currentBytesUnarchived += change;
postProgress();
}
var rNC20 = 298,
rDC20 = 48,
rRC20 = 28,
rBC20 = 19,
rMC20 = 257;
var UnpOldTable20 = new Array(rMC20 * 4);
function rarReadTables20(bstream) {
var BitLength = new Array(rBC20);
var Table = new Array(rMC20 * 4);
var TableSize, N, I;
var i;
bstream.readBits(1);
if (!bstream.readBits(1)) {
for (i = UnpOldTable20.length; i--;) {
UnpOldTable20[i] = 0;
}
}
TableSize = rNC20 + rDC20 + rRC20;
for (I = 0; I < rBC20; I++) {
BitLength[I] = bstream.readBits(4);
}
rarMakeDecodeTables(BitLength, 0, BD, rBC20);
I = 0;
while (I < TableSize) {
var num = rarDecodeNumber(bstream, BD);
if (num < 16) {
Table[I] = num + UnpOldTable20[I] & 0xf;
I++;
} else if (num === 16) {
N = bstream.readBits(2) + 3;
while (N-- > 0 && I < TableSize) {
Table[I] = Table[I - 1];
I++;
}
} else {
if (num === 17) {
N = bstream.readBits(3) + 3;
} else {
N = bstream.readBits(7) + 11;
}
while (N-- > 0 && I < TableSize) {
Table[I++] = 0;
}
}
}
rarMakeDecodeTables(Table, 0, LD, rNC20);
rarMakeDecodeTables(Table, rNC20, DD, rDC20);
rarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20);
for (i = UnpOldTable20.length; i--;) {
UnpOldTable20[i] = Table[i];
}
}
// ============================================================================================== //
// Unpack code specific to RarVM
var VM = new RarVM();
/**
* Filters code, one entry per filter.
* @type {Array<UnpackFilter>}
*/
var Filters = [];
/**
* Filters stack, several entrances of same filter are possible.
* @type {Array<UnpackFilter>}
*/
var PrgStack = [];
/**
* Lengths of preceding blocks, one length per filter. Used to reduce
* size required to write block length if lengths are repeating.
* @type {Array<number>}
*/
var OldFilterLengths = [];
var LastFilter = 0;
function initFilters() {
OldFilterLengths = [];
LastFilter = 0;
Filters = [];
PrgStack = [];
}
/**
* @param {number} firstByte The first byte (flags).
* @param {Uint8Array} vmCode An array of bytes.
*/
function rarAddVMCode(firstByte, vmCode) {
VM.init();
var i;
var bstream = new bitjs.io.BitStream(vmCode.buffer, true /* rtl */ );
var filtPos;
if (firstByte & 0x80) {
filtPos = RarVM.readData(bstream);
if (filtPos === 0) {
initFilters();
} else {
filtPos--;
}
} else {
filtPos = LastFilter;
}
if (filtPos > Filters.length || filtPos > OldFilterLengths.length) {
return false;
}
LastFilter = filtPos;
var newFilter = (filtPos === Filters.length);
// new filter for PrgStack
var stackFilter = new UnpackFilter();
var filter = null;
// new filter code, never used before since VM reset
if (newFilter) {
// too many different filters, corrupt archive
if (filtPos > 1024) {
return false;
}
filter = new UnpackFilter();
Filters.push(filter);
stackFilter.ParentFilter = (Filters.length - 1);
OldFilterLengths.push(0); // OldFilterLengths.Add(1)
filter.ExecCount = 0;
} else { // filter was used in the past
filter = Filters[filtPos];
stackFilter.ParentFilter = filtPos;
filter.ExecCount++;
}
var emptyCount = 0;
for (i = 0; i < PrgStack.length; ++i) {
PrgStack[i - emptyCount] = PrgStack[i];
if (PrgStack[i] === null) {
emptyCount++;
}
if (emptyCount > 0) {
PrgStack[i] = null;
}
}
if (emptyCount === 0) {
PrgStack.push(null); //PrgStack.Add(1);
emptyCount = 1;
}
var stackPos = PrgStack.length - emptyCount;
PrgStack[stackPos] = stackFilter;
stackFilter.ExecCount = filter.ExecCount;
var blockStart = RarVM.readData(bstream);
if (firstByte & 0x40) {
blockStart += 258;
}
stackFilter.BlockStart = (blockStart + rBuffer.ptr) & MAXWINMASK;
if (firstByte & 0x20) {
stackFilter.BlockLength = RarVM.readData(bstream);
} else {
stackFilter.BlockLength = filtPos < OldFilterLengths.length ?
OldFilterLengths[filtPos] :
0;
}
stackFilter.NextWindow = (wBuffer.ptr !== rBuffer.ptr) &&
(((wBuffer.ptr - rBuffer.ptr) & MAXWINMASK) <= blockStart);
OldFilterLengths[filtPos] = stackFilter.BlockLength;
for (i = 0; i < 7; ++i) {
stackFilter.Prg.InitR[i] = 0;
}
stackFilter.Prg.InitR[3] = VM_GLOBALMEMADDR;
stackFilter.Prg.InitR[4] = stackFilter.BlockLength;
stackFilter.Prg.InitR[5] = stackFilter.ExecCount;
// set registers to optional parameters if any
if (firstByte & 0x10) {
var initMask = bstream.readBits(7);
for (i = 0; i < 7; ++i) {
if (initMask & (1 << i)) {
stackFilter.Prg.InitR[i] = RarVM.readData(bstream);
}
}
}
if (newFilter) {
var vmCodeSize = RarVM.readData(bstream);
if (vmCodeSize >= 0x10000 || vmCodeSize === 0) {
return false;
}
vmCode = new Uint8Array(vmCodeSize);
for (i = 0; i < vmCodeSize; ++i) {
//if (Inp.Overflow(3))
// return(false);
vmCode[i] = bstream.readBits(8);
}
VM.prepare(vmCode, filter.Prg);
}
stackFilter.Prg.Cmd = filter.Prg.Cmd;
stackFilter.Prg.AltCmd = filter.Prg.Cmd;
var staticDataSize = filter.Prg.StaticData.length;
if (staticDataSize > 0 && staticDataSize < VM_GLOBALMEMSIZE) {
// read statically defined data contained in DB commands
for (i = 0; i < staticDataSize; ++i) {
stackFilter.Prg.StaticData[i] = filter.Prg.StaticData[i];
}
}
if (stackFilter.Prg.GlobalData.length < VM_FIXEDGLOBALSIZE) {
stackFilter.Prg.GlobalData = new Uint8Array(VM_FIXEDGLOBALSIZE);
}
var globalData = stackFilter.Prg.GlobalData;
for (i = 0; i < 7; ++i) {
VM.setLowEndianValue(globalData, stackFilter.Prg.InitR[i], i * 4);
}
VM.setLowEndianValue(globalData, stackFilter.BlockLength, 0x1c);
VM.setLowEndianValue(globalData, 0, 0x20);
VM.setLowEndianValue(globalData, stackFilter.ExecCount, 0x2c);
for (i = 0; i < 16; ++i) {
globalData[0x30 + i] = 0;
}
// put data block passed as parameter if any
if (firstByte & 8) {
//if (Inp.Overflow(3))
// return(false);
var dataSize = RarVM.readData(bstream);
if (dataSize > (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)) {
return (false);
}
var curSize = stackFilter.Prg.GlobalData.length;
if (curSize < dataSize + VM_FIXEDGLOBALSIZE) {
// Resize global data and update the stackFilter and local variable.
var numBytesToAdd = dataSize + VM_FIXEDGLOBALSIZE - curSize;
var newGlobalData = new Uint8Array(globalData.length + numBytesToAdd);
newGlobalData.set(globalData);
stackFilter.Prg.GlobalData = newGlobalData;
globalData = newGlobalData;
}
//byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE];
for (i = 0; i < dataSize; ++i) {
//if (Inp.Overflow(3))
// return(false);
globalData[VM_FIXEDGLOBALSIZE + i] = bstream.readBits(8);
}
}
return true;
}
/**
* @param {!bitjs.io.BitStream} bstream
*/
function rarReadVMCode(bstream) {
var firstByte = bstream.readBits(8);
var length = (firstByte & 7) + 1;
if (length === 7) {
length = bstream.readBits(8) + 7;
} else if (length === 8) {
length = bstream.readBits(16);
}
// Read all bytes of VM code into an array.
var vmCode = new Uint8Array(length);
for (var i = 0; i < length; i++) {
// Do something here with checking readbuf.
vmCode[i] = bstream.readBits(8);
}
return rarAddVMCode(firstByte, vmCode);
}
/**
* Unpacks the bit stream into rBuffer using the Unpack29 algorithm.
* @param {bitjs.io.BitStream} bstream
* @param {boolean} Solid
*/
function unpack29(bstream) {
// lazy initialize rDDecode and rDBits
var DDecode = new Array(rDC);
var DBits = new Array(rDC);
var Distance = 0;
var Length = 0;
var Dist = 0, BitLength = 0, Slot = 0;
var I;
for (I = 0; I < rDBitLengthCounts.length; I++, BitLength++) {
for (var J = 0; J < rDBitLengthCounts[I]; J++, Slot++, Dist += (1 << BitLength)) {
DDecode[Slot] = Dist;
DBits[Slot] = BitLength;
}
}
var Bits;
//tablesRead = false;
rOldDist = [0, 0, 0, 0];
lastDist = 0;
lastLength = 0;
var i;
for (i = UnpOldTable.length; i--;) {
UnpOldTable[i] = 0;
}
// read in Huffman tables
rarReadTables(bstream);
while (true) {
var num = rarDecodeNumber(bstream, LD);
if (num < 256) {
rBuffer.insertByte(num);
continue;
}
if (num >= 271) {
Length = rLDecode[num -= 271] + 3;
if ((Bits = rLBits[num]) > 0) {
Length += bstream.readBits(Bits);
}
var DistNumber = rarDecodeNumber(bstream, DD);
Distance = DDecode[DistNumber] + 1;
if ((Bits = DBits[DistNumber]) > 0) {
if (DistNumber > 9) {
if (Bits > 4) {
Distance += ((bstream.getBits() >>> (20 - Bits)) << 4);
bstream.readBits(Bits - 4);
//todo: check this
}
if (lowDistRepCount > 0) {
lowDistRepCount--;
Distance += prevLowDist;
} else {
var LowDist = rarDecodeNumber(bstream, LDD);
if (LowDist === 16) {
lowDistRepCount = rLowDistRepCount - 1;
Distance += prevLowDist;
} else {
Distance += LowDist;
prevLowDist = LowDist;
}
}
} else {
Distance += bstream.readBits(Bits);
}
}
if (Distance >= 0x2000) {
Length++;
if (Distance >= 0x40000) {
Length++;
}
}
rarInsertOldDist(Distance);
rarInsertLastMatch(Length, Distance);
rarCopyString(Length, Distance);
continue;
}
if (num === 256) {
if (!rarReadEndOfBlock(bstream)) {
break;
}
continue;
}
if (num === 257) {
if (!rarReadVMCode(bstream)) {
break;
}
continue;
}
if (num === 258) {
if (lastLength !== 0) {
rarCopyString(lastLength, lastDist);
}
continue;
}
if (num < 263) {
var DistNum = num - 259;
Distance = rOldDist[DistNum];
for (var I2 = DistNum; I2 > 0; I2--) {
rOldDist[I2] = rOldDist[I2 - 1];
}
rOldDist[0] = Distance;
var LengthNumber = rarDecodeNumber(bstream, RD);
Length = rLDecode[LengthNumber] + 2;
if ((Bits = rLBits[LengthNumber]) > 0) {
Length += bstream.readBits(Bits);
}
rarInsertLastMatch(Length, Distance);
rarCopyString(Length, Distance);
continue;
}
if (num < 272) {
Distance = rSDDecode[num -= 263] + 1;
if ((Bits = rSDBits[num]) > 0) {
Distance += bstream.readBits(Bits);
}
rarInsertOldDist(Distance);
rarInsertLastMatch(2, Distance);
rarCopyString(2, Distance);
continue;
}
} // while (true)
rarUpdateProgress();
rarWriteBuf();
}
/**
* Does stuff to the current byte buffer (rBuffer) based on
* the filters loaded into the RarVM and writes out to wBuffer.
*/
function rarWriteBuf() {
var writeSize = (rBuffer.ptr & MAXWINMASK);
var j;
var flt;
for (var i = 0; i < PrgStack.length; ++i) {
flt = PrgStack[i];
if (flt === null) {
continue;
}
if (flt.NextWindow) {
flt.NextWindow = false;
continue;
}
var blockStart = flt.BlockStart;
var blockLength = flt.BlockLength;
var parentPrg;
// WrittenBorder = wBuffer.ptr
if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) {
if (wBuffer.ptr !== blockStart) {
// Copy blockStart bytes from rBuffer into wBuffer.
rarWriteArea(wBuffer.ptr, blockStart);
writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK;
}
if (blockLength <= writeSize) {
var blockEnd = (blockStart + blockLength) & MAXWINMASK;
if (blockStart < blockEnd || blockEnd === 0) {
VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + blockLength), blockLength);
} else {
var firstPartLength = MAXWINSIZE - blockStart;
VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + firstPartLength), firstPartLength);
VM.setMemory(firstPartLength, rBuffer.data, blockEnd);
}
parentPrg = Filters[flt.ParentFilter].Prg;
var prg = flt.Prg;
if (parentPrg.GlobalData.length > VM_FIXEDGLOBALSIZE) {
// Copy global data from previous script execution if any.
prg.GlobalData = new Uint8Array(parentPrg.GlobalData);
}
rarExecuteCode(prg);
var globalDataLen;
if (prg.GlobalData.length > VM_FIXEDGLOBALSIZE) {
// Save global data for next script execution.
globalDataLen = prg.GlobalData.length;
if (parentPrg.GlobalData.length < globalDataLen) {
parentPrg.GlobalData = new Uint8Array(globalDataLen);
}
parentPrg.GlobalData.set(
this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen),
VM_FIXEDGLOBALSIZE);
} else {
parentPrg.GlobalData = new Uint8Array(0);
}
var filteredData = prg.FilteredData;
PrgStack[i] = null;
while (i + 1 < PrgStack.length) {
var nextFilter = PrgStack[i + 1];
if (nextFilter === null || nextFilter.BlockStart !== blockStart ||
nextFilter.BlockLength !== filteredData.length || nextFilter.NextWindow) {
break;
}
// Apply several filters to same data block.
VM.setMemory(0, filteredData, filteredData.length);
parentPrg = Filters[nextFilter.ParentFilter].Prg;
var nextPrg = nextFilter.Prg;
globalDataLen = parentPrg.GlobalData.length;
if (globalDataLen > VM_FIXEDGLOBALSIZE) {
// Copy global data from previous script execution if any.
nextPrg.GlobalData = new Uint8Array(globalDataLen);
nextPrg.GlobalData.set(parentPrg.GlobalData.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), VM_FIXEDGLOBALSIZE);
}
rarExecuteCode(nextPrg);
if (nextPrg.GlobalData.length > VM_GLOBALMEMSIZE) {
// Save global data for next script execution.
globalDataLen = nextPrg.GlobalData.length;
if (parentPrg.GlobalData.length < globalDataLen) {
parentPrg.GlobalData = new Uint8Array(globalDataLen);
}
parentPrg.GlobalData.set(
this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen),
VM_FIXEDGLOBALSIZE);
} else {
parentPrg.GlobalData = new Uint8Array(0);
}
filteredData = nextPrg.FilteredData;
i++;
PrgStack[i] = null;
} // while (i + 1 < PrgStack.length)
for (j = 0; j < filteredData.length; ++j) {
wBuffer.insertByte(filteredData[j]);
}
writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK;
} else { // if (blockLength <= writeSize)
for (j = i; j < PrgStack.length; ++j) {
flt = PrgStack[j];
if (flt !== null && flt.NextWindow) {
flt.NextWindow = false;
}
}
//WrPtr=WrittenBorder;
return;
}
} // if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize)
} // for (var i = 0; i < PrgStack.length; ++i)
// Write any remaining bytes from rBuffer to wBuffer;
rarWriteArea(wBuffer.ptr, rBuffer.ptr);
// Now that the filtered buffer has been written, swap it back to rBuffer.
rBuffer = wBuffer;
}
/**
* Copy bytes from rBuffer to wBuffer.
* @param {number} startPtr The starting point to copy from rBuffer.
* @param {number} endPtr The ending point to copy from rBuffer.
*/
function rarWriteArea(startPtr, endPtr) {
if (endPtr < startPtr) {
console.error("endPtr < startPtr, endPtr=" + endPtr + ", startPtr=" + startPtr);
// rarWriteData(startPtr, -(int)StartPtr & MAXWINMASK);
// RarWriteData(0, endPtr);
return;
} else if (startPtr < endPtr) {
rarWriteData(startPtr, endPtr - startPtr);
}
}
/**
* Writes bytes into wBuffer from rBuffer.
* @param {number} offset The starting point to copy bytes from rBuffer.
* @param {number} numBytes The number of bytes to copy.
*/
function rarWriteData(offset, numBytes) {
if (wBuffer.ptr >= rBuffer.data.length) {
return;
}
var leftToWrite = rBuffer.data.length - wBuffer.ptr;
if (numBytes > leftToWrite) {
numBytes = leftToWrite;
}
for (var i = 0; i < numBytes; ++i) {
wBuffer.insertByte(rBuffer.data[offset + i]);
}
}
/**
* @param {VM_PreparedProgram} prg
*/
function rarExecuteCode(prg) {
if (prg.GlobalData.length > 0) {
var writtenFileSize = wBuffer.ptr;
prg.InitR[6] = writtenFileSize;
VM.setLowEndianValue(prg.GlobalData, writtenFileSize, 0x24);
VM.setLowEndianValue(prg.GlobalData, (writtenFileSize >>> 32) >> 0, 0x28);
VM.execute(prg);
}
}
function rarReadEndOfBlock(bstream) {
rarUpdateProgress();
var NewTable = false,
NewFile = false;
if (bstream.readBits(1)) {
NewTable = true;
} else {
NewFile = true;
NewTable = !!bstream.readBits(1);
}
//tablesRead = !NewTable;
return !(NewFile || (NewTable && !rarReadTables(bstream)));
}
function rarInsertLastMatch(length, distance) {
lastDist = distance;
lastLength = length;
}
function rarInsertOldDist(distance) {
rOldDist.splice(3, 1);
rOldDist.splice(0, 0, distance);
}
/**
* Copies len bytes from distance bytes ago in the buffer to the end of the
* current byte buffer.
* @param {number} length How many bytes to copy.
* @param {number} distance How far back in the buffer from the current write
* pointer to start copying from.
*/
function rarCopyString(length, distance) {
var srcPtr = rBuffer.ptr - distance;
if (srcPtr < 0) {
var l = rOldBuffers.length;
while (srcPtr < 0) {
srcPtr = rOldBuffers[--l].data.length + srcPtr;
}
// TODO: lets hope that it never needs to read beyond file boundaries
while (length--) {
rBuffer.insertByte(rOldBuffers[l].data[srcPtr++]);
}
}
if (length > distance) {
while (length--) {
rBuffer.insertByte(rBuffer.data[srcPtr++]);
}
} else {
rBuffer.insertBytes(rBuffer.data.subarray(srcPtr, srcPtr + length));
}
}
/**
* @param {RarLocalFile} v
*/
function unpack(v) {
// TODO: implement what happens when unpVer is < 15
var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer;
// var Solid = v.header.LHD_SOLID;
var bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength);
rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize);
info("Unpacking " + v.filename + " RAR v" + Ver);
switch (Ver) {
case 15: // rar 1.5 compression
unpack15(); //(bstream, Solid);
break;
case 20: // rar 2.x compression
case 26: // files larger than 2GB
unpack20(bstream); //, Solid);
break;
case 29: // rar 3.x compression
case 36: // alternative hash
wBuffer = new bitjs.io.ByteBuffer(rBuffer.data.length);
unpack29(bstream);
break;
} // switch(method)
rOldBuffers.push(rBuffer);
// TODO: clear these old buffers when there's over 4MB of history
return rBuffer.data;
}
// bstream is a bit stream
var RarLocalFile = function(bstream) {
this.header = new RarVolumeHeader(bstream);
this.filename = this.header.filename;
if (this.header.headType !== FILE_HEAD && this.header.headType !== ENDARC_HEAD) {
this.isValid = false;
info("Error! RAR Volume did not include a FILE_HEAD header ");
} else {
// read in the compressed data
this.fileData = null;
if (this.header.packSize > 0) {
this.fileData = bstream.readBytes(this.header.packSize);
this.isValid = true;
}
}
};
RarLocalFile.prototype.unrar = function() {
if (!this.header.flags.LHD_SPLIT_BEFORE) {
// unstore file
if (this.header.method === 0x30) {
info("Unstore " + this.filename);
this.isValid = true;
currentBytesUnarchivedInFile += this.fileData.length;
currentBytesUnarchived += this.fileData.length;
// Create a new buffer and copy it over.
var len = this.header.packSize;
var newBuffer = new bitjs.io.ByteBuffer(len);
newBuffer.insertBytes(this.fileData);
this.fileData = newBuffer.data;
} else {
this.isValid = true;
this.fileData = unpack(this);
}
}
};
var unrar = function(arrayBuffer) {
currentFilename = "";
currentFileNumber = 0;
currentBytesUnarchivedInFile = 0;
currentBytesUnarchived = 0;
totalUncompressedBytesInArchive = 0;
totalFilesInArchive = 0;
postMessage(new bitjs.archive.UnarchiveStartEvent());
var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
var header = new RarVolumeHeader(bstream);
if (header.crc === 0x6152 &&
header.headType === 0x72 &&
header.flags.value === 0x1A21 &&
header.headSize === 7) {
info("Found RAR signature");
var mhead = new RarVolumeHeader(bstream);
if (mhead.headType !== MAIN_HEAD) {
info("Error! RAR did not include a MAIN_HEAD header");
} else {
var localFiles = [];
var localFile = null;
do {
try {
localFile = new RarLocalFile(bstream);
info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize);
if (localFile && localFile.isValid && localFile.header.packSize > 0) {
totalUncompressedBytesInArchive += localFile.header.unpackedSize;
localFiles.push(localFile);
} else if (localFile.header.packSize === 0 && localFile.header.unpackedSize === 0) {
localFile.isValid = true;
}
} catch (err) {
break;
}
//info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length);
} while (localFile.isValid);
totalFilesInArchive = localFiles.length;
// now we have all information but things are unpacked
localFiles.sort(alphanumCase);
info(localFiles.map(function(a) {
return a.filename;
}).join(", "));
for (var i = 0; i < localFiles.length; ++i) {
var localfile = localFiles[i];
// update progress
currentFilename = localfile.header.filename;
currentBytesUnarchivedInFile = 0;
// actually do the unzipping
localfile.unrar();
if (localfile.isValid) {
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
postProgress();
}
}
postProgress();
}
} else {
err("Invalid RAR file");
}
postMessage(new bitjs.archive.UnarchiveFinishEvent());
};
// event.data.file has the ArrayBuffer.
onmessage = function(event) {
var ab = event.data.file;
unrar(ab, true);
};
/**
* unrar.js
*
* Licensed under the MIT License
*
* Copyright(c) 2011 Google Inc.
* Copyright(c) 2011 antimatter15
*
* Reference Documentation:
*
* http://kthoom.googlecode.com/hg/docs/unrar.html
*/
/* global bitjs, importScripts, RarVM, Uint8Array, UnpackFilter */
/* global VM_FIXEDGLOBALSIZE, VM_GLOBALMEMSIZE, MAXWINMASK, VM_GLOBALMEMADDR, MAXWINSIZE */
// This file expects to be invoked as a Worker (see onmessage below).
/*importScripts("../io/bitstream.js");
importScripts("../io/bytebuffer.js");
importScripts("archive.js");
importScripts("rarvm.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) {
console.log(str)
//postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
};
var err = function(str) {
console.log(str)
//postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
};
var postProgress = function() {
/*postMessage(new bitjs.archive.UnarchiveProgressEvent(
currentFilename,
currentFileNumber,
currentBytesUnarchivedInFile,
currentBytesUnarchived,
totalUncompressedBytesInArchive,
totalFilesInArchive));*/
};
// shows a byte value as its hex representation
var nibble = "0123456789ABCDEF";
var byteValueToHexString = function(num) {
return nibble[num >> 4] + nibble[num & 0xF];
};
var twoByteValueToHexString = function(num) {
return nibble[(num >> 12) & 0xF] + nibble[(num >> 8) & 0xF] + nibble[(num >> 4) & 0xF] + nibble[num & 0xF];
};
// Volume Types
var MAIN_HEAD = 0x01,
ENCRYPT_HEAD = 0x04,
FILE_HEAD = 0x02,
SERVICE_HEAD = 0x03,
// COMM_HEAD = 0x75,
// AV_HEAD = 0x76,
// SUB_HEAD = 0x77,
// PROTECT_HEAD = 0x78,
// SIGN_HEAD = 0x79,
// NEWSUB_HEAD = 0x7a,
ENDARC_HEAD = 0x05;
// ============================================================================================== //
var RarMainVolumeHeader = function(bstream) {
var headPos = bstream.bytePtr;
// byte 1,2
info("Rar Volume Header @" + bstream.bytePtr);
this.crc = bstream.readBits(16);
info(" crc=" + this.crc);
// byte 3
this.headType = bstream.readBits(8);
info(" headType=" + this.headType);
// Get flags
// bytes 4,5
this.flags = {};
this.flags.value = bstream.readBits(16);
// byte 6
this.headSize = bstream.readBits(8);
// byte 7
if (bstream.readBits(8) === 1) {
info(" RarVersion=5");
}
// byte 8
bstream.readBits(8);
}
var vint = function(bstream) {
var size = 0;
var result = 0;
var loop = 0 ;
do {
size = bstream.readBits(8);
result |= (size & 0x7F) << (loop * 7);
loop++;
} while (size & 0x80 )
return result;
}
/**
* @param {bitjs.io.BitStream} bstream
* @constructor
*/
var RarVolumeHeader = function(bstream) {
var headPos = bstream.bytePtr;
// byte 1,2
info("Rar Volume Header @" + bstream.bytePtr);
this.crc = bstream.readBits(32);
info(" crc=" + this.crc);
// byte 3 + x Header size
this.headSize = vint(bstream);
info(" Header Size=" + this.headSize);
// byte 4
this.headType = bstream.readBits(8);
info(" headType=" + this.headType);
// Get Header flags
this.headFlags = {};
this.headFlags.value = bstream.peekBits(8);
info(" Header flags=" + byteValueToHexString(this.headFlags.value));
this.headFlags.EXTRA_AREA = !!bstream.readBits(1);
this.headFlags.DATA_AREA = !!bstream.readBits(1);
this.headFlags.UNKNOWN = !!bstream.readBits(1);
this.headFlags.CONTINUE_FROM = !!bstream.readBits(1);
this.headFlags.CONTNUE_TO = !!bstream.readBits(1);
this.headFlags.DEPENDS = !!bstream.readBits(1);
this.headFlags.CHILDBLOCK = !!bstream.readBits(1);
bstream.readBits(1); // unused*/
// Get extra AreaSize
if (this.headFlags.EXTRA_AREA) {
this.extraSize = vint(bstream);
} else {
this.extraSize = 0;
}
if (this.headFlags.DATA_AREA && (this.headType == FILE_HEAD || this.headType == SERVICE_HEAD)) {
this.packSize = vint(bstream);
// this.packSize = bstream.readBits(32);
}
this.flags = {};
this.flags.value = bstream.peekBits(8);
switch (this.headType) {
case MAIN_HEAD:
// this.flags = {};
// this.flags.value = bstream.peekBits(16);
this.flags.MHD_VOLUME = !!bstream.readBits(1);
this.flags.MHD_VOLUMNE_NO = !!bstream.readBits(1);
this.flags.MHD_SOLID = !!bstream.readBits(1);
this.flags.MHD_RECOVERY = !!bstream.readBits(1);
this.flags.MHD_LOCKED = !!bstream.readBits(1);
bstream.readBits(3); // unused*/
if (this.flags.MHD_VOLUMNE_NO) {
this.volumeNumber = vint(bstream);
}
bstream.readBytes(this.extraSize);
// break;
return; // Main Header finally parsed
// ------------
case FILE_HEAD:
case SERVICE_HEAD:
this.flags.DIRECTORY = !!bstream.readBits(1);
this.flags.TIME = !!bstream.readBits(1);
this.flags.CRC = !!bstream.readBits(1);
this.flags.UNPACK_UNKNOWN = !!bstream.readBits(1);
bstream.readBits(4);
if (this.flags.UNPACK_UNKNOWN) {
vint(bstream);
} else {
this.unpackedSize = vint(bstream);
}
this.fileAttr = vint(bstream);
if (this.flags.TIME) {
this.fileTime = bstream.readBits(32);
}
if (this.flags.CRC) {
this.fileCRC = bstream.readBits(32);
}
// var compInfo = vint(bstream);
this.unpVer = bstream.readBits(6);
this.solid = bstream.readBits(1);
bstream.readBits(1);
this.method = bstream.readBits(3);
this.dictSize = bstream.readBits(4);
bstream.readBits(1);
this.hostOS = vint(bstream);
this.nameSize = vint(bstream);
this.filename = bstream.readBytes(this.nameSize);
var _s = "";
for (var _i = 0; _i < this.filename.length ; _i++) {
_s += String.fromCharCode(this.filename[_i]);
}
this.filename = _s;
bstream.readBytes(this.extraSize);
break;
default:
info("Found a header of type 0x" + byteValueToHexString(this.headType));
// skip the rest of the header bytes (for now)
bstream.readBytes(this.headSize - 7);
break;
}
};
//var BLOCK_LZ = 0;
var rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224],
rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5],
rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12],
rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192],
rSDBits = [2, 2, 3, 4, 5, 6, 6, 6];
var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072,
4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304,
131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824,
655360, 720896, 786432, 851968, 917504, 983040
];
var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
];
var rLowDistRepCount = 16;
var rNC = 299,
rDC = 60,
rLDC = 17,
rRC = 28,
rBC = 20,
rHuffTableSize = (rNC + rDC + rRC + rLDC);
//var UnpBlockType = BLOCK_LZ;
var UnpOldTable = new Array(rHuffTableSize);
var BD = { //bitdecode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rBC)
};
var LD = { //litdecode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rNC)
};
var DD = { //distdecode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rDC)
};
var LDD = { //low dist decode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rLDC)
};
var RD = { //rep decode
DecodeLen: new Array(16),
DecodePos: new Array(16),
DecodeNum: new Array(rRC)
};
/**
* @type {Array<bitjs.io.ByteBuffer>}
*/
var rOldBuffers = [];
/**
* The current buffer we are unpacking to.
* @type {bitjs.io.ByteBuffer}
*/
var rBuffer;
/**
* The buffer of the final bytes after filtering (only used in Unpack29).
* @type {bitjs.io.ByteBuffer}
*/
var wBuffer;
var lowDistRepCount = 0;
var prevLowDist = 0;
var rOldDist = [0, 0, 0, 0];
var lastDist;
var lastLength;
/**
* In unpack.cpp, UnpPtr keeps track of what bytes have been unpacked
* into the Window buffer and WrPtr keeps track of what bytes have been
* actually written to disk after the unpacking and optional filtering
* has been done.
*
* In our case, rBuffer is the buffer for the unpacked bytes and wBuffer is
* the final output bytes.
*/
/**
* Read in Huffman tables for RAR
* @param {bitjs.io.BitStream} bstream
*/
function rarReadTables(bstream) {
var BitLength = new Array(rBC);
var Table = new Array(rHuffTableSize);
var i;
// before we start anything we need to get byte-aligned
bstream.readBits((8 - bstream.bitPtr) & 0x7);
if (bstream.readBits(1)) {
info("Error! PPM not implemented yet");
return;
}
if (!bstream.readBits(1)) { //discard old table
for (i = UnpOldTable.length; i--;) {
UnpOldTable[i] = 0;
}
}
// read in bit lengths
for (var I = 0; I < rBC; ++I) {
var Length = bstream.readBits(4);
if (Length === 15) {
var ZeroCount = bstream.readBits(4);
if (ZeroCount === 0) {
BitLength[I] = 15;
} else {
ZeroCount += 2;
while (ZeroCount-- > 0 && I < rBC) {
BitLength[I++] = 0;
}
--I;
}
} else {
BitLength[I] = Length;
}
}
// now all 20 bit lengths are obtained, we construct the Huffman Table:
rarMakeDecodeTables(BitLength, 0, BD, rBC);
var TableSize = rHuffTableSize;
//console.log(DecodeLen, DecodePos, DecodeNum);
for (i = 0; i < TableSize;) {
var N;
var num = rarDecodeNumber(bstream, BD);
if (num < 16) {
Table[i] = (num + UnpOldTable[i]) & 0xf;
i++;
} else if (num < 18) {
N = (num === 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
while (N-- > 0 && i < TableSize) {
Table[i] = Table[i - 1];
i++;
}
} else {
N = (num === 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
while (N-- > 0 && i < TableSize) {
Table[i++] = 0;
}
}
}
rarMakeDecodeTables(Table, 0, LD, rNC);
rarMakeDecodeTables(Table, rNC, DD, rDC);
rarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC);
rarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC);
for (i = UnpOldTable.length; i--;) {
UnpOldTable[i] = Table[i];
}
return true;
}
function rarDecodeNumber(bstream, dec) {
var DecodeLen = dec.DecodeLen,
DecodePos = dec.DecodePos,
DecodeNum = dec.DecodeNum;
var bitField = bstream.getBits() & 0xfffe;
//some sort of rolled out binary search
var bits = ((bitField < DecodeLen[8]) ?
((bitField < DecodeLen[4]) ?
((bitField < DecodeLen[2]) ?
((bitField < DecodeLen[1]) ? 1 : 2) :
((bitField < DecodeLen[3]) ? 3 : 4)) :
(bitField < DecodeLen[6]) ?
((bitField < DecodeLen[5]) ? 5 : 6) :
((bitField < DecodeLen[7]) ? 7 : 8)) :
((bitField < DecodeLen[12]) ?
((bitField < DecodeLen[10]) ?
((bitField < DecodeLen[9]) ? 9 : 10) :
((bitField < DecodeLen[11]) ? 11 : 12)) :
(bitField < DecodeLen[14]) ?
((bitField < DecodeLen[13]) ? 13 : 14) :
15));
bstream.readBits(bits);
var N = DecodePos[bits] + ((bitField - DecodeLen[bits - 1]) >>> (16 - bits));
return DecodeNum[N];
}
function rarMakeDecodeTables(BitLength, offset, dec, size) {
var DecodeLen = dec.DecodeLen;
var DecodePos = dec.DecodePos;
var DecodeNum = dec.DecodeNum;
var LenCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var TmpPos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var N = 0;
var M = 0;
var i;
for (i = DecodeNum.length; i--;) {
DecodeNum[i] = 0;
}
for (i = 0; i < size; i++) {
LenCount[BitLength[i + offset] & 0xF]++;
}
LenCount[0] = 0;
TmpPos[0] = 0;
DecodePos[0] = 0;
DecodeLen[0] = 0;
var I;
for (I = 1; I < 16; ++I) {
N = 2 * (N + LenCount[I]);
M = (N << (15 - I));
if (M > 0xFFFF) {
M = 0xFFFF;
}
DecodeLen[I] = M;
DecodePos[I] = DecodePos[I - 1] + LenCount[I - 1];
TmpPos[I] = DecodePos[I];
}
for (I = 0; I < size; ++I) {
if (BitLength[I + offset] !== 0) {
DecodeNum[TmpPos[BitLength[offset + I] & 0xF]++] = I;
}
}
}
// TODO: implement
/**
* @param {bitjs.io.BitStream} bstream
* @param {boolean} Solid
*/
function unpack15() { //bstream, Solid) {
info("ERROR! RAR 1.5 compression not supported");
}
/**
* Unpacks the bit stream into rBuffer using the Unpack20 algorithm.
* @param {bitjs.io.BitStream} bstream
* @param {boolean} Solid
*/
function unpack20(bstream) { //, Solid) {
var destUnpSize = rBuffer.data.length;
var oldDistPtr = 0;
var Length;
var Distance;
rarReadTables20(bstream);
while (destUnpSize > rBuffer.ptr) {
var num = rarDecodeNumber(bstream, LD);
var Bits;
if (num < 256) {
rBuffer.insertByte(num);
continue;
}
if (num > 269) {
Length = rLDecode[num -= 270] + 3;
if ((Bits = rLBits[num]) > 0) {
Length += bstream.readBits(Bits);
}
var DistNumber = rarDecodeNumber(bstream, DD);
Distance = rDDecode[DistNumber] + 1;
if ((Bits = rDBits[DistNumber]) > 0) {
Distance += bstream.readBits(Bits);
}
if (Distance >= 0x2000) {
Length++;
if (Distance >= 0x40000) {
Length++;
}
}
lastLength = Length;
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
rarCopyString(Length, Distance);
continue;
}
if (num === 269) {
rarReadTables20(bstream);
rarUpdateProgress();
continue;
}
if (num === 256) {
lastDist = rOldDist[oldDistPtr++ & 3] = lastDist;
rarCopyString(lastLength, lastDist);
continue;
}
if (num < 261) {
Distance = rOldDist[(oldDistPtr - (num - 256)) & 3];
var LengthNumber = rarDecodeNumber(bstream, RD);
Length = rLDecode[LengthNumber] + 2;
if ((Bits = rLBits[LengthNumber]) > 0) {
Length += bstream.readBits(Bits);
}
if (Distance >= 0x101) {
Length++;
if (Distance >= 0x2000) {
Length++;
if (Distance >= 0x40000) {
Length++;
}
}
}
lastLength = Length;
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
rarCopyString(Length, Distance);
continue;
}
if (num < 270) {
Distance = rSDDecode[num -= 261] + 1;
if ((Bits = rSDBits[num]) > 0) {
Distance += bstream.readBits(Bits);
}
lastLength = 2;
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
rarCopyString(2, Distance);
continue;
}
}
rarUpdateProgress();
}
function rarUpdateProgress() {
var change = rBuffer.ptr - currentBytesUnarchivedInFile;
currentBytesUnarchivedInFile = rBuffer.ptr;
currentBytesUnarchived += change;
postProgress();
}
var rNC20 = 298,
rDC20 = 48,
rRC20 = 28,
rBC20 = 19,
rMC20 = 257;
var UnpOldTable20 = new Array(rMC20 * 4);
function rarReadTables20(bstream) {
var BitLength = new Array(rBC20);
var Table = new Array(rMC20 * 4);
var TableSize, N, I;
var i;
bstream.readBits(1);
if (!bstream.readBits(1)) {
for (i = UnpOldTable20.length; i--;) {
UnpOldTable20[i] = 0;
}
}
TableSize = rNC20 + rDC20 + rRC20;
for (I = 0; I < rBC20; I++) {
BitLength[I] = bstream.readBits(4);
}
rarMakeDecodeTables(BitLength, 0, BD, rBC20);
I = 0;
while (I < TableSize) {
var num = rarDecodeNumber(bstream, BD);
if (num < 16) {
Table[I] = num + UnpOldTable20[I] & 0xf;
I++;
} else if (num === 16) {
N = bstream.readBits(2) + 3;
while (N-- > 0 && I < TableSize) {
Table[I] = Table[I - 1];
I++;
}
} else {
if (num === 17) {
N = bstream.readBits(3) + 3;
} else {
N = bstream.readBits(7) + 11;
}
while (N-- > 0 && I < TableSize) {
Table[I++] = 0;
}
}
}
rarMakeDecodeTables(Table, 0, LD, rNC20);
rarMakeDecodeTables(Table, rNC20, DD, rDC20);
rarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20);
for (i = UnpOldTable20.length; i--;) {
UnpOldTable20[i] = Table[i];
}
}
// ============================================================================================== //
// Unpack code specific to RarVM
var VM = new RarVM();
/**
* Filters code, one entry per filter.
* @type {Array<UnpackFilter>}
*/
var Filters = [];
/**
* Filters stack, several entrances of same filter are possible.
* @type {Array<UnpackFilter>}
*/
var PrgStack = [];
/**
* Lengths of preceding blocks, one length per filter. Used to reduce
* size required to write block length if lengths are repeating.
* @type {Array<number>}
*/
var OldFilterLengths = [];
var LastFilter = 0;
function initFilters() {
OldFilterLengths = [];
LastFilter = 0;
Filters = [];
PrgStack = [];
}
/**
* @param {number} firstByte The first byte (flags).
* @param {Uint8Array} vmCode An array of bytes.
*/
function rarAddVMCode(firstByte, vmCode) {
VM.init();
var i;
var bstream = new bitjs.io.BitStream(vmCode.buffer, true /* rtl */ );
var filtPos;
if (firstByte & 0x80) {
filtPos = RarVM.readData(bstream);
if (filtPos === 0) {
initFilters();
} else {
filtPos--;
}
} else {
filtPos = LastFilter;
}
if (filtPos > Filters.length || filtPos > OldFilterLengths.length) {
return false;
}
LastFilter = filtPos;
var newFilter = (filtPos === Filters.length);
// new filter for PrgStack
var stackFilter = new UnpackFilter();
var filter = null;
// new filter code, never used before since VM reset
if (newFilter) {
// too many different filters, corrupt archive
if (filtPos > 1024) {
return false;
}
filter = new UnpackFilter();
Filters.push(filter);
stackFilter.ParentFilter = (Filters.length - 1);
OldFilterLengths.push(0); // OldFilterLengths.Add(1)
filter.ExecCount = 0;
} else { // filter was used in the past
filter = Filters[filtPos];
stackFilter.ParentFilter = filtPos;
filter.ExecCount++;
}
var emptyCount = 0;
for (i = 0; i < PrgStack.length; ++i) {
PrgStack[i - emptyCount] = PrgStack[i];
if (PrgStack[i] === null) {
emptyCount++;
}
if (emptyCount > 0) {
PrgStack[i] = null;
}
}
if (emptyCount === 0) {
PrgStack.push(null); //PrgStack.Add(1);
emptyCount = 1;
}
var stackPos = PrgStack.length - emptyCount;
PrgStack[stackPos] = stackFilter;
stackFilter.ExecCount = filter.ExecCount;
var blockStart = RarVM.readData(bstream);
if (firstByte & 0x40) {
blockStart += 258;
}
stackFilter.BlockStart = (blockStart + rBuffer.ptr) & MAXWINMASK;
if (firstByte & 0x20) {
stackFilter.BlockLength = RarVM.readData(bstream);
} else {
stackFilter.BlockLength = filtPos < OldFilterLengths.length ?
OldFilterLengths[filtPos] :
0;
}
stackFilter.NextWindow = (wBuffer.ptr !== rBuffer.ptr) &&
(((wBuffer.ptr - rBuffer.ptr) & MAXWINMASK) <= blockStart);
OldFilterLengths[filtPos] = stackFilter.BlockLength;
for (i = 0; i < 7; ++i) {
stackFilter.Prg.InitR[i] = 0;
}
stackFilter.Prg.InitR[3] = VM_GLOBALMEMADDR;
stackFilter.Prg.InitR[4] = stackFilter.BlockLength;
stackFilter.Prg.InitR[5] = stackFilter.ExecCount;
// set registers to optional parameters if any
if (firstByte & 0x10) {
var initMask = bstream.readBits(7);
for (i = 0; i < 7; ++i) {
if (initMask & (1 << i)) {
stackFilter.Prg.InitR[i] = RarVM.readData(bstream);
}
}
}
if (newFilter) {
var vmCodeSize = RarVM.readData(bstream);
if (vmCodeSize >= 0x10000 || vmCodeSize === 0) {
return false;
}
vmCode = new Uint8Array(vmCodeSize);
for (i = 0; i < vmCodeSize; ++i) {
//if (Inp.Overflow(3))
// return(false);
vmCode[i] = bstream.readBits(8);
}
VM.prepare(vmCode, filter.Prg);
}
stackFilter.Prg.Cmd = filter.Prg.Cmd;
stackFilter.Prg.AltCmd = filter.Prg.Cmd;
var staticDataSize = filter.Prg.StaticData.length;
if (staticDataSize > 0 && staticDataSize < VM_GLOBALMEMSIZE) {
// read statically defined data contained in DB commands
for (i = 0; i < staticDataSize; ++i) {
stackFilter.Prg.StaticData[i] = filter.Prg.StaticData[i];
}
}
if (stackFilter.Prg.GlobalData.length < VM_FIXEDGLOBALSIZE) {
stackFilter.Prg.GlobalData = new Uint8Array(VM_FIXEDGLOBALSIZE);
}
var globalData = stackFilter.Prg.GlobalData;
for (i = 0; i < 7; ++i) {
VM.setLowEndianValue(globalData, stackFilter.Prg.InitR[i], i * 4);
}
VM.setLowEndianValue(globalData, stackFilter.BlockLength, 0x1c);
VM.setLowEndianValue(globalData, 0, 0x20);
VM.setLowEndianValue(globalData, stackFilter.ExecCount, 0x2c);
for (i = 0; i < 16; ++i) {
globalData[0x30 + i] = 0;
}
// put data block passed as parameter if any
if (firstByte & 8) {
//if (Inp.Overflow(3))
// return(false);
var dataSize = RarVM.readData(bstream);
if (dataSize > (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)) {
return (false);
}
var curSize = stackFilter.Prg.GlobalData.length;
if (curSize < dataSize + VM_FIXEDGLOBALSIZE) {
// Resize global data and update the stackFilter and local variable.
var numBytesToAdd = dataSize + VM_FIXEDGLOBALSIZE - curSize;
var newGlobalData = new Uint8Array(globalData.length + numBytesToAdd);
newGlobalData.set(globalData);
stackFilter.Prg.GlobalData = newGlobalData;
globalData = newGlobalData;
}
//byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE];
for (i = 0; i < dataSize; ++i) {
//if (Inp.Overflow(3))
// return(false);
globalData[VM_FIXEDGLOBALSIZE + i] = bstream.readBits(8);
}
}
return true;
}
/**
* @param {!bitjs.io.BitStream} bstream
*/
function rarReadVMCode(bstream) {
var firstByte = bstream.readBits(8);
var length = (firstByte & 7) + 1;
if (length === 7) {
length = bstream.readBits(8) + 7;
} else if (length === 8) {
length = bstream.readBits(16);
}
// Read all bytes of VM code into an array.
var vmCode = new Uint8Array(length);
for (var i = 0; i < length; i++) {
// Do something here with checking readbuf.
vmCode[i] = bstream.readBits(8);
}
return rarAddVMCode(firstByte, vmCode);
}
/**
* Unpacks the bit stream into rBuffer using the Unpack29 algorithm.
* @param {bitjs.io.BitStream} bstream
* @param {boolean} Solid
*/
function unpack29(bstream) {
// lazy initialize rDDecode and rDBits
var DDecode = new Array(rDC);
var DBits = new Array(rDC);
var Distance = 0;
var Length = 0;
var Dist = 0, BitLength = 0, Slot = 0;
var I;
for (I = 0; I < rDBitLengthCounts.length; I++, BitLength++) {
for (var J = 0; J < rDBitLengthCounts[I]; J++, Slot++, Dist += (1 << BitLength)) {
DDecode[Slot] = Dist;
DBits[Slot] = BitLength;
}
}
var Bits;
//tablesRead = false;
rOldDist = [0, 0, 0, 0];
lastDist = 0;
lastLength = 0;
var i;
for (i = UnpOldTable.length; i--;) {
UnpOldTable[i] = 0;
}
// read in Huffman tables
rarReadTables(bstream);
while (true) {
var num = rarDecodeNumber(bstream, LD);
if (num < 256) {
rBuffer.insertByte(num);
continue;
}
if (num >= 271) {
Length = rLDecode[num -= 271] + 3;
if ((Bits = rLBits[num]) > 0) {
Length += bstream.readBits(Bits);
}
var DistNumber = rarDecodeNumber(bstream, DD);
Distance = DDecode[DistNumber] + 1;
if ((Bits = DBits[DistNumber]) > 0) {
if (DistNumber > 9) {
if (Bits > 4) {
Distance += ((bstream.getBits() >>> (20 - Bits)) << 4);
bstream.readBits(Bits - 4);
//todo: check this
}
if (lowDistRepCount > 0) {
lowDistRepCount--;
Distance += prevLowDist;
} else {
var LowDist = rarDecodeNumber(bstream, LDD);
if (LowDist === 16) {
lowDistRepCount = rLowDistRepCount - 1;
Distance += prevLowDist;
} else {
Distance += LowDist;
prevLowDist = LowDist;
}
}
} else {
Distance += bstream.readBits(Bits);
}
}
if (Distance >= 0x2000) {
Length++;
if (Distance >= 0x40000) {
Length++;
}
}
rarInsertOldDist(Distance);
rarInsertLastMatch(Length, Distance);
rarCopyString(Length, Distance);
continue;
}
if (num === 256) {
if (!rarReadEndOfBlock(bstream)) {
break;
}
continue;
}
if (num === 257) {
if (!rarReadVMCode(bstream)) {
break;
}
continue;
}
if (num === 258) {
if (lastLength !== 0) {
rarCopyString(lastLength, lastDist);
}
continue;
}
if (num < 263) {
var DistNum = num - 259;
Distance = rOldDist[DistNum];
for (var I2 = DistNum; I2 > 0; I2--) {
rOldDist[I2] = rOldDist[I2 - 1];
}
rOldDist[0] = Distance;
var LengthNumber = rarDecodeNumber(bstream, RD);
Length = rLDecode[LengthNumber] + 2;
if ((Bits = rLBits[LengthNumber]) > 0) {
Length += bstream.readBits(Bits);
}
rarInsertLastMatch(Length, Distance);
rarCopyString(Length, Distance);
continue;
}
if (num < 272) {
Distance = rSDDecode[num -= 263] + 1;
if ((Bits = rSDBits[num]) > 0) {
Distance += bstream.readBits(Bits);
}
rarInsertOldDist(Distance);
rarInsertLastMatch(2, Distance);
rarCopyString(2, Distance);
continue;
}
} // while (true)
rarUpdateProgress();
rarWriteBuf();
}
/**
* Does stuff to the current byte buffer (rBuffer) based on
* the filters loaded into the RarVM and writes out to wBuffer.
*/
function rarWriteBuf() {
var writeSize = (rBuffer.ptr & MAXWINMASK);
var j;
var flt;
for (var i = 0; i < PrgStack.length; ++i) {
flt = PrgStack[i];
if (flt === null) {
continue;
}
if (flt.NextWindow) {
flt.NextWindow = false;
continue;
}
var blockStart = flt.BlockStart;
var blockLength = flt.BlockLength;
var parentPrg;
// WrittenBorder = wBuffer.ptr
if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) {
if (wBuffer.ptr !== blockStart) {
// Copy blockStart bytes from rBuffer into wBuffer.
rarWriteArea(wBuffer.ptr, blockStart);
writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK;
}
if (blockLength <= writeSize) {
var blockEnd = (blockStart + blockLength) & MAXWINMASK;
if (blockStart < blockEnd || blockEnd === 0) {
VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + blockLength), blockLength);
} else {
var firstPartLength = MAXWINSIZE - blockStart;
VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + firstPartLength), firstPartLength);
VM.setMemory(firstPartLength, rBuffer.data, blockEnd);
}
parentPrg = Filters[flt.ParentFilter].Prg;
var prg = flt.Prg;
if (parentPrg.GlobalData.length > VM_FIXEDGLOBALSIZE) {
// Copy global data from previous script execution if any.
prg.GlobalData = new Uint8Array(parentPrg.GlobalData);
}
rarExecuteCode(prg);
var globalDataLen;
if (prg.GlobalData.length > VM_FIXEDGLOBALSIZE) {
// Save global data for next script execution.
globalDataLen = prg.GlobalData.length;
if (parentPrg.GlobalData.length < globalDataLen) {
parentPrg.GlobalData = new Uint8Array(globalDataLen);
}
parentPrg.GlobalData.set(
this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen),
VM_FIXEDGLOBALSIZE);
} else {
parentPrg.GlobalData = new Uint8Array(0);
}
var filteredData = prg.FilteredData;
PrgStack[i] = null;
while (i + 1 < PrgStack.length) {
var nextFilter = PrgStack[i + 1];
if (nextFilter === null || nextFilter.BlockStart !== blockStart ||
nextFilter.BlockLength !== filteredData.length || nextFilter.NextWindow) {
break;
}
// Apply several filters to same data block.
VM.setMemory(0, filteredData, filteredData.length);
parentPrg = Filters[nextFilter.ParentFilter].Prg;
var nextPrg = nextFilter.Prg;
globalDataLen = parentPrg.GlobalData.length;
if (globalDataLen > VM_FIXEDGLOBALSIZE) {
// Copy global data from previous script execution if any.
nextPrg.GlobalData = new Uint8Array(globalDataLen);
nextPrg.GlobalData.set(parentPrg.GlobalData.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), VM_FIXEDGLOBALSIZE);
}
rarExecuteCode(nextPrg);
if (nextPrg.GlobalData.length > VM_GLOBALMEMSIZE) {
// Save global data for next script execution.
globalDataLen = nextPrg.GlobalData.length;
if (parentPrg.GlobalData.length < globalDataLen) {
parentPrg.GlobalData = new Uint8Array(globalDataLen);
}
parentPrg.GlobalData.set(
this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen),
VM_FIXEDGLOBALSIZE);
} else {
parentPrg.GlobalData = new Uint8Array(0);
}
filteredData = nextPrg.FilteredData;
i++;
PrgStack[i] = null;
} // while (i + 1 < PrgStack.length)
for (j = 0; j < filteredData.length; ++j) {
wBuffer.insertByte(filteredData[j]);
}
writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK;
} else { // if (blockLength <= writeSize)
for (j = i; j < PrgStack.length; ++j) {
flt = PrgStack[j];
if (flt !== null && flt.NextWindow) {
flt.NextWindow = false;
}
}
//WrPtr=WrittenBorder;
return;
}
} // if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize)
} // for (var i = 0; i < PrgStack.length; ++i)
// Write any remaining bytes from rBuffer to wBuffer;
rarWriteArea(wBuffer.ptr, rBuffer.ptr);
// Now that the filtered buffer has been written, swap it back to rBuffer.
rBuffer = wBuffer;
}
/**
* Copy bytes from rBuffer to wBuffer.
* @param {number} startPtr The starting point to copy from rBuffer.
* @param {number} endPtr The ending point to copy from rBuffer.
*/
function rarWriteArea(startPtr, endPtr) {
if (endPtr < startPtr) {
console.error("endPtr < startPtr, endPtr=" + endPtr + ", startPtr=" + startPtr);
// rarWriteData(startPtr, -(int)StartPtr & MAXWINMASK);
// RarWriteData(0, endPtr);
return;
} else if (startPtr < endPtr) {
rarWriteData(startPtr, endPtr - startPtr);
}
}
/**
* Writes bytes into wBuffer from rBuffer.
* @param {number} offset The starting point to copy bytes from rBuffer.
* @param {number} numBytes The number of bytes to copy.
*/
function rarWriteData(offset, numBytes) {
if (wBuffer.ptr >= rBuffer.data.length) {
return;
}
var leftToWrite = rBuffer.data.length - wBuffer.ptr;
if (numBytes > leftToWrite) {
numBytes = leftToWrite;
}
for (var i = 0; i < numBytes; ++i) {
wBuffer.insertByte(rBuffer.data[offset + i]);
}
}
/**
* @param {VM_PreparedProgram} prg
*/
function rarExecuteCode(prg) {
if (prg.GlobalData.length > 0) {
var writtenFileSize = wBuffer.ptr;
prg.InitR[6] = writtenFileSize;
VM.setLowEndianValue(prg.GlobalData, writtenFileSize, 0x24);
VM.setLowEndianValue(prg.GlobalData, (writtenFileSize >>> 32) >> 0, 0x28);
VM.execute(prg);
}
}
function rarReadEndOfBlock(bstream) {
rarUpdateProgress();
var NewTable = false,
NewFile = false;
if (bstream.readBits(1)) {
NewTable = true;
} else {
NewFile = true;
NewTable = !!bstream.readBits(1);
}
//tablesRead = !NewTable;
return !(NewFile || (NewTable && !rarReadTables(bstream)));
}
function rarInsertLastMatch(length, distance) {
lastDist = distance;
lastLength = length;
}
function rarInsertOldDist(distance) {
rOldDist.splice(3, 1);
rOldDist.splice(0, 0, distance);
}
/**
* Copies len bytes from distance bytes ago in the buffer to the end of the
* current byte buffer.
* @param {number} length How many bytes to copy.
* @param {number} distance How far back in the buffer from the current write
* pointer to start copying from.
*/
function rarCopyString(length, distance) {
var srcPtr = rBuffer.ptr - distance;
if (srcPtr < 0) {
var l = rOldBuffers.length;
while (srcPtr < 0) {
srcPtr = rOldBuffers[--l].data.length + srcPtr;
}
// TODO: lets hope that it never needs to read beyond file boundaries
while (length--) {
rBuffer.insertByte(rOldBuffers[l].data[srcPtr++]);
}
}
if (length > distance) {
while (length--) {
rBuffer.insertByte(rBuffer.data[srcPtr++]);
}
} else {
rBuffer.insertBytes(rBuffer.data.subarray(srcPtr, srcPtr + length));
}
}
/**
* @param {RarLocalFile} v
*/
function unpack(v) {
// TODO: implement what happens when unpVer is < 15
var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer;
// var Solid = v.header.LHD_SOLID;
var bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength);
rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize);
info("Unpacking " + v.filename + " RAR v" + Ver);
switch (Ver) {
case 15: // rar 1.5 compression
unpack15(); //(bstream, Solid);
break;
case 20: // rar 2.x compression
case 26: // files larger than 2GB
unpack20(bstream); //, Solid);
break;
case 29: // rar 3.x compression
case 36: // alternative hash
wBuffer = new bitjs.io.ByteBuffer(rBuffer.data.length);
unpack29(bstream);
break;
} // switch(method)
rOldBuffers.push(rBuffer);
// TODO: clear these old buffers when there's over 4MB of history
return rBuffer.data;
}
// bstream is a bit stream
var RarLocalFile = function(bstream) {
this.header = new RarVolumeHeader(bstream);
this.filename = this.header.filename;
if (this.header.headType !== FILE_HEAD && this.header.headType !== ENDARC_HEAD && this.header.headType !== SERVICE_HEAD) {
this.isValid = false;
info("Error! RAR Volume did not include a FILE_HEAD header ");
} else {
// read in the compressed data
this.fileData = null;
if (this.header.packSize > 0) {
this.fileData = bstream.readBytes(this.header.packSize);
if (this.header.headType === FILE_HEAD) {
this.isValid = true;
}
}
}
};
RarLocalFile.prototype.unrar5 = function() {
//if (!this.header.flags.LHD_SPLIT_BEFORE) {
// unstore file
// No compression
if (this.header.method === 0x00) {
info("Unstore " + this.filename);
this.isValid = true;
currentBytesUnarchivedInFile += this.fileData.length;
currentBytesUnarchived += this.fileData.length;
// Create a new buffer and copy it over.
var len = this.header.packSize;
var newBuffer = new bitjs.io.ByteBuffer(len);
newBuffer.insertBytes(this.fileData);
this.fileData = newBuffer.data;
} else {
this.isValid = true;
this.fileData = unpack(this);
}
//}
};
var unrar5 = function(arrayBuffer) {
currentFilename = "";
currentFileNumber = 0;
currentBytesUnarchivedInFile = 0;
currentBytesUnarchived = 0;
totalUncompressedBytesInArchive = 0;
totalFilesInArchive = 0;
// postMessage(new bitjs.archive.UnarchiveStartEvent());
var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
var header = new RarMainVolumeHeader(bstream);
if (header.crc === 0x6152 &&
header.headType === 0x72 &&
header.flags.value === 0x1A21 &&
header.headSize === 7) {
info("Found RAR signature");
var mhead = new RarVolumeHeader(bstream);
if (mhead.headType !== MAIN_HEAD) {
info("Error! RAR did not include a MAIN_HEAD header");
} else {
var localFiles = [];
var localFile = null;
do {
try {
localFile = new RarLocalFile(bstream);
info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize);
if (localFile && localFile.isValid && localFile.header.packSize > 0) {
totalUncompressedBytesInArchive += localFile.header.unpackedSize;
localFiles.push(localFile);
} else if (localFile.header.packSize === 0 && localFile.header.unpackedSize === 0) {
localFile.isValid = true;
}
} catch (err) {
break;
}
//info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length);
} while (localFile.isValid);
totalFilesInArchive = localFiles.length;
// now we have all information but things are unpacked
localFiles.sort(alphanumCase);
info(localFiles.map(function(a) {
return a.filename;
}).join(", "));
for (var i = 0; i < localFiles.length; ++i) {
var localfile = localFiles[i];
// update progress
currentFilename = localfile.header.filename;
currentBytesUnarchivedInFile = 0;
// actually do the unzipping
localfile.unrar5();
if (localfile.isValid) {
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
postProgress();
}
}
postProgress();
}
} else {
err("Invalid RAR file");
}
// postMessage(new bitjs.archive.UnarchiveFinishEvent());
};
// event.data.file has the ArrayBuffer.
onmessage = function(event) {
var ab = event.data.file;
unrar5(ab, true);
};
/**
* 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;
}
}
};
/**
* 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);
};
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// 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;
})();
// 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;
})();
/*
* 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);
};
})();
/*
* 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);
}
};
})();
/*
* 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,43 +182,22 @@ 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;
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(f.filename) === -1) {
var test = new kthoom.ImageFile(f);
if ( test.mimeType !== undefined) {
imageFilenames.push(f.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(
......@@ -240,17 +216,12 @@ function loadFromArrayBuffer(ab) {
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") {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment