// @ts-nocheck // // jDataView by Vjeux - Jan 2010 // Continued by RReverser - Feb 2013 // // A unique way to work with a binary file in the browser // http://github.com/jDataView/jDataView // http://jDataView.github.io/ var compatibility = { // NodeJS Buffer in v0.5.5 and newer NodeBuffer: "Buffer" in globalThis && "readInt16LE" in Buffer.prototype, DataView: "DataView" in globalThis && ("getFloat64" in DataView.prototype || // Chrome "getFloat64" in new DataView(new ArrayBuffer(1))), // Node ArrayBuffer: "ArrayBuffer" in globalThis, PixelData: "CanvasPixelArray" in globalThis && "ImageData" in globalThis && "document" in globalThis, }; var createPixelData = function (byteLength, buffer) { var data = createPixelData.context2d.createImageData( (byteLength + 3) / 4, 1 ).data; data.byteLength = byteLength; if (buffer !== undefined) { for (var i = 0; i < byteLength; i++) { data[i] = buffer[i]; } } return data; }; createPixelData.context2d = document.createElement("canvas").getContext("2d"); var dataTypes = { Int8: 1, Int16: 2, Int32: 4, Uint8: 1, Uint16: 2, Uint32: 4, Float32: 4, Float64: 8, }; var nodeNaming = { Int8: "Int8", Int16: "Int16", Int32: "Int32", Uint8: "UInt8", Uint16: "UInt16", Uint32: "UInt32", Float32: "Float", Float64: "Double", }; function arrayFrom(arrayLike, forceCopy) { return !forceCopy && arrayLike instanceof Array ? arrayLike : Array.prototype.slice.call(arrayLike); } function defined(value, defaultValue) { return value !== undefined ? value : defaultValue; } export function jDataView(buffer, byteOffset, byteLength, littleEndian) { /* jshint validthis:true */ if (buffer instanceof jDataView) { var result = buffer.slice(byteOffset, byteOffset + byteLength); result._littleEndian = defined(littleEndian, result._littleEndian); return result; } if (!(this instanceof jDataView)) { return new jDataView(buffer, byteOffset, byteLength, littleEndian); } this.buffer = buffer = jDataView.wrapBuffer(buffer); // Check parameters and existing functionnalities this._isArrayBuffer = compatibility.ArrayBuffer && buffer instanceof ArrayBuffer; this._isPixelData = compatibility.PixelData && buffer instanceof CanvasPixelArray; this._isDataView = compatibility.DataView && this._isArrayBuffer; this._isNodeBuffer = compatibility.NodeBuffer && buffer instanceof Buffer; // Handle Type Errors if ( !this._isNodeBuffer && !this._isArrayBuffer && !this._isPixelData && !(buffer instanceof Array) ) { throw new TypeError("jDataView buffer has an incompatible type"); } // Default Values this._littleEndian = !!littleEndian; var bufferLength = "byteLength" in buffer ? buffer.byteLength : buffer.length; this.byteOffset = byteOffset = defined(byteOffset, 0); this.byteLength = byteLength = defined(byteLength, bufferLength - byteOffset); if (!this._isDataView) { this._checkBounds(byteOffset, byteLength, bufferLength); } else { this._view = new DataView(buffer, byteOffset, byteLength); } // Create uniform methods (action wrappers) for the following data types this._engineAction = this._isDataView ? this._dataViewAction : this._isNodeBuffer ? this._nodeBufferAction : this._isArrayBuffer ? this._arrayBufferAction : this._arrayAction; } function getCharCodes(string) { if (compatibility.NodeBuffer) { return new Buffer(string, "binary"); } var Type = compatibility.ArrayBuffer ? Uint8Array : Array, codes = new Type(string.length); for (var i = 0, length = string.length; i < length; i++) { codes[i] = string.charCodeAt(i) & 0xff; } return codes; } // mostly internal function for wrapping any supported input (String or Array-like) to best suitable buffer format jDataView.wrapBuffer = function (buffer) { switch (typeof buffer) { case "number": if (compatibility.NodeBuffer) { buffer = new Buffer(buffer); buffer.fill(0); } else if (compatibility.ArrayBuffer) { buffer = new Uint8Array(buffer).buffer; } else if (compatibility.PixelData) { buffer = createPixelData(buffer); } else { buffer = new Array(buffer); for (var i = 0; i < buffer.length; i++) { buffer[i] = 0; } } return buffer; case "string": buffer = getCharCodes(buffer); /* falls through */ default: if ( "length" in buffer && !( (compatibility.NodeBuffer && buffer instanceof Buffer) || (compatibility.ArrayBuffer && buffer instanceof ArrayBuffer) || (compatibility.PixelData && buffer instanceof CanvasPixelArray) ) ) { if (compatibility.NodeBuffer) { buffer = new Buffer(buffer); } else if (compatibility.ArrayBuffer) { if (!(buffer instanceof ArrayBuffer)) { buffer = new Uint8Array(buffer).buffer; // bug in Node.js <= 0.8: if (!(buffer instanceof ArrayBuffer)) { buffer = new Uint8Array(arrayFrom(buffer, true)).buffer; } } } else if (compatibility.PixelData) { buffer = createPixelData(buffer.length, buffer); } else { buffer = arrayFrom(buffer); } } return buffer; } }; function pow2(n) { return n >= 0 && n < 31 ? 1 << n : pow2[n] || (pow2[n] = Math.pow(2, n)); } // left for backward compatibility jDataView.createBuffer = function () { return jDataView.wrapBuffer(arguments); }; function Uint64(lo, hi) { this.lo = lo; this.hi = hi; } jDataView.Uint64 = Uint64; Uint64.prototype = { valueOf: function () { return this.lo + pow2(32) * this.hi; }, toString: function () { return Number.prototype.toString.apply(this.valueOf(), arguments); }, }; Uint64.fromNumber = function (number) { var hi = Math.floor(number / pow2(32)), lo = number - hi * pow2(32); return new Uint64(lo, hi); }; function Int64(lo, hi) { Uint64.apply(this, arguments); } jDataView.Int64 = Int64; Int64.prototype = "create" in Object ? Object.create(Uint64.prototype) : new Uint64(); Int64.prototype.valueOf = function () { if (this.hi < pow2(31)) { return Uint64.prototype.valueOf.apply(this, arguments); } return -(pow2(32) - this.lo + pow2(32) * (pow2(32) - 1 - this.hi)); }; Int64.fromNumber = function (number) { var lo, hi; if (number >= 0) { var unsigned = Uint64.fromNumber(number); lo = unsigned.lo; hi = unsigned.hi; } else { hi = Math.floor(number / pow2(32)); lo = number - hi * pow2(32); hi += pow2(32); } return new Int64(lo, hi); }; jDataView.prototype = { _offset: 0, _bitOffset: 0, compatibility: compatibility, _checkBounds: function (byteOffset, byteLength, maxLength) { // Do additional checks to simulate DataView if (typeof byteOffset !== "number") { throw new TypeError("Offset is not a number."); } if (typeof byteLength !== "number") { throw new TypeError("Size is not a number."); } if (byteLength < 0) { throw new RangeError("Length is negative."); } if ( byteOffset < 0 || byteOffset + byteLength > defined(maxLength, this.byteLength) ) { throw new RangeError("Offsets are out of bounds."); } }, _action: function (type, isReadAction, byteOffset, littleEndian, value) { return this._engineAction( type, isReadAction, defined(byteOffset, this._offset), defined(littleEndian, this._littleEndian), value ); }, _dataViewAction: function ( type, isReadAction, byteOffset, littleEndian, value ) { // Move the internal offset forward this._offset = byteOffset + dataTypes[type]; return isReadAction ? this._view["get" + type](byteOffset, littleEndian) : this._view["set" + type](byteOffset, value, littleEndian); }, _nodeBufferAction: function ( type, isReadAction, byteOffset, littleEndian, value ) { // Move the internal offset forward this._offset = byteOffset + dataTypes[type]; var nodeName = nodeNaming[type] + (type === "Int8" || type === "Uint8" ? "" : littleEndian ? "LE" : "BE"); byteOffset += this.byteOffset; return isReadAction ? this.buffer["read" + nodeName](byteOffset) : this.buffer["write" + nodeName](value, byteOffset); }, _arrayBufferAction: function ( type, isReadAction, byteOffset, littleEndian, value ) { var size = dataTypes[type], TypedArray = globalThis[type + "Array"], typedArray; littleEndian = defined(littleEndian, this._littleEndian); // ArrayBuffer: we use a typed array of size 1 from original buffer if alignment is good and from slice when it's not if ( size === 1 || ((this.byteOffset + byteOffset) % size === 0 && littleEndian) ) { typedArray = new TypedArray(this.buffer, this.byteOffset + byteOffset, 1); this._offset = byteOffset + size; return isReadAction ? typedArray[0] : (typedArray[0] = value); } else { var bytes = new Uint8Array( isReadAction ? this.getBytes(size, byteOffset, littleEndian, true) : size ); typedArray = new TypedArray(bytes.buffer, 0, 1); if (isReadAction) { return typedArray[0]; } else { typedArray[0] = value; this._setBytes(byteOffset, bytes, littleEndian); } } }, _arrayAction: function (type, isReadAction, byteOffset, littleEndian, value) { return isReadAction ? this["_get" + type](byteOffset, littleEndian) : this["_set" + type](byteOffset, value, littleEndian); }, // Helpers _getBytes: function (length, byteOffset, littleEndian) { littleEndian = defined(littleEndian, this._littleEndian); byteOffset = defined(byteOffset, this._offset); length = defined(length, this.byteLength - byteOffset); this._checkBounds(byteOffset, length); byteOffset += this.byteOffset; this._offset = byteOffset - this.byteOffset + length; var result = this._isArrayBuffer ? new Uint8Array(this.buffer, byteOffset, length) : (this.buffer.slice || Array.prototype.slice).call( this.buffer, byteOffset, byteOffset + length ); return littleEndian || length <= 1 ? result : arrayFrom(result).reverse(); }, // wrapper for external calls (do not return inner buffer directly to prevent it's modifying) getBytes: function (length, byteOffset, littleEndian, toArray) { var result = this._getBytes( length, byteOffset, defined(littleEndian, true) ); return toArray ? arrayFrom(result) : result; }, _setBytes: function (byteOffset, bytes, littleEndian) { var length = bytes.length; // needed for Opera if (length === 0) { return; } littleEndian = defined(littleEndian, this._littleEndian); byteOffset = defined(byteOffset, this._offset); this._checkBounds(byteOffset, length); if (!littleEndian && length > 1) { bytes = arrayFrom(bytes, true).reverse(); } byteOffset += this.byteOffset; if (this._isArrayBuffer) { new Uint8Array(this.buffer, byteOffset, length).set(bytes); } else { if (this._isNodeBuffer) { new Buffer(bytes).copy(this.buffer, byteOffset); } else { for (var i = 0; i < length; i++) { this.buffer[byteOffset + i] = bytes[i]; } } } this._offset = byteOffset - this.byteOffset + length; }, setBytes: function (byteOffset, bytes, littleEndian) { this._setBytes(byteOffset, bytes, defined(littleEndian, true)); }, getString: function (byteLength, byteOffset, encoding) { if (this._isNodeBuffer) { byteOffset = defined(byteOffset, this._offset); byteLength = defined(byteLength, this.byteLength - byteOffset); this._checkBounds(byteOffset, byteLength); this._offset = byteOffset + byteLength; return this.buffer.toString( encoding || "binary", this.byteOffset + byteOffset, this.byteOffset + this._offset ); } var bytes = this._getBytes(byteLength, byteOffset, true), string = ""; byteLength = bytes.length; for (var i = 0; i < byteLength; i++) { string += String.fromCharCode(bytes[i]); } if (encoding === "utf8") { string = decodeURIComponent(escape(string)); } return string; }, setString: function (byteOffset, subString, encoding) { if (this._isNodeBuffer) { byteOffset = defined(byteOffset, this._offset); this._checkBounds(byteOffset, subString.length); this._offset = byteOffset + this.buffer.write( subString, this.byteOffset + byteOffset, encoding || "binary" ); return; } if (encoding === "utf8") { subString = unescape(encodeURIComponent(subString)); } this._setBytes(byteOffset, getCharCodes(subString), true); }, getChar: function (byteOffset) { return this.getString(1, byteOffset); }, setChar: function (byteOffset, character) { this.setString(byteOffset, character); }, tell: function () { return this._offset; }, seek: function (byteOffset) { this._checkBounds(byteOffset, 0); /* jshint boss: true */ return (this._offset = byteOffset); }, skip: function (byteLength) { return this.seek(this._offset + byteLength); }, slice: function (start, end, forceCopy) { function normalizeOffset(offset, byteLength) { return offset < 0 ? offset + byteLength : offset; } start = normalizeOffset(start, this.byteLength); end = normalizeOffset(defined(end, this.byteLength), this.byteLength); return forceCopy ? new jDataView( this.getBytes(end - start, start, true, true), undefined, undefined, this._littleEndian ) : new jDataView( this.buffer, this.byteOffset + start, end - start, this._littleEndian ); }, alignBy: function (byteCount) { this._bitOffset = 0; if (defined(byteCount, 1) !== 1) { return this.skip(byteCount - (this._offset % byteCount || byteCount)); } else { return this._offset; } }, // Compatibility functions _getFloat64: function (byteOffset, littleEndian) { var b = this._getBytes(8, byteOffset, littleEndian), sign = 1 - 2 * (b[7] >> 7), exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1), // Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead mantissa = (b[6] & 0x0f) * pow2(48) + b[5] * pow2(40) + b[4] * pow2(32) + b[3] * pow2(24) + b[2] * pow2(16) + b[1] * pow2(8) + b[0]; if (exponent === 1024) { if (mantissa !== 0) { return NaN; } else { return sign * Infinity; } } if (exponent === -1023) { // Denormalized return sign * mantissa * pow2(-1022 - 52); } return sign * (1 + mantissa * pow2(-52)) * pow2(exponent); }, _getFloat32: function (byteOffset, littleEndian) { var b = this._getBytes(4, byteOffset, littleEndian), sign = 1 - 2 * (b[3] >> 7), exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127, mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0]; if (exponent === 128) { if (mantissa !== 0) { return NaN; } else { return sign * Infinity; } } if (exponent === -127) { // Denormalized return sign * mantissa * pow2(-126 - 23); } return sign * (1 + mantissa * pow2(-23)) * pow2(exponent); }, _get64: function (Type, byteOffset, littleEndian) { littleEndian = defined(littleEndian, this._littleEndian); byteOffset = defined(byteOffset, this._offset); var parts = littleEndian ? [0, 4] : [4, 0]; for (var i = 0; i < 2; i++) { parts[i] = this.getUint32(byteOffset + parts[i], littleEndian); } this._offset = byteOffset + 8; return new Type(parts[0], parts[1]); }, getInt64: function (byteOffset, littleEndian) { return this._get64(Int64, byteOffset, littleEndian); }, getUint64: function (byteOffset, littleEndian) { return this._get64(Uint64, byteOffset, littleEndian); }, _getInt32: function (byteOffset, littleEndian) { var b = this._getBytes(4, byteOffset, littleEndian); return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; }, _getUint32: function (byteOffset, littleEndian) { return this._getInt32(byteOffset, littleEndian) >>> 0; }, _getInt16: function (byteOffset, littleEndian) { return (this._getUint16(byteOffset, littleEndian) << 16) >> 16; }, _getUint16: function (byteOffset, littleEndian) { var b = this._getBytes(2, byteOffset, littleEndian); return (b[1] << 8) | b[0]; }, _getInt8: function (byteOffset) { return (this._getUint8(byteOffset) << 24) >> 24; }, _getUint8: function (byteOffset) { return this._getBytes(1, byteOffset)[0]; }, _getBitRangeData: function (bitLength, byteOffset) { var startBit = (defined(byteOffset, this._offset) << 3) + this._bitOffset, endBit = startBit + bitLength, start = startBit >>> 3, end = (endBit + 7) >>> 3, b = this._getBytes(end - start, start, true), wideValue = 0; /* jshint boss: true */ if ((this._bitOffset = endBit & 7)) { this._bitOffset -= 8; } for (var i = 0, length = b.length; i < length; i++) { wideValue = (wideValue << 8) | b[i]; } return { start: start, bytes: b, wideValue: wideValue, }; }, getSigned: function (bitLength, byteOffset) { var shift = 32 - bitLength; return (this.getUnsigned(bitLength, byteOffset) << shift) >> shift; }, getUnsigned: function (bitLength, byteOffset) { var value = this._getBitRangeData(bitLength, byteOffset).wideValue >>> -this._bitOffset; return bitLength < 32 ? value & ~(-1 << bitLength) : value; }, _setBinaryFloat: function ( byteOffset, value, mantSize, expSize, littleEndian ) { var signBit = value < 0 ? 1 : 0, exponent, mantissa, eMax = ~(-1 << (expSize - 1)), eMin = 1 - eMax; if (value < 0) { value = -value; } if (value === 0) { exponent = 0; mantissa = 0; } else if (isNaN(value)) { exponent = 2 * eMax + 1; mantissa = 1; } else if (value === Infinity) { exponent = 2 * eMax + 1; mantissa = 0; } else { exponent = Math.floor(Math.log(value) / Math.LN2); if (exponent >= eMin && exponent <= eMax) { mantissa = Math.floor((value * pow2(-exponent) - 1) * pow2(mantSize)); exponent += eMax; } else { mantissa = Math.floor(value / pow2(eMin - mantSize)); exponent = 0; } } var b = []; while (mantSize >= 8) { b.push(mantissa % 256); mantissa = Math.floor(mantissa / 256); mantSize -= 8; } exponent = (exponent << mantSize) | mantissa; expSize += mantSize; while (expSize >= 8) { b.push(exponent & 0xff); exponent >>>= 8; expSize -= 8; } b.push((signBit << expSize) | exponent); this._setBytes(byteOffset, b, littleEndian); }, _setFloat32: function (byteOffset, value, littleEndian) { this._setBinaryFloat(byteOffset, value, 23, 8, littleEndian); }, _setFloat64: function (byteOffset, value, littleEndian) { this._setBinaryFloat(byteOffset, value, 52, 11, littleEndian); }, _set64: function (Type, byteOffset, value, littleEndian) { if (!(value instanceof Type)) { value = Type.fromNumber(value); } littleEndian = defined(littleEndian, this._littleEndian); byteOffset = defined(byteOffset, this._offset); var parts = littleEndian ? { lo: 0, hi: 4 } : { lo: 4, hi: 0 }; for (var partName in parts) { this.setUint32( byteOffset + parts[partName], value[partName], littleEndian ); } this._offset = byteOffset + 8; }, setInt64: function (byteOffset, value, littleEndian) { this._set64(Int64, byteOffset, value, littleEndian); }, setUint64: function (byteOffset, value, littleEndian) { this._set64(Uint64, byteOffset, value, littleEndian); }, _setUint32: function (byteOffset, value, littleEndian) { this._setBytes( byteOffset, [value & 0xff, (value >>> 8) & 0xff, (value >>> 16) & 0xff, value >>> 24], littleEndian ); }, _setUint16: function (byteOffset, value, littleEndian) { this._setBytes( byteOffset, [value & 0xff, (value >>> 8) & 0xff], littleEndian ); }, _setUint8: function (byteOffset, value) { this._setBytes(byteOffset, [value & 0xff]); }, setUnsigned: function (byteOffset, value, bitLength) { var data = this._getBitRangeData(bitLength, byteOffset), wideValue = data.wideValue, b = data.bytes; wideValue &= ~(~(-1 << bitLength) << -this._bitOffset); // clearing bit range before binary "or" wideValue |= (bitLength < 32 ? value & ~(-1 << bitLength) : value) << -this._bitOffset; // setting bits for (var i = b.length - 1; i >= 0; i--) { b[i] = wideValue & 0xff; wideValue >>>= 8; } this._setBytes(data.start, b, true); }, }; var proto = jDataView.prototype; for (var type in dataTypes) { (function (type) { proto["get" + type] = function (byteOffset, littleEndian) { return this._action(type, true, byteOffset, littleEndian); }; proto["set" + type] = function (byteOffset, value, littleEndian) { this._action(type, false, byteOffset, littleEndian, value); }; })(type); } proto._setInt32 = proto._setUint32; proto._setInt16 = proto._setUint16; proto._setInt8 = proto._setUint8; proto.setSigned = proto.setUnsigned; for (var method in proto) { if (method.slice(0, 3) === "set") { (function (type) { proto["write" + type] = function () { Array.prototype.unshift.call(arguments, undefined); this["set" + type].apply(this, arguments); }; })(method.slice(3)); } } if (typeof module !== "undefined" && typeof module.exports === "object") { module.exports = jDataView; } else if (typeof define === "function" && define.amd) { define([], function () { return jDataView; }); } else { var oldGlobalThis = globalThis.jDataView; (globalThis.jDataView = jDataView).noConflict = function () { globalThis.jDataView = oldGlobalThis; return this; }; }