diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e508d4..c7f6fc7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,10 @@ jobs: node-version: '20' registry-url: https://registry.npmjs.org/ - run: npm ci + - run: npm run build + - name: Check that KaitaiStream.js will be included + run: | + npm publish --tag next --dry-run --json | jq --exit-status '.files | map(.path) | any(. == "KaitaiStream.js")' - name: Publish to npm run: npm publish --tag next env: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..86df457 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/node_modules/ + +# Generated by Typescript compiler +/KaitaiStream.d.ts +/KaitaiStream.js diff --git a/KaitaiStream.js b/KaitaiStream.js deleted file mode 100644 index 473b065..0000000 --- a/KaitaiStream.js +++ /dev/null @@ -1,948 +0,0 @@ -// -*- mode: js; js-indent-level: 2; -*- -'use strict'; -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof module === 'object' && module.exports) { - module.exports = factory(); - } else { - root.KaitaiStream = factory(); - } -}(typeof self !== 'undefined' ? self : this, function () { - -/** - KaitaiStream is an implementation of Kaitai Struct API for JavaScript. - Based on DataStream - https://github.com/kig/DataStream.js - - @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. - @param {?Number} byteOffset Offset from arrayBuffer beginning for the KaitaiStream. - */ -var KaitaiStream = function(arrayBuffer, byteOffset) { - this._byteOffset = byteOffset || 0; - if (arrayBuffer instanceof ArrayBuffer) { - this.buffer = arrayBuffer; - } else if (typeof arrayBuffer == "object") { - this.dataView = arrayBuffer; - if (byteOffset) { - this._byteOffset += byteOffset; - } - } else { - this.buffer = new ArrayBuffer(arrayBuffer || 1); - } - this.pos = 0; - this.alignToByte(); -}; - - -KaitaiStream.prototype = {}; - -/** - Dependency configuration data. Holds urls for (optional) dynamic loading - of code dependencies from a remote server. For use by (static) processing functions. - - Caller should the supported keys to the asset urls as needed. - NOTE: `depUrls` is a static property of KaitaiStream (the factory),like the various - processing functions. It is NOT part of the prototype of instances. - @type {Object} - */ -KaitaiStream.depUrls = { - // processZlib uses this and expected a link to a copy of pako. - // specifically the pako_inflate.min.js script at: - // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js - zlib: undefined -}; - -/** - Virtual byte length of the KaitaiStream backing buffer. - Updated to be max of original buffer size and last written size. - If dynamicSize is false is set to buffer size. - @type {number} - */ -KaitaiStream.prototype._byteLength = 0; - -/** - Set/get the backing ArrayBuffer of the KaitaiStream object. - The setter updates the DataView to point to the new buffer. - @type {Object} - */ -Object.defineProperty(KaitaiStream.prototype, 'buffer', - { get: function() { - this._trimAlloc(); - return this._buffer; - }, - set: function(v) { - this._buffer = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the byteOffset of the KaitaiStream object. - The setter updates the DataView to point to the new byteOffset. - @type {number} - */ -Object.defineProperty(KaitaiStream.prototype, 'byteOffset', - { get: function() { - return this._byteOffset; - }, - set: function(v) { - this._byteOffset = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the backing DataView of the KaitaiStream object. - The setter updates the buffer and byteOffset to point to the DataView values. - @type {Object} - */ -Object.defineProperty(KaitaiStream.prototype, 'dataView', - { get: function() { - return this._dataView; - }, - set: function(v) { - this._byteOffset = v.byteOffset; - this._buffer = v.buffer; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._byteOffset + v.byteLength; - } }); - -/** - Internal function to trim the KaitaiStream buffer when required. - Used for stripping out the extra bytes from the backing buffer when - the virtual byteLength is smaller than the buffer byteLength (happens after - growing the buffer with writes and not filling the extra space completely). - - @return {null} - */ -KaitaiStream.prototype._trimAlloc = function() { - if (this._byteLength === this._buffer.byteLength) { - return; - } - var buf = new ArrayBuffer(this._byteLength); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, 0, dst.length); - dst.set(src); - this.buffer = buf; -}; - -// ======================================================================== -// Stream positioning -// ======================================================================== - -/** - Returns true if the KaitaiStream seek pointer is at the end of buffer and - there's no more data to read. - - @return {boolean} True if the seek pointer is at the end of the buffer. - */ -KaitaiStream.prototype.isEof = function() { - return this.pos >= this.size && this.bitsLeft === 0; -}; - -/** - Sets the KaitaiStream read/write position to given position. - Clamps between 0 and KaitaiStream length. - - @param {number} pos Position to seek to. - @return {null} - */ -KaitaiStream.prototype.seek = function(pos) { - var npos = Math.max(0, Math.min(this.size, pos)); - this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; -}; - -/** - Returns the byte length of the KaitaiStream object. - @type {number} - */ -Object.defineProperty(KaitaiStream.prototype, 'size', - { get: function() { - return this._byteLength - this._byteOffset; - }}); - -// ======================================================================== -// Integer numbers -// ======================================================================== - -// ------------------------------------------------------------------------ -// Signed -// ------------------------------------------------------------------------ - -/** - Reads an 8-bit signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS1 = function() { - this.ensureBytesLeft(1); - var v = this._dataView.getInt8(this.pos); - this.pos += 1; - return v; -}; - -// ........................................................................ -// Big-endian -// ........................................................................ - -/** - Reads a 16-bit big-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS2be = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getInt16(this.pos); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit big-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS4be = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getInt32(this.pos); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit big-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readS8be = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4be(); - var v2 = this.readU4be(); - - if ((v1 & 0x80000000) !== 0) { - // negative number - return -(0x100000000 * (v1 ^ 0xffffffff) + (v2 ^ 0xffffffff)) - 1; - } else { - return 0x100000000 * v1 + v2; - } -}; - -// ........................................................................ -// Little-endian -// ........................................................................ - -/** - Reads a 16-bit little-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS2le = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getInt16(this.pos, true); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit little-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS4le = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getInt32(this.pos, true); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit little-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readS8le = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4le(); - var v2 = this.readU4le(); - - if ((v2 & 0x80000000) !== 0) { - // negative number - return -(0x100000000 * (v2 ^ 0xffffffff) + (v1 ^ 0xffffffff)) - 1; - } else { - return 0x100000000 * v2 + v1; - } -}; - -// ------------------------------------------------------------------------ -// Unsigned -// ------------------------------------------------------------------------ - -/** - Reads an 8-bit unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU1 = function() { - this.ensureBytesLeft(1); - var v = this._dataView.getUint8(this.pos); - this.pos += 1; - return v; -}; - -// ........................................................................ -// Big-endian -// ........................................................................ - -/** - Reads a 16-bit big-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU2be = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getUint16(this.pos); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit big-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU4be = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getUint32(this.pos); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit big-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readU8be = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4be(); - var v2 = this.readU4be(); - return 0x100000000 * v1 + v2; -}; - -// ........................................................................ -// Little-endian -// ........................................................................ - -/** - Reads a 16-bit little-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU2le = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getUint16(this.pos, true); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit little-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU4le = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getUint32(this.pos, true); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit little-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readU8le = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4le(); - var v2 = this.readU4le(); - return 0x100000000 * v2 + v1; -}; - -// ======================================================================== -// Floating point numbers -// ======================================================================== - -// ------------------------------------------------------------------------ -// Big endian -// ------------------------------------------------------------------------ - -KaitaiStream.prototype.readF4be = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getFloat32(this.pos); - this.pos += 4; - return v; -}; - -KaitaiStream.prototype.readF8be = function() { - this.ensureBytesLeft(8); - var v = this._dataView.getFloat64(this.pos); - this.pos += 8; - return v; -}; - -// ------------------------------------------------------------------------ -// Little endian -// ------------------------------------------------------------------------ - -KaitaiStream.prototype.readF4le = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getFloat32(this.pos, true); - this.pos += 4; - return v; -}; - -KaitaiStream.prototype.readF8le = function() { - this.ensureBytesLeft(8); - var v = this._dataView.getFloat64(this.pos, true); - this.pos += 8; - return v; -}; - -// ------------------------------------------------------------------------ -// Unaligned bit values -// ------------------------------------------------------------------------ - -KaitaiStream.prototype.alignToByte = function() { - this.bitsLeft = 0; - this.bits = 0; -}; - -/* - bitsLeft = 3 - \ \ bitsNeeded = 10 -> bytesNeeded = 2 - \ \ / \ - |01101xxx|xxxxxxxx|xx......| - \ /\ \ - \__ n = 13 _/ \ \ - new bitsLeft = 6 - */ -KaitaiStream.prototype.readBitsIntBe = function(n) { - // JS only supports bit operations on 32 bits - if (n > 32) { - throw new RangeError("readBitsIntBe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); - } - var res = 0; - - var bitsNeeded = n - this.bitsLeft; - this.bitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8` - - if (bitsNeeded > 0) { - // 1 bit => 1 byte - // 8 bits => 1 byte - // 9 bits => 2 bytes - var bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { - res = res << 8 | buf[i]; - } - - var newBits = res; - res = res >>> this.bitsLeft | this.bits << bitsNeeded; // `x << 32` is defined as `x << 0` in JS, but only `0 << 32` - // can occur here (`n = 32` and `bitsLeft = 0`, this implies - // `bits = 0` unless changed externally) - this.bits = newBits; // will be masked at the end of the function - } else { - res = this.bits >>> -bitsNeeded; // shift unneeded bits out - } - - var mask = (1 << this.bitsLeft) - 1; // `bitsLeft` is in range 0..7, so `(1 << 32)` does not have to be considered - this.bits &= mask; - - // always return an unsigned 32-bit integer - return res >>> 0; -}; - -/** - * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions - * - * @deprecated use {@link readBitsIntBe} instead - */ -KaitaiStream.prototype.readBitsInt = KaitaiStream.prototype.readBitsIntBe; - -/* - n = 13 bitsNeeded = 10 - / \ - bitsLeft = 3 ______ __ - \ \ / \ \ \ - |xxx01101|xxxxxxxx|......xx| - \ / - new bitsLeft = 6 - - bitsLeft = 7 - \ \ - |01101100|..xxxxx1|........| - \___/ - n = 5 - */ -KaitaiStream.prototype.readBitsIntLe = function(n) { - // JS only supports bit operations on 32 bits - if (n > 32) { - throw new RangeError("readBitsIntLe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); - } - var res = 0; - var bitsNeeded = n - this.bitsLeft; - - if (bitsNeeded > 0) { - // 1 bit => 1 byte - // 8 bits => 1 byte - // 9 bits => 2 bytes - var bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { - res |= buf[i] << (i * 8); - } - - // NB: in JavaScript, bit shift operators always shift by modulo 32 of the right-hand operand (see - // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-unsignedRightShift), - // so `res >>> 32` is equivalent to `res >>> 0` (but we don't want that) - var newBits = bitsNeeded < 32 ? res >>> bitsNeeded : 0; - res = res << this.bitsLeft | this.bits; - this.bits = newBits; - } else { - res = this.bits; - this.bits >>>= n; - } - - this.bitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8` - - // always return an unsigned 32-bit integer - if (n < 32) { - var mask = (1 << n) - 1; - res &= mask; // this produces a signed 32-bit int, but the sign bit is cleared - } else { - res >>>= 0; - } - return res; -}; - -/** - Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN - depending on the platform endianness. - - @type {boolean} - */ -KaitaiStream.endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; - -// ======================================================================== -// Byte arrays -// ======================================================================== - -KaitaiStream.prototype.readBytes = function(len) { - return this.mapUint8Array(len); -}; - -KaitaiStream.prototype.readBytesFull = function() { - return this.mapUint8Array(this.size - this.pos); -}; - -KaitaiStream.prototype.readBytesTerm = function(terminator, include, consume, eosError) { - var blen = this.size - this.pos; - var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); - for (var i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte - if (i === blen) { - // we've read all the buffer and haven't found the terminator - if (eosError) { - throw "End of stream reached, but no terminator " + terminator + " found"; - } else { - return this.mapUint8Array(i); - } - } else { - var arr; - if (include) { - arr = this.mapUint8Array(i + 1); - } else { - arr = this.mapUint8Array(i); - } - if (consume) { - this.pos += 1; - } - return arr; - } -}; - -KaitaiStream.prototype.readBytesTermMulti = function(terminator, include, consume, eosError) { - var unitSize = terminator.length; - var data = new Uint8Array(this._buffer, this._byteOffset + this.pos, this.size - this.pos); - var res = KaitaiStream.bytesTerminateMulti(data, terminator, true); - this.pos += res.length; - var termFound = - res.length !== 0 && - res.length % unitSize === 0 && - KaitaiStream.byteArrayCompare(new Uint8Array(res.buffer, res.length - unitSize), terminator) === 0; - if (termFound) { - if (!include) { - res = new Uint8Array(res.buffer, res.byteOffset, res.length - unitSize); - } - if (!consume) { - this.pos -= unitSize; - } - } else if (eosError) { - throw new Error("End of stream reached, but no terminator " + terminator + " found"); - } - return res; -}; - -// Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions -KaitaiStream.prototype.ensureFixedContents = function(expected) { - var actual = this.readBytes(expected.length); - if (actual.length !== expected.length) { - throw new UnexpectedDataError(expected, actual); - } - var actLen = actual.length; - for (var i = 0; i < actLen; i++) { - if (actual[i] !== expected[i]) { - throw new UnexpectedDataError(expected, actual); - } - } - return actual; -}; - -KaitaiStream.bytesStripRight = function(data, padByte) { - var newLen = data.length; - while (data[newLen - 1] === padByte) { - newLen--; - } - return data.slice(0, newLen); -}; - -KaitaiStream.bytesTerminate = function(data, term, include) { - var newLen = 0; - var maxLen = data.length; - while (newLen < maxLen && data[newLen] !== term) { - newLen++; - } - if (include && newLen < maxLen) - newLen++; - return data.slice(0, newLen); -}; - -KaitaiStream.bytesTerminateMulti = function(data, term, include) { - var unitSize = term.length; - if (unitSize === 0) { - return new Uint8Array(); - } - var len = data.length; - var iTerm = 0; - for (var iData = 0; iData < len;) { - if (data[iData] !== term[iTerm]) { - iData += unitSize - iTerm; - iTerm = 0; - continue; - } - iData++; - iTerm++; - if (iTerm === unitSize) { - return data.slice(0, iData - (include ? 0 : unitSize)); - } - } - return data.slice(); -}; - -KaitaiStream.bytesToStr = function(arr, encoding) { - if (encoding == null || encoding.toLowerCase() === "ascii") { - return KaitaiStream.createStringFromArray(arr); - } else { - if (typeof TextDecoder === 'function') { - // we're in the browser that supports TextDecoder - return (new TextDecoder(encoding)).decode(arr); - } else { - // probably we're in node.js - - // check if it's supported natively by node.js Buffer - // see https://github.com/nodejs/node/blob/master/lib/buffer.js#L187 for details - switch (encoding.toLowerCase()) { - case 'utf8': - case 'utf-8': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return new Buffer(arr).toString(encoding); - break; - default: - // unsupported encoding, we'll have to resort to iconv-lite - if (typeof KaitaiStream.iconvlite === 'undefined') - KaitaiStream.iconvlite = require('iconv-lite'); - - return KaitaiStream.iconvlite.decode(arr, encoding); - } - } - } -}; - -// ======================================================================== -// Byte array processing -// ======================================================================== - -KaitaiStream.processXorOne = function(data, key) { - var r = new Uint8Array(data.length); - var dl = data.length; - for (var i = 0; i < dl; i++) - r[i] = data[i] ^ key; - return r; -}; - -KaitaiStream.processXorMany = function(data, key) { - var dl = data.length; - var r = new Uint8Array(dl); - var kl = key.length; - var ki = 0; - for (var i = 0; i < dl; i++) { - r[i] = data[i] ^ key[ki]; - ki++; - if (ki >= kl) - ki = 0; - } - return r; -}; - -KaitaiStream.processRotateLeft = function(data, amount, groupSize) { - if (groupSize !== 1) - throw("unable to rotate group of " + groupSize + " bytes yet"); - - var mask = groupSize * 8 - 1; - var antiAmount = -amount & mask; - - var r = new Uint8Array(data.length); - for (var i = 0; i < data.length; i++) - r[i] = (data[i] << amount) & 0xff | (data[i] >> antiAmount); - - return r; -}; - -KaitaiStream.processZlib = function(buf) { - if (typeof require !== 'undefined') { - // require is available - we're running under node - if (typeof KaitaiStream.zlib === 'undefined') - KaitaiStream.zlib = require('zlib'); - // use node's zlib module API - var r = KaitaiStream.zlib.inflateSync( - Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) - ); - return r; - } else { - // no require() - assume we're running as a web worker in browser. - // user should have configured KaitaiStream.depUrls.zlib, if not - // we'll throw. - if (typeof KaitaiStream.zlib === 'undefined' - && typeof KaitaiStream.depUrls.zlib !== 'undefined') { - importScripts(KaitaiStream.depUrls.zlib); - KaitaiStream.zlib = pako; - } - // use pako API - r = KaitaiStream.zlib.inflate(buf); - return r; - } -}; - -// ======================================================================== -// Misc runtime operations -// ======================================================================== - -KaitaiStream.mod = function(a, b) { - if (b <= 0) - throw "mod divisor <= 0"; - var r = a % b; - if (r < 0) - r += b; - return r; -}; - -KaitaiStream.arrayMin = function(arr) { - var min = arr[0]; - var x; - for (var i = 1, n = arr.length; i < n; ++i) { - x = arr[i]; - if (x < min) min = x; - } - return min; -}; - -KaitaiStream.arrayMax = function(arr) { - var max = arr[0]; - var x; - for (var i = 1, n = arr.length; i < n; ++i) { - x = arr[i]; - if (x > max) max = x; - } - return max; -}; - -KaitaiStream.byteArrayCompare = function(a, b) { - if (a === b) - return 0; - var al = a.length; - var bl = b.length; - var minLen = al < bl ? al : bl; - for (var i = 0; i < minLen; i++) { - var cmp = a[i] - b[i]; - if (cmp !== 0) - return cmp; - } - - // Reached the end of at least one of the arrays - if (al === bl) { - return 0; - } else { - return al - bl; - } -}; - -// ======================================================================== -// Internal implementation details -// ======================================================================== - -var EOFError = KaitaiStream.EOFError = function(bytesReq, bytesAvail) { - this.name = "EOFError"; - this.message = "requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"; - this.bytesReq = bytesReq; - this.bytesAvail = bytesAvail; - this.stack = (new Error()).stack; -}; - -EOFError.prototype = Object.create(Error.prototype); -EOFError.prototype.constructor = EOFError; - -// Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions -var UnexpectedDataError = KaitaiStream.UnexpectedDataError = function(expected, actual) { - this.name = "UnexpectedDataError"; - this.message = "expected [" + expected + "], but got [" + actual + "]"; - this.expected = expected; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -UnexpectedDataError.prototype = Object.create(Error.prototype); -UnexpectedDataError.prototype.constructor = UnexpectedDataError; - -var UndecidedEndiannessError = KaitaiStream.UndecidedEndiannessError = function() { - this.name = "UndecidedEndiannessError"; - this.stack = (new Error()).stack; -}; - -UndecidedEndiannessError.prototype = Object.create(Error.prototype); -UndecidedEndiannessError.prototype.constructor = UndecidedEndiannessError; - -var ValidationNotEqualError = KaitaiStream.ValidationNotEqualError = function(expected, actual) { - this.name = "ValidationNotEqualError"; - this.message = "not equal, expected [" + expected + "], but got [" + actual + "]"; - this.expected = expected; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationNotEqualError.prototype = Object.create(Error.prototype); -ValidationNotEqualError.prototype.constructor = ValidationNotEqualError; - -var ValidationLessThanError = KaitaiStream.ValidationLessThanError = function(min, actual) { - this.name = "ValidationLessThanError"; - this.message = "not in range, min [" + min + "], but got [" + actual + "]"; - this.min = min; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationLessThanError.prototype = Object.create(Error.prototype); -ValidationLessThanError.prototype.constructor = ValidationLessThanError; - -var ValidationGreaterThanError = KaitaiStream.ValidationGreaterThanError = function(max, actual) { - this.name = "ValidationGreaterThanError"; - this.message = "not in range, max [" + max + "], but got [" + actual + "]"; - this.max = max; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationGreaterThanError.prototype = Object.create(Error.prototype); -ValidationGreaterThanError.prototype.constructor = ValidationGreaterThanError; - -var ValidationNotAnyOfError = KaitaiStream.ValidationNotAnyOfError = function(actual, io, srcPath) { - this.name = "ValidationNotAnyOfError"; - this.message = "not any of the list, got [" + actual + "]"; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationNotAnyOfError.prototype = Object.create(Error.prototype); -ValidationNotAnyOfError.prototype.constructor = ValidationNotAnyOfError; - -var ValidationNotInEnumError = KaitaiStream.ValidationNotInEnumError = function(actual, io, srcPath) { - this.name = "ValidationNotInEnumError"; - this.message = "not in the enum, got [" + actual + "]"; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationNotInEnumError.prototype = Object.create(Error.prototype); -ValidationNotInEnumError.prototype.constructor = ValidationNotInEnumError; - -var ValidationExprError = KaitaiStream.ValidationExprError = function(actual, io, srcPath) { - this.name = "ValidationExprError"; - this.message = "not matching the expression, got [" + actual + "]"; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationExprError.prototype = Object.create(Error.prototype); -ValidationExprError.prototype.constructor = ValidationExprError; - -/** - Ensures that we have an least `length` bytes left in the stream. - If that's not true, throws an EOFError. - - @param {number} length Number of bytes to require - */ -KaitaiStream.prototype.ensureBytesLeft = function(length) { - if (this.pos + length > this.size) { - throw new EOFError(length, this.size - this.pos); - } -}; - -/** - Maps a Uint8Array into the KaitaiStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @return {Object} Uint8Array to the KaitaiStream backing buffer. - */ -KaitaiStream.prototype.mapUint8Array = function(length) { - length |= 0; - - this.ensureBytesLeft(length); - - var arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); - this.pos += length; - return arr; -}; - -/** - Creates an array from an array of character codes. - Uses String.fromCharCode in chunks for memory efficiency and then concatenates - the resulting string chunks. - - @param {array|Uint8Array} array Array of character codes. - @return {string} String created from the character codes. -**/ -KaitaiStream.createStringFromArray = function(array) { - var chunk_size = 0x8000; - var chunks = []; - var useSubarray = typeof array.subarray === 'function'; - for (var i=0; i < array.length; i += chunk_size) { - chunks.push(String.fromCharCode.apply(null, useSubarray ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size))); - } - return chunks.join(""); -}; - -return KaitaiStream; - -})); diff --git a/KaitaiStream.ts b/KaitaiStream.ts new file mode 100644 index 0000000..a53a11e --- /dev/null +++ b/KaitaiStream.ts @@ -0,0 +1,1154 @@ +// -*- mode: js; js-indent-level: 2; -*- + +// Interfaces for optional dependencies +interface IconvLite { + decode(buffer: Buffer | Uint8Array, encoding: string): string +} + +interface Zlib { + inflateSync(buf: ArrayBuffer | NodeJS.ArrayBufferView): Buffer; +} + +interface Pako { + inflate(data: Uint8Array | number[]): Uint8Array; +} + +// Workaround for https://github.com/microsoft/TypeScript/issues/36470 +declare global { + interface CallableFunction { + apply(this: (this: T, ...args: A[]) => R, thisArg: T, args: ArrayLike): R; + } +} + +// When loaded into a web worker, pako gets added to the global scope +declare const pako: Pako; + +/** + * KaitaiStream is an implementation of Kaitai Struct API for JavaScript. + * Based on DataStream - https://github.com/kig/DataStream.js . + */ +class KaitaiStream { + /** + * @param arrayBuffer ArrayBuffer to read from. + * @param byteOffset Offset from arrayBuffer beginning for the KaitaiStream. + */ + public constructor(arrayBuffer: ArrayBuffer | DataView | number, byteOffset?: number) { + this._byteOffset = byteOffset || 0; + if (arrayBuffer instanceof ArrayBuffer) { + this.buffer = arrayBuffer; + } else if (typeof arrayBuffer == "object") { + this.dataView = arrayBuffer; + if (byteOffset) { + this._byteOffset += byteOffset; + } + } else { + this.buffer = new ArrayBuffer(arrayBuffer || 1); + } + this.pos = 0; + this.alignToByte(); + } + + /** + * Virtual byte length of the KaitaiStream backing buffer. + * Updated to be max of original buffer size and last written size. + * If dynamicSize is false is set to buffer size. + */ + private _byteLength = 0; + private _byteOffset = 0; + private _buffer!: ArrayBuffer; + private _dataView!: DataView; + + public pos: number; + public bits = 0; + public bitsLeft = 0; + + /** + * Dependency configuration data. Holds urls for (optional) dynamic loading + * of code dependencies from a remote server. For use by (static) processing functions. + * + * Caller should the supported keys to the asset urls as needed. + * NOTE: `depUrls` is a static property of KaitaiStream (the factory), like the various + * processing functions. It is NOT part of the prototype of instances. + */ + public static depUrls: Record = { + // processZlib uses this and expected a link to a copy of pako. + // specifically the pako_inflate.min.js script at: + // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js + zlib: undefined + }; + + public static iconvlite?: IconvLite; + public static zlib?: Pako | Zlib; + + /** + * Gets the backing ArrayBuffer of the KaitaiStream object. + * + * @returns The backing ArrayBuffer. + */ + public get buffer(): ArrayBuffer { + this._trimAlloc(); + return this._buffer; + } + + /** + * Sets the backing ArrayBuffer of the KaitaiStream object and updates the + * DataView to point to the new buffer. + */ + public set buffer(v: ArrayBuffer) { + this._buffer = v; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._buffer.byteLength; + } + + /** + * Gets the byteOffset of the KaitaiStream object. + * + * @returns The byteOffset. + */ + public get byteOffset(): number { + return this._byteOffset; + } + + /** + * Sets the byteOffset of the KaitaiStream object and updates the DataView to + * point to the new byteOffset. + */ + public set byteOffset(v: number) { + this._byteOffset = v; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._buffer.byteLength; + } + + /** + * Gets the backing DataView of the KaitaiStream object. + * + * @returns The backing DataView. + */ + public get dataView(): DataView { + return this._dataView; + } + /** + * Sets the backing DataView of the KaitaiStream object and updates the buffer + * and byteOffset to point to the DataView values. + */ + public set dataView(v: DataView) { + this._byteOffset = v.byteOffset; + this._buffer = v.buffer; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._byteOffset + v.byteLength; + } + + /** + * Internal function to trim the KaitaiStream buffer when required. + * Used for stripping out the extra bytes from the backing buffer when + * the virtual byteLength is smaller than the buffer byteLength (happens after + * growing the buffer with writes and not filling the extra space completely). + */ + private _trimAlloc(): void { + if (this._byteLength === this._buffer.byteLength) { + return; + } + const buf = new ArrayBuffer(this._byteLength); + const dst = new Uint8Array(buf); + const src = new Uint8Array(this._buffer, 0, dst.length); + dst.set(src); + this.buffer = buf; + } + + // ======================================================================== + // Stream positioning + // ======================================================================== + + /** + * Returns true if the KaitaiStream seek pointer is at the end of buffer and + * there's no more data to read. + * + * @returns True if the seek pointer is at the end of the buffer. + */ + public isEof(): boolean { + return this.pos >= this.size && this.bitsLeft === 0; + } + + /** + * Sets the KaitaiStream read/write position to given position. + * Clamps between 0 and KaitaiStream length. + * + * @param pos Position to seek to. + */ + public seek(pos: number): void { + const npos = Math.max(0, Math.min(this.size, pos)); + this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; + } + + /** + * Returns the byte length of the KaitaiStream object. + * + * @returns The byte length. + */ + public get size(): number { + return this._byteLength - this._byteOffset; + } + + // ======================================================================== + // Integer numbers + // ======================================================================== + + // ------------------------------------------------------------------------ + // Signed + // ------------------------------------------------------------------------ + + /** + * Reads an 8-bit signed int from the stream. + * + * @returns The read number. + */ + public readS1(): number { + this.ensureBytesLeft(1); + const v = this._dataView.getInt8(this.pos); + this.pos += 1; + return v; + } + + // ........................................................................ + // Big-endian + // ........................................................................ + + /** + * Reads a 16-bit big-endian signed int from the stream. + * + * @returns The read number. + */ + public readS2be(): number { + this.ensureBytesLeft(2); + const v = this._dataView.getInt16(this.pos); + this.pos += 2; + return v; + } + + /** + * Reads a 32-bit big-endian signed int from the stream. + * + * @returns The read number. + */ + public readS4be(): number { + this.ensureBytesLeft(4); + const v = this._dataView.getInt32(this.pos); + this.pos += 4; + return v; + } + + /** + * Reads a 64-bit big-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ + public readS8be(): number { + this.ensureBytesLeft(8); + const v1 = this.readU4be(); + const v2 = this.readU4be(); + + if ((v1 & 0x80000000) !== 0) { + // negative number + return -(0x100000000 * (v1 ^ 0xffffffff) + (v2 ^ 0xffffffff)) - 1; + } else { + return 0x100000000 * v1 + v2; + } + } + + // ........................................................................ + // Little-endian + // ........................................................................ + + /** + * Reads a 16-bit little-endian signed int from the stream. + * + * @returns The read number. + */ + public readS2le(): number { + this.ensureBytesLeft(2); + const v = this._dataView.getInt16(this.pos, true); + this.pos += 2; + return v; + } + + /** + * Reads a 32-bit little-endian signed int from the stream. + * + * @returns The read number. + */ + public readS4le(): number { + this.ensureBytesLeft(4); + const v = this._dataView.getInt32(this.pos, true); + this.pos += 4; + return v; + } + + /** + * Reads a 64-bit little-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ + public readS8le(): number { + this.ensureBytesLeft(8); + const v1 = this.readU4le(); + const v2 = this.readU4le(); + + if ((v2 & 0x80000000) !== 0) { + // negative number + return -(0x100000000 * (v2 ^ 0xffffffff) + (v1 ^ 0xffffffff)) - 1; + } else { + return 0x100000000 * v2 + v1; + } + } + + // ------------------------------------------------------------------------ + // Unsigned + // ------------------------------------------------------------------------ + + /** + * Reads an 8-bit unsigned int from the stream. + * + * @returns The read number. + */ + public readU1(): number { + this.ensureBytesLeft(1); + const v = this._dataView.getUint8(this.pos); + this.pos += 1; + return v; + } + + // ........................................................................ + // Big-endian + // ........................................................................ + + /** + * Reads a 16-bit big-endian unsigned int from the stream. + * + * @returns The read number. + */ + public readU2be(): number { + this.ensureBytesLeft(2); + const v = this._dataView.getUint16(this.pos); + this.pos += 2; + return v; + } + + /** + * Reads a 32-bit big-endian unsigned int from the stream. + * + * @returns The read number. + */ + public readU4be(): number { + this.ensureBytesLeft(4); + const v = this._dataView.getUint32(this.pos); + this.pos += 4; + return v; + } + + /** + * Reads a 64-bit big-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ + public readU8be(): number { + this.ensureBytesLeft(8); + const v1 = this.readU4be(); + const v2 = this.readU4be(); + return 0x100000000 * v1 + v2; + } + + // ........................................................................ + // Little-endian + // ........................................................................ + + /** + * Reads a 16-bit little-endian unsigned int from the stream. + * + * @returns The read number. + */ + public readU2le(): number { + this.ensureBytesLeft(2); + const v = this._dataView.getUint16(this.pos, true); + this.pos += 2; + return v; + } + + /** + * Reads a 32-bit little-endian unsigned int from the stream. + * + * @returns The read number. + */ + public readU4le(): number { + this.ensureBytesLeft(4); + const v = this._dataView.getUint32(this.pos, true); + this.pos += 4; + return v; + } + + /** + * Reads a 64-bit little-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ + public readU8le(): number { + this.ensureBytesLeft(8); + const v1 = this.readU4le(); + const v2 = this.readU4le(); + return 0x100000000 * v2 + v1; + } + + // ======================================================================== + // Floating point numbers + // ======================================================================== + + // ------------------------------------------------------------------------ + // Big endian + // ------------------------------------------------------------------------ + + /** + * Reads a 32-bit big-endian float from the stream. + * + * @returns The read number. + */ + public readF4be(): number { + this.ensureBytesLeft(4); + const v = this._dataView.getFloat32(this.pos); + this.pos += 4; + return v; + } + + /** + * Reads a 64-bit big-endian float from the stream. + * + * @returns The read number. + */ + public readF8be(): number { + this.ensureBytesLeft(8); + const v = this._dataView.getFloat64(this.pos); + this.pos += 8; + return v; + } + + // ------------------------------------------------------------------------ + // Little endian + // ------------------------------------------------------------------------ + + /** + * Reads a 32-bit little-endian float from the stream. + * + * @returns The read number. + */ + public readF4le(): number { + this.ensureBytesLeft(4); + const v = this._dataView.getFloat32(this.pos, true); + this.pos += 4; + return v; + } + + /** + * Reads a 64-bit little-endian float from the stream. + * + * @returns The read number. + */ + public readF8le(): number { + this.ensureBytesLeft(8); + const v = this._dataView.getFloat64(this.pos, true); + this.pos += 8; + return v; + } + + // ------------------------------------------------------------------------ + // Unaligned bit values + // ------------------------------------------------------------------------ + + /** + * Aligns the stream position to the next byte boundary. + */ + public alignToByte(): void { + this.bitsLeft = 0; + this.bits = 0; + } + + /** + * @param n The number of bits to read. + * @returns The read bits. + * @throws {RangeError} + */ + public readBitsIntBe(n: number): number { + // JS only supports bit operations on 32 bits + if (n > 32) { + throw new RangeError("readBitsIntBe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); + } + let res = 0; + + const bitsNeeded = n - this.bitsLeft; + this.bitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8` + + if (bitsNeeded > 0) { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + const bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) + const buf = this.readBytes(bytesNeeded); + for (let i = 0; i < bytesNeeded; i++) { + res = res << 8 | buf[i]; + } + + const newBits = res; + res = res >>> this.bitsLeft | this.bits << bitsNeeded; // `x << 32` is defined as `x << 0` in JS, but only `0 << 32` + // can occur here (`n = 32` and `bitsLeft = 0`, this implies + // `bits = 0` unless changed externally) + this.bits = newBits; // will be masked at the end of the function + } else { + res = this.bits >>> -bitsNeeded; // shift unneeded bits out + } + + const mask = (1 << this.bitsLeft) - 1; // `bitsLeft` is in range 0..7, so `(1 << 32)` does not have to be considered + this.bits &= mask; + + // always return an unsigned 32-bit integer + return res >>> 0; + } + + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. + * + * @deprecated Use {@link readBitsIntBe} instead. + * @param n The number of bits to read. + * @returns The read bits. + */ + public readBitsInt(n: number): number { + return this.readBitsIntBe(n); + } + + /** + * @param n The number of bits to read. + * @returns The read bits. + * @throws {RangeError} + */ + public readBitsIntLe(n: number): number { + // JS only supports bit operations on 32 bits + if (n > 32) { + throw new RangeError("readBitsIntLe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); + } + let res = 0; + const bitsNeeded = n - this.bitsLeft; + + if (bitsNeeded > 0) { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + const bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) + const buf = this.readBytes(bytesNeeded); + for (let i = 0; i < bytesNeeded; i++) { + res |= buf[i] << (i * 8); + } + + // NB: in JavaScript, bit shift operators always shift by modulo 32 of the right-hand operand (see + // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-unsignedRightShift), + // so `res >>> 32` is equivalent to `res >>> 0` (but we don't want that) + const newBits = bitsNeeded < 32 ? res >>> bitsNeeded : 0; + res = res << this.bitsLeft | this.bits; + this.bits = newBits; + } else { + res = this.bits; + this.bits >>>= n; + } + + this.bitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8` + + // always return an unsigned 32-bit integer + if (n < 32) { + const mask = (1 << n) - 1; + res &= mask; // this produces a signed 32-bit int, but the sign bit is cleared + } else { + res >>>= 0; + } + return res; + } + + /** + * Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN + * depending on the platform endianness. + */ + public static endianness: boolean = new Int8Array(new Int16Array([1]).buffer)[0] > 0; + + // ======================================================================== + // Byte arrays + // ======================================================================== + + /** + * @param len The number of bytes to read. + * @returns The read bytes. + */ + public readBytes(len: number): Uint8Array { + return this.mapUint8Array(len); + } + + /** + * @returns The read bytes. + */ + public readBytesFull(): Uint8Array { + return this.mapUint8Array(this.size - this.pos); + } + + /** + * Reads bytes until the terminator byte is found. + * + * @param terminator The terminator byte. + * @param include True if the terminator should be included with the returned bytes. + * @param consume True if the terminator should be consumed from the input stream. + * @param eosError True to throw an error if the end of stream is reached. + * @returns The read bytes. + * @throws {string} + */ + public readBytesTerm(terminator: number, include: boolean, consume: boolean, eosError: boolean): Uint8Array { + const blen = this.size - this.pos; + const u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); + let i; + for (i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte + if (i === blen) { + // we've read all the buffer and haven't found the terminator + if (eosError) { + throw "End of stream reached, but no terminator " + terminator + " found"; + } else { + return this.mapUint8Array(i); + } + } else { + let arr; + if (include) { + arr = this.mapUint8Array(i + 1); + } else { + arr = this.mapUint8Array(i); + } + if (consume) { + this.pos += 1; + } + return arr; + } + } + + /** + * Reads bytes until the terminator byte sequence is found. + * + * @param terminator The terminator byte sequence. + * @param include True if the terminator should be included with the returned bytes. + * @param consume True if the terminator should be consumed from the input stream. + * @param eosError True to throw an error if the end of stream is reached. + * @returns The read bytes. + * @throws {string} + */ + public readBytesTermMulti(terminator: Uint8Array, include: boolean, consume: boolean, eosError: boolean): Uint8Array { + const unitSize = terminator.length; + const data = new Uint8Array(this._buffer, this._byteOffset + this.pos, this.size - this.pos); + let res = KaitaiStream.bytesTerminateMulti(data, terminator, true); + this.pos += res.length; + const termFound = + res.length !== 0 && + res.length % unitSize === 0 && + KaitaiStream.byteArrayCompare(new Uint8Array(res.buffer, res.length - unitSize), terminator) === 0; + if (termFound) { + if (!include) { + res = new Uint8Array(res.buffer, res.byteOffset, res.length - unitSize); + } + if (!consume) { + this.pos -= unitSize; + } + } else if (eosError) { + throw new Error("End of stream reached, but no terminator " + terminator + " found"); + } + return res; + } + + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. + * + * @param expected The expected bytes. + * @returns The read bytes. + * @throws {KaitaiStream.UnexpectedDataError} + */ + public ensureFixedContents(expected: ArrayLike): Uint8Array { + const actual = this.readBytes(expected.length); + if (actual.length !== expected.length) { + throw new KaitaiStream.UnexpectedDataError(expected, actual); + } + const actLen = actual.length; + for (let i = 0; i < actLen; i++) { + if (actual[i] !== expected[i]) { + throw new KaitaiStream.UnexpectedDataError(expected, actual); + } + } + return actual; + } + + /** + * @param data The data. + * @param padByte The byte to strip. + * @returns The stripped data. + */ + public static bytesStripRight(data: Uint8Array, padByte: number): Uint8Array { + let newLen = data.length; + while (data[newLen - 1] === padByte) { + newLen--; + } + return data.slice(0, newLen); + } + + /** + * @param data The data. + * @param term The terminator. + * @param include True if the returned bytes should include the terminator. + * @returns The terminated bytes. + */ + public static bytesTerminate(data: Uint8Array, term: number, include: boolean): Uint8Array { + let newLen = 0; + const maxLen = data.length; + while (newLen < maxLen && data[newLen] !== term) { + newLen++; + } + if (include && newLen < maxLen) + newLen++; + return data.slice(0, newLen); + } + + /** + * @param data The data. + * @param term The terminator. + * @param include True if the returned bytes should include the terminator. + * @returns The terminated bytes. + */ + public static bytesTerminateMulti(data: Uint8Array, term: Uint8Array, include: boolean): Uint8Array { + const unitSize = term.length; + if (unitSize === 0) { + return new Uint8Array(); + } + const len = data.length; + let iTerm = 0; + for (let iData = 0; iData < len;) { + if (data[iData] !== term[iTerm]) { + iData += unitSize - iTerm; + iTerm = 0; + continue; + } + iData++; + iTerm++; + if (iTerm === unitSize) { + return data.slice(0, iData - (include ? 0 : unitSize)); + } + } + return data.slice(); + } + + /** + * @param arr The bytes. + * @param encoding The character encoding. + * @returns The decoded string. + */ + public static bytesToStr(arr: Uint8Array, encoding: string): string { + if (encoding == null || encoding.toLowerCase() === "ascii") { + return KaitaiStream.createStringFromArray(arr); + } else { + if (typeof TextDecoder === 'function') { + // we're in a browser that supports TextDecoder, or in Node.js 11 or later + return (new TextDecoder(encoding)).decode(arr); + } else { + // probably we're in Node.js < 11 + + // check if it's supported natively by Node.js Buffer + // see https://nodejs.org/docs/latest-v10.x/api/buffer.html#buffer_buffers_and_character_encodings + switch (encoding.toLowerCase()) { + case 'utf8': + case 'utf-8': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return Buffer.from(arr).toString(encoding as BufferEncoding); + default: + // unsupported encoding, we'll have to resort to iconv-lite + if (typeof KaitaiStream.iconvlite === 'undefined') + KaitaiStream.iconvlite = require('iconv-lite') as IconvLite; + + return KaitaiStream.iconvlite.decode(arr, encoding); + } + } + } + } + + // ======================================================================== + // Byte array processing + // ======================================================================== + + /** + * @param data The input bytes. + * @param key The key byte. + * @returns The Xor'd bytes. + */ + public static processXorOne(data: Uint8Array, key: number): Uint8Array { + const r = new Uint8Array(data.length); + const dl = data.length; + for (let i = 0; i < dl; i++) + r[i] = data[i] ^ key; + return r; + } + + /** + * @param data The input bytes. + * @param key The key bytes. + * @returns The Xor'd bytes. + */ + public static processXorMany(data: Uint8Array, key: Uint8Array): Uint8Array { + const dl = data.length; + const r = new Uint8Array(dl); + const kl = key.length; + let ki = 0; + for (let i = 0; i < dl; i++) { + r[i] = data[i] ^ key[ki]; + ki++; + if (ki >= kl) + ki = 0; + } + return r; + } + + /** + * @param data The input bytes. + * @param amount The shift amount in bits. + * @param groupSize The number of bytes in each group. + * @returns The rotated bytes. + * @throws {string} + */ + public static processRotateLeft(data: Uint8Array, amount: number, groupSize: number): Uint8Array { + if (groupSize !== 1) + throw ("unable to rotate group of " + groupSize + " bytes yet"); + + const mask = groupSize * 8 - 1; + const antiAmount = -amount & mask; + + const r = new Uint8Array(data.length); + for (let i = 0; i < data.length; i++) + r[i] = (data[i] << amount) & 0xff | (data[i] >> antiAmount); + + return r; + } + + /** + * @param buf The input bytes. + * @returns The uncompressed bytes. + */ + public static processZlib(buf: Uint8Array): Uint8Array { + if (typeof require !== 'undefined') { + // require is available - we're running under node + if (typeof KaitaiStream.zlib === 'undefined') + KaitaiStream.zlib = require('zlib') as Zlib; + // use node's zlib module API + const r = (KaitaiStream.zlib as Zlib).inflateSync( + Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) + ); + return new Uint8Array(r.buffer, r.byteOffset, r.length); + } else { + // no require() - assume we're running as a web worker in browser. + // user should have configured KaitaiStream.depUrls.zlib, if not + // we'll throw. + if (typeof KaitaiStream.zlib === 'undefined' + && typeof KaitaiStream.depUrls.zlib !== 'undefined') { + importScripts(KaitaiStream.depUrls.zlib); + KaitaiStream.zlib = pako; + } + // use pako API + return (KaitaiStream.zlib as Pako).inflate(buf); + } + } + + // ======================================================================== + // Misc runtime operations + // ======================================================================== + + /** + * @param a The dividend. + * @param b The divisor. + * @returns The result of `a` mod `b`. + * @throws {string} + */ + public static mod(a: number, b: number): number { + if (b <= 0) + throw "mod divisor <= 0"; + let r = a % b; + if (r < 0) + r += b; + return r; + } + + /** + * Gets the smallest value in an array. + * + * @param arr The input array. + * @returns The smallest value. + */ + public static arrayMin(arr: ArrayLike): number { + let min = arr[0]; + let x; + for (let i = 1, n = arr.length; i < n; ++i) { + x = arr[i]; + if (x < min) min = x; + } + return min; + } + + /** + * Gets the largest value in an array. + * + * @param arr The input array. + * @returns The largest value. + */ + public static arrayMax(arr: ArrayLike): number { + let max = arr[0]; + let x; + for (let i = 1, n = arr.length; i < n; ++i) { + x = arr[i]; + if (x > max) max = x; + } + return max; + } + + /** + * Compares two arrays of bytes from left to right. + * + * @param a The first array. + * @param b The second array. + * @returns `0` if the arrays are the equal, a positive number if `a` is greater than `b`, or a negative number if `a` is less than `b`. + */ + public static byteArrayCompare(a: Uint8Array, b: Uint8Array): number { + if (a === b) + return 0; + const al = a.length; + const bl = b.length; + const minLen = al < bl ? al : bl; + for (let i = 0; i < minLen; i++) { + const cmp = a[i] - b[i]; + if (cmp !== 0) + return cmp; + } + + // Reached the end of at least one of the arrays + if (al === bl) { + return 0; + } else { + return al - bl; + } + } + + // ======================================================================== + // Internal implementation details + // ======================================================================== + + public static EOFError = class EOFError extends Error { + public name = "EOFError"; + public bytesReq: number; + public bytesAvail: number; + + /** + * @param bytesReq The number of bytes requested. + * @param bytesAvail The number of bytes available. + */ + public constructor(bytesReq: number, bytesAvail: number) { + super("requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.EOFError.prototype); + this.bytesReq = bytesReq; + this.bytesAvail = bytesAvail; + } + }; + + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. + */ + public static UnexpectedDataError = class UnexpectedDataError extends Error { + public name = "UnexpectedDataError"; + public expected: any; + public actual: any; + + /** + * @param expected The expected value. + * @param actual The actual value. + */ + public constructor(expected: any, actual: any) { + super("expected [" + expected + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.UnexpectedDataError.prototype); + this.expected = expected; + this.actual = actual; + } + }; + + public static UndecidedEndiannessError = class UndecidedEndiannessError extends Error { + public name = "UndecidedEndiannessError"; + + public constructor() { + super(); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.UndecidedEndiannessError.prototype); + } + }; + + public static ValidationNotEqualError = class ValidationNotEqualError extends Error { + public name = "ValidationNotEqualError"; + public expected: any; + public actual: any; + + /** + * @param expected The expected value. + * @param actual The actual value. + */ + public constructor(expected: any, actual: any) { + super("not equal, expected [" + expected + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.ValidationNotEqualError.prototype); + this.expected = expected; + this.actual = actual; + } + }; + + public static ValidationLessThanError = class ValidationLessThanError extends Error { + public name = "ValidationLessThanError"; + public min: any; + public actual: any; + + /** + * @param min The minimum allowed value. + * @param actual The actual value. + */ + public constructor(min: any, actual: any) { + super("not in range, min [" + min + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.ValidationLessThanError.prototype); + this.min = min; + this.actual = actual; + } + }; + + public static ValidationGreaterThanError = class ValidationGreaterThanError extends Error { + public name = "ValidationGreaterThanError"; + public max: any; + public actual: any; + + /** + * @param max The maximum allowed value. + * @param actual The actual value. + */ + public constructor(max: any, actual: any) { + super("not in range, max [" + max + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.ValidationGreaterThanError.prototype); + this.max = max; + this.actual = actual; + } + }; + + public static ValidationNotAnyOfError = class ValidationNotAnyOfError extends Error { + public name = "ValidationNotAnyOfError"; + public actual: any; + + /** + * @param actual The actual value. + */ + public constructor(actual: any) { + super("not any of the list, got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.ValidationNotAnyOfError.prototype); + this.actual = actual; + } + }; + + public static ValidationNotInEnumError = class ValidationNotInEnumError extends Error { + public name = "ValidationNotInEnumError"; + public actual: any; + + /** + * @param actual The actual value. + */ + public constructor(actual: any) { + super("not in the enum, got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.ValidationNotInEnumError.prototype); + this.actual = actual; + } + }; + + public static ValidationExprError = class ValidationExprError extends Error { + public name = "ValidationExprError"; + public actual: any; + + /** + * @param actual The actual value. + */ + public constructor(actual: any) { + super("not matching the expression, got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types + Object.setPrototypeOf(this, KaitaiStream.ValidationExprError.prototype); + this.actual = actual; + } + }; + + /** + * Ensures that we have at least `length` bytes left in the stream. + * If not, throws an EOFError. + * + * @param length Number of bytes to require. + * @throws {KaitaiStream.EOFError} + */ + public ensureBytesLeft(length: number): void { + if (this.pos + length > this.size) { + throw new KaitaiStream.EOFError(length, this.size - this.pos); + } + } + + /** + * Maps a Uint8Array into the KaitaiStream buffer. + * Nice for quickly reading in data. + * + * @param length Number of elements to map. + * @returns A Uint8Array to the KaitaiStream backing buffer. + */ + public mapUint8Array(length: number): Uint8Array { + length |= 0; + + this.ensureBytesLeft(length); + + const arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); + this.pos += length; + return arr; + } + + /** + * Creates an array from an array of character codes. + * Uses String.fromCharCode in chunks for memory efficiency and then concatenates + * the resulting string chunks. + * + * @param array Array of character codes. + * @returns String created from the character codes. + */ + public static createStringFromArray(array: Uint8Array): string { + const chunk_size = 0x8000; + const chunks = []; + for (let i = 0; i < array.length; i += chunk_size) { + const chunk = array.subarray(i, i + chunk_size); + chunks.push(String.fromCharCode.apply(null, chunk)); + } + return chunks.join(""); + } +} + +export default KaitaiStream; diff --git a/README.md b/README.md index d3a2afe..0141140 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,20 @@ Further reading: * [About API implemented in this library](http://doc.kaitai.io/stream_api.html) * [JavaScript-specific notes](http://doc.kaitai.io/lang_javascript.html) - also includes Quick start guide +## Development + +After cloning this repository, you must run these commands to compile the +TypeScript source file [`KaitaiStream.ts`](./KaitaiStream.ts) into the +`KaitaiStream.js` file (ignored by Git) with JavaScript code: + +```shell +npm install +npm run build +``` + +`npm run build` also needs to be run after each change to +[`KaitaiStream.ts`](./KaitaiStream.ts). + ## Licensing Copyright 2012-2016 Ilmari Heikkinen\ diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..a31ec9a --- /dev/null +++ b/index.d.ts @@ -0,0 +1,5 @@ +import KaitaiStream = require("./KaitaiStream"); +declare const _default: { + KaitaiStream: typeof KaitaiStream; +}; +export = _default; diff --git a/package-lock.json b/package-lock.json index 87761dd..b02e9bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,219 @@ "": { "name": "kaitai-struct", "version": "0.11.0-SNAPSHOT.1", - "license": "Apache-2.0" + "license": "Apache-2.0", + "devDependencies": { + "@rollup/plugin-typescript": "^8.3.0", + "@types/node": "^16.11.11", + "rollup": "^2.60.2", + "tslib": "^2.3.1", + "typescript": "^4.5.2" + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz", + "integrity": "sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "16.18.112", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.112.tgz", + "integrity": "sha512-EKrbKUGJROm17+dY/gMi31aJlGLJ75e1IkTojt9n6u+hnaTBDs+M1bIdOawpk2m6YUAXq/R2W0SxCng1tndHCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } } } } diff --git a/package.json b/package.json index d57d4a7..c125fce 100644 --- a/package.json +++ b/package.json @@ -2,20 +2,6 @@ "name": "kaitai-struct", "version": "0.11.0-SNAPSHOT.1", "description": "Kaitai Struct: runtime library for JavaScript", - "license": "Apache-2.0", - "author": "Kaitai team (https://github.com/orgs/kaitai-io/people) and Ilmari Heikkinen", - "homepage": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime#readme", - "repository": { - "type": "git", - "url": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime.git" - }, - "bugs": { - "url": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime/issues" - }, - "main": "index.js", - "files": [ - "KaitaiStream.js" - ], "keywords": [ "binary", "file", @@ -29,5 +15,32 @@ "stream", "struct", "structure" - ] + ], + "homepage": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime#readme", + "bugs": { + "url": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime.git" + }, + "license": "Apache-2.0", + "author": "Kaitai team (https://github.com/orgs/kaitai-io/people) and Ilmari Heikkinen", + "main": "index.js", + "types": "index.d.ts", + "files": [ + "KaitaiStream.js", + "KaitaiStream.d.ts", + "index.d.ts" + ], + "scripts": { + "build": "rollup -c" + }, + "devDependencies": { + "@rollup/plugin-typescript": "^8.3.0", + "@types/node": "^16.11.11", + "rollup": "^2.60.2", + "tslib": "^2.3.1", + "typescript": "^4.5.2" + } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..b6ad679 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,11 @@ +import typescript from '@rollup/plugin-typescript'; + +export default [{ + input: 'KaitaiStream.ts', + output: { + dir: '.', + format: 'umd', + name: 'KaitaiStream' + }, + plugins: [typescript()] +}]; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..86e0333 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,102 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + + /* Modules */ + "module": "es2015", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "exclude": [] +}