mp4box
Version:
JavaScript version of GPAC's MP4Box tool
1 lines • 653 kB
Source Map (JSON)
{"version":3,"sources":["../entries/all.ts","../src/constants.ts","../src/mp4boxbuffer.ts","../src/DataStream.ts","../src/log.ts","../src/buffer.ts","../src/box.ts","../src/box-diff.ts","../src/registry.ts","../src/parser.ts","../src/containerBox.ts","../src/boxes/sampleentries/base.ts","../src/boxes/displays/parameterSetArray.ts","../src/boxes/avcC.ts","../src/boxes/defaults.ts","../src/boxes/dref.ts","../src/boxes/elng.ts","../src/boxes/ftyp.ts","../src/boxes/hdlr.ts","../src/boxes/hvcC.ts","../src/boxes/mdhd.ts","../src/boxes/mehd.ts","../src/boxes/infe.ts","../src/boxes/iinf.ts","../src/boxes/iloc.ts","../src/boxes/iref.ts","../src/boxes/pitm.ts","../src/boxes/meta.ts","../src/boxes/mfhd.ts","../src/boxes/mvhd.ts","../src/boxes/sampleentries/mett.ts","../src/boxes/sampleentries/metx.ts","../src/boxes/av1C.ts","../src/boxes/esds.ts","../src/boxes/vpcC.ts","../src/boxes/vvcC.ts","../src/boxes/colr.ts","../src/boxes/sampleentries/sampleentry.ts","../src/boxes/sampleentries/sbtt.ts","../src/boxes/sampleentries/stpp.ts","../src/boxes/sampleentries/stxt.ts","../src/boxes/sampleentries/tx3g.ts","../src/boxes/sampleentries/wvtt.ts","../src/boxes/sbgp.ts","../src/boxes/sdtp.ts","../src/boxes/sgpd.ts","../src/boxes/sidx.ts","../src/boxes/smhd.ts","../src/boxes/stco.ts","../src/boxes/sthd.ts","../src/boxes/stsc.ts","../src/boxes/stsd.ts","../src/boxes/stsz.ts","../src/boxes/stts.ts","../src/boxes/tfdt.ts","../src/boxes/tfhd.ts","../src/boxes/tkhd.ts","../src/boxes/trex.ts","../src/boxes/trun.ts","../src/boxes/url.ts","../src/boxes/vmhd.ts","../src/isofile.ts","../src/create-file.ts","../src/descriptor.ts","../src/text-mp4.ts","../entries/all-boxes.ts","../src/boxes/a1lx.ts","../src/boxes/a1op.ts","../src/boxes/auxC.ts","../src/boxes/btrt.ts","../src/boxes/ccst.ts","../src/boxes/cdef.ts","../src/boxes/clap.ts","../src/boxes/clli.ts","../src/boxes/cmex.ts","../src/boxes/cmin.ts","../src/boxes/cmpd.ts","../src/boxes/co64.ts","../src/boxes/CoLL.ts","../src/boxes/covi.ts","../src/boxes/cprt.ts","../src/boxes/csch.ts","../src/boxes/cslg.ts","../src/boxes/ctts.ts","../src/boxes/dac3.ts","../src/boxes/dec3.ts","../src/boxes/dfLa.ts","../src/boxes/dimm.ts","../src/boxes/dmax.ts","../src/boxes/dmed.ts","../src/boxes/dOps.ts","../src/boxes/drep.ts","../src/boxes/elst.ts","../src/boxes/emsg.ts","../src/boxes/EntityToGroup/base.ts","../src/boxes/EntityToGroup/index.ts","../src/boxes/fiel.ts","../src/boxes/frma.ts","../src/boxes/imir.ts","../src/boxes/ipma.ts","../src/boxes/irot.ts","../src/boxes/ispe.ts","../src/boxes/itai.ts","../src/boxes/kind.ts","../src/boxes/leva.ts","../src/boxes/lhvC.ts","../src/boxes/lsel.ts","../src/boxes/maxr.ts","../src/boxes/displays/colorPoint.ts","../src/boxes/mdcv.ts","../src/boxes/mfro.ts","../src/boxes/mskC.ts","../src/boxes/npck.ts","../src/boxes/nump.ts","../src/boxes/padb.ts","../src/boxes/pasp.ts","../src/boxes/payl.ts","../src/boxes/payt.ts","../src/boxes/pdin.ts","../src/boxes/pixi.ts","../src/boxes/pmax.ts","../src/boxes/prdi.ts","../src/boxes/prfr.ts","../src/boxes/prft.ts","../src/boxes/pssh.ts","../src/boxes/qt/clef.ts","../src/boxes/qt/data.ts","../src/boxes/qt/enof.ts","../src/boxes/qt/ilst.ts","../src/boxes/qt/keys.ts","../src/boxes/qt/prof.ts","../src/boxes/qt/tapt.ts","../src/boxes/qt/wave.ts","../src/boxes/rtp.ts","../src/boxes/saio.ts","../src/boxes/saiz.ts","../src/boxes/displays/pixel.ts","../src/boxes/sbpm.ts","../src/boxes/schm.ts","../src/boxes/sdp.ts","../src/boxes/senc.ts","../src/boxes/SmDm.ts","../src/boxes/srat.ts","../src/boxes/ssix.ts","../src/boxes/stdp.ts","../src/boxes/stri.ts","../src/boxes/stsg.ts","../src/boxes/stsh.ts","../src/boxes/stss.ts","../src/boxes/stvi.ts","../src/boxes/styp.ts","../src/boxes/stz2.ts","../src/boxes/subs.ts","../src/boxes/taic.ts","../src/boxes/tenc.ts","../src/boxes/tfra.ts","../src/boxes/tmax.ts","../src/boxes/tmin.ts","../src/boxes/totl.ts","../src/boxes/tpay.ts","../src/boxes/tpyl.ts","../src/boxes/trackgroups/msrc.ts","../src/boxes/tref.ts","../src/boxes/trep.ts","../src/boxes/trpy.ts","../src/boxes/tsel.ts","../src/boxes/txtC.ts","../src/boxes/tyco.ts","../src/boxes/udes.ts","../src/boxes/uncC.ts","../src/boxes/urn.ts","../src/boxes/vttC.ts","../src/boxes/vvnC.ts","../src/boxes/samplegroups/alst.ts","../src/boxes/samplegroups/avll.ts","../src/boxes/samplegroups/avss.ts","../src/boxes/samplegroups/dtrt.ts","../src/boxes/samplegroups/mvif.ts","../src/boxes/samplegroups/prol.ts","../src/boxes/samplegroups/rap.ts","../src/boxes/samplegroups/rash.ts","../src/boxes/samplegroups/roll.ts","../src/boxes/samplegroups/scif.ts","../src/boxes/samplegroups/scnm.ts","../src/boxes/samplegroups/seig.ts","../src/boxes/samplegroups/stsa.ts","../src/boxes/samplegroups/sync.ts","../src/boxes/samplegroups/tele.ts","../src/boxes/samplegroups/tsas.ts","../src/boxes/samplegroups/tscl.ts","../src/boxes/samplegroups/vipr.ts","../src/boxes/uuid/index.ts"],"sourcesContent":["export * from '#/box';\nexport * from '#/box-diff';\nexport * from '#/boxes/sampleentries/base';\nexport * from '#/buffer';\nexport * from '#/create-file';\nexport * from '#/DataStream';\nexport * from '#/descriptor';\nexport * from '#/isofile';\nexport * from '#/log';\nexport * from '#/mp4boxbuffer';\nexport * from '#/text-mp4';\nexport * from './types';\nimport * as DESCRIPTORS from '#/descriptor';\nimport { registerBoxes, registerDescriptors } from '#/registry';\nimport * as BOXES from './all-boxes';\n\nexport const BoxParser = registerBoxes(BOXES);\n\nregisterDescriptors(DESCRIPTORS);\n","export const MAX_SIZE = Math.pow(2, 32);\nexport const MAX_UINT32 = Math.pow(2, 32) - 1;\n\n// Flags\nexport const TKHD_FLAG_ENABLED = 0x000001;\nexport const TKHD_FLAG_IN_MOVIE = 0x000002;\nexport const TKHD_FLAG_IN_PREVIEW = 0x000004;\n\nexport const TFHD_FLAG_BASE_DATA_OFFSET = 0x01;\nexport const TFHD_FLAG_SAMPLE_DESC = 0x02;\nexport const TFHD_FLAG_SAMPLE_DUR = 0x08;\nexport const TFHD_FLAG_SAMPLE_SIZE = 0x10;\nexport const TFHD_FLAG_SAMPLE_FLAGS = 0x20;\nexport const TFHD_FLAG_DUR_EMPTY = 0x10000;\nexport const TFHD_FLAG_DEFAULT_BASE_IS_MOOF = 0x20000;\n\nexport const TRUN_FLAGS_DATA_OFFSET = 0x01;\nexport const TRUN_FLAGS_FIRST_FLAG = 0x04;\nexport const TRUN_FLAGS_DURATION = 0x100;\nexport const TRUN_FLAGS_SIZE = 0x200;\nexport const TRUN_FLAGS_FLAGS = 0x400;\nexport const TRUN_FLAGS_CTS_OFFSET = 0x800;\n\nexport const ERR_INVALID_DATA = -1;\nexport const ERR_NOT_ENOUGH_DATA = 0;\nexport const OK = 1;\n\n// Constants\nexport const SAMPLE_ENTRY_TYPE_VISUAL = 'Visual';\nexport const SAMPLE_ENTRY_TYPE_AUDIO = 'Audio';\nexport const SAMPLE_ENTRY_TYPE_HINT = 'Hint';\nexport const SAMPLE_ENTRY_TYPE_METADATA = 'Metadata';\nexport const SAMPLE_ENTRY_TYPE_SUBTITLE = 'Subtitle';\nexport const SAMPLE_ENTRY_TYPE_SYSTEM = 'System';\nexport const SAMPLE_ENTRY_TYPE_TEXT = 'Text';\n","export class MP4BoxBuffer extends ArrayBuffer {\n fileStart: number;\n usedBytes?: number;\n\n constructor(byteLength: number) {\n super(byteLength);\n this.fileStart = 0;\n this.usedBytes = 0;\n }\n\n static fromArrayBuffer(buffer: ArrayBufferLike, fileStart: number): MP4BoxBuffer {\n const mp4BoxBuffer = new MP4BoxBuffer(buffer.byteLength);\n const view = new Uint8Array(mp4BoxBuffer);\n view.set(new Uint8Array(buffer));\n mp4BoxBuffer.fileStart = fileStart;\n return mp4BoxBuffer;\n }\n}\n","import { MAX_SIZE } from '#/constants';\nimport type {\n Charset,\n ParsedType,\n StructDataFromStructDefinition,\n StructDefinition,\n TupleType,\n Type,\n TypedArray,\n ValueFromType,\n} from '@types';\nimport { MP4BoxBuffer } from '#/mp4boxbuffer';\nimport type { ISOFile } from './isofile';\n\ntype ReadTypeReturnValue =\n | string\n | number\n | Uint8Array\n | Uint16Array\n | Uint32Array\n | Int8Array\n | Int16Array\n | Int32Array\n | Float32Array\n | Float64Array\n | Array<ReadTypeReturnValue>\n | {\n [key: string]: ReadTypeReturnValue;\n };\n\nexport enum Endianness {\n BIG_ENDIAN = 1,\n LITTLE_ENDIAN,\n}\n\n// TODO: fix endianness for 24/64-bit fields\n// TODO: check range/support for 64-bits numbers in JavaScript\n\nexport class DataStream {\n static ENDIANNESS: Endianness =\n new Int8Array(new Int16Array([1]).buffer)[0] > 0\n ? Endianness.LITTLE_ENDIAN\n : Endianness.BIG_ENDIAN;\n\n isofile?: ISOFile; // Reference to the ISOFile object if any\n\n _buffer?: MP4BoxBuffer;\n _byteOffset?: number;\n _dataView?: DataView<ArrayBuffer>;\n\n endianness: Endianness;\n protected position: number;\n\n /**\n * DataStream reads scalars, arrays and structs of data from an ArrayBuffer.\n * It's like a file-like DataView on steroids.\n *\n * @param arrayBuffer ArrayBuffer to read from.\n * @param byteOffset Offset from arrayBuffer beginning for the DataStream.\n * @param endianness Endianness of the DataStream (default: BIG_ENDIAN).\n */\n constructor(\n arrayBuffer?: ArrayBuffer | DataView<ArrayBuffer> | number,\n byteOffset?: number,\n endianness?: Endianness,\n ) {\n this._byteOffset = byteOffset || 0;\n if (arrayBuffer instanceof ArrayBuffer) {\n this.buffer = MP4BoxBuffer.fromArrayBuffer(arrayBuffer, 0);\n } else if (arrayBuffer instanceof DataView) {\n this.dataView = arrayBuffer;\n if (byteOffset) this._byteOffset += byteOffset;\n } else {\n this.buffer = new MP4BoxBuffer(arrayBuffer || 0);\n }\n this.position = 0;\n this.endianness = endianness ? endianness : Endianness.BIG_ENDIAN;\n }\n\n getPosition() {\n return this.position;\n }\n\n /**\n * Internal function to resize the DataStream buffer when required.\n * @param extra Number of bytes to add to the buffer allocation.\n */\n _realloc(extra: number) {\n if (!this._dynamicSize) {\n return;\n }\n const req = this._byteOffset + this.position + extra;\n let blen = this._buffer.byteLength;\n if (req <= blen) {\n if (req > this._byteLength) {\n this._byteLength = req;\n }\n return;\n }\n if (blen < 1) {\n blen = 1;\n }\n while (req > blen) {\n blen *= 2;\n }\n const buf = new MP4BoxBuffer(blen);\n const src = new Uint8Array(this._buffer);\n const dst = new Uint8Array(buf, 0, src.length);\n dst.set(src);\n this.buffer = buf;\n this._byteLength = req;\n }\n\n /**\n * Internal function to trim the DataStream buffer when required.\n * Used for stripping out the extra bytes from the backing buffer when\n * the virtual byteLength is smaller than the buffer byteLength (happens after\n * growing the buffer with writes and not filling the extra space completely).\n */\n _trimAlloc() {\n if (this._byteLength === this._buffer.byteLength) {\n return;\n }\n const buf = new MP4BoxBuffer(this._byteLength);\n const dst = new Uint8Array(buf);\n const src = new Uint8Array(this._buffer, 0, dst.length);\n dst.set(src);\n this.buffer = buf;\n }\n\n /**\n * Virtual byte length of the DataStream backing buffer.\n * Updated to be max of original buffer size and last written size.\n * If dynamicSize is false is set to buffer size.\n */\n _byteLength = 0;\n\n /**\n * Returns the byte length of the DataStream object.\n * @type {number}\n */\n get byteLength() {\n return this._byteLength - this._byteOffset;\n }\n\n /**\n * Set/get the backing ArrayBuffer of the DataStream object.\n * The setter updates the DataView to point to the new buffer.\n * @type {Object}\n */\n\n get buffer() {\n this._trimAlloc();\n return this._buffer;\n }\n set buffer(value: MP4BoxBuffer) {\n this._buffer = value;\n this._dataView = new DataView(value, this._byteOffset);\n this._byteLength = value.byteLength;\n }\n\n /**\n * Set/get the byteOffset of the DataStream object.\n * The setter updates the DataView to point to the new byteOffset.\n * @type {number}\n */\n get byteOffset() {\n return this._byteOffset;\n }\n set byteOffset(value) {\n this._byteOffset = value;\n this._dataView = new DataView(this._buffer, this._byteOffset);\n this._byteLength = this._buffer.byteLength;\n }\n\n /**\n * Set/get the byteOffset of the DataStream object.\n * The setter updates the DataView to point to the new byteOffset.\n * @type {number}\n */\n get dataView() {\n return this._dataView;\n }\n set dataView(value: DataView<ArrayBuffer>) {\n this._byteOffset = value.byteOffset;\n this._buffer = MP4BoxBuffer.fromArrayBuffer(value.buffer, 0);\n this._dataView = new DataView(this._buffer, this._byteOffset);\n this._byteLength = this._byteOffset + value.byteLength;\n }\n\n /**\n * Sets the DataStream read/write position to given position.\n * Clamps between 0 and DataStream length.\n *\n * @param pos Position to seek to.\n * @return\n */\n seek(pos: number) {\n const npos = Math.max(0, Math.min(this.byteLength, pos));\n this.position = isNaN(npos) || !isFinite(npos) ? 0 : npos;\n }\n\n /**\n * Returns true if the DataStream seek pointer is at the end of buffer and\n * there's no more data to read.\n *\n * @return True if the seek pointer is at the end of the buffer.\n */\n isEof() {\n return this.position >= this._byteLength;\n }\n\n #isTupleType(type: unknown): type is TupleType {\n return Array.isArray(type) && type.length === 3 && type[0] === '[]';\n }\n\n /**\n * Maps a Uint8Array into the DataStream buffer.\n *\n * Nice for quickly reading in data.\n *\n * @param length Number of elements to map.\n * @param e Endianness of the data to read.\n * @return Uint8Array to the DataStream backing buffer.\n */\n mapUint8Array(length: number) {\n this._realloc(length * 1);\n const arr = new Uint8Array(this._buffer, this.byteOffset + this.position, length);\n this.position += length * 1;\n return arr;\n }\n\n /**\n * Reads an Int32Array of desired length and endianness from the DataStream.\n *\n * @param length Number of elements to map.\n * @param endianness Endianness of the data to read.\n * @return The read Int32Array.\n */\n readInt32Array(length?: number, endianness?: Endianness) {\n length = length === undefined ? this.byteLength - this.position / 4 : length;\n const arr = new Int32Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n DataStream.arrayToNative(arr, endianness ?? this.endianness);\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads an Int16Array of desired length and endianness from the DataStream.\n *\n * @param length Number of elements to map.\n * @param endianness Endianness of the data to read.\n * @return The read Int16Array.\n */\n readInt16Array(length?: number, endianness?: Endianness) {\n length = length === undefined ? this.byteLength - this.position / 2 : length;\n const arr = new Int16Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n DataStream.arrayToNative(arr, endianness ?? this.endianness);\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads an Int8Array of desired length from the DataStream.\n *\n * @param length Number of elements to map.\n * @param e Endianness of the data to read.\n * @return The read Int8Array.\n */\n readInt8Array(length?: number) {\n length = length === undefined ? this.byteLength - this.position : length;\n const arr = new Int8Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads a Uint32Array of desired length and endianness from the DataStream.\n *\n * @param length Number of elements to map.\n * @param endianness Endianness of the data to read.\n * @return The read Uint32Array.\n */\n readUint32Array(length?: number, endianness?: Endianness) {\n length = length === undefined ? this.byteLength - this.position / 4 : length;\n const arr = new Uint32Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n DataStream.arrayToNative(arr, endianness ?? this.endianness);\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads a Uint16Array of desired length and endianness from the DataStream.\n *\n * @param length Number of elements to map.\n * @param endianness Endianness of the data to read.\n * @return The read Uint16Array.\n */\n readUint16Array(length?: number, endianness?: Endianness) {\n length = length === undefined ? this.byteLength - this.position / 2 : length;\n const arr = new Uint16Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n DataStream.arrayToNative(arr, endianness ?? this.endianness);\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads a Uint8Array of desired length from the DataStream.\n *\n * @param length Number of elements to map.\n * @param e Endianness of the data to read.\n * @return The read Uint8Array.\n */\n readUint8Array(length?: number) {\n length = length === undefined ? this.byteLength - this.position : length;\n const arr = new Uint8Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads a Float64Array of desired length and endianness from the DataStream.\n *\n * @param length Number of elements to map.\n * @param endianness Endianness of the data to read.\n * @return The read Float64Array.\n */\n readFloat64Array(length?: number, endianness?: Endianness) {\n length = length === undefined ? this.byteLength - this.position / 8 : length;\n const arr = new Float64Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n DataStream.arrayToNative(arr, endianness ?? this.endianness);\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads a Float32Array of desired length and endianness from the DataStream.\n *\n * @param length Number of elements to map.\n * @param endianness Endianness of the data to read.\n * @return The read Float32Array.\n */\n readFloat32Array(length?: number, endianness?: Endianness) {\n length = length === undefined ? this.byteLength - this.position / 4 : length;\n const arr = new Float32Array(length);\n DataStream.memcpy(\n arr.buffer,\n 0,\n this.buffer,\n this.byteOffset + this.position,\n length * arr.BYTES_PER_ELEMENT,\n );\n DataStream.arrayToNative(arr, endianness ?? this.endianness);\n this.position += arr.byteLength;\n return arr;\n }\n\n /**\n * Reads a 32-bit int from the DataStream with the desired endianness.\n *\n * @param endianness Endianness of the number.\n * @return The read number.\n */\n readInt32(endianness?: Endianness) {\n const v = this._dataView.getInt32(\n this.position,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 4;\n return v;\n }\n\n /**\n * Reads a 16-bit int from the DataStream with the desired endianness.\n *\n * @param endianness Endianness of the number.\n * @return The read number.\n */\n readInt16(endianness?: Endianness) {\n const v = this._dataView.getInt16(\n this.position,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 2;\n return v;\n }\n\n /**\n * Reads an 8-bit int from the DataStream.\n *\n * @return The read number.\n */\n readInt8() {\n const v = this._dataView.getInt8(this.position);\n this.position += 1;\n return v;\n }\n\n /**\n * Reads a 32-bit unsigned int from the DataStream with the desired endianness.\n *\n * @param endianness Endianness of the number.\n * @return The read number.\n */\n readUint32(endianness?: Endianness) {\n const v = this._dataView.getUint32(\n this.position,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 4;\n return v;\n }\n\n /**\n * Reads a 16-bit unsigned int from the DataStream with the desired endianness.\n *\n * @param endianness Endianness of the number.\n * @return The read number.\n */\n readUint16(endianness?: Endianness) {\n const v = this._dataView.getUint16(\n this.position,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 2;\n return v;\n }\n\n /**\n * Reads an 8-bit unsigned int from the DataStream.\n *\n * @return The read number.\n */\n readUint8() {\n const v = this._dataView.getUint8(this.position);\n this.position += 1;\n return v;\n }\n\n /**\n * Reads a 32-bit float from the DataStream with the desired endianness.\n *\n * @param endianness Endianness of the number.\n * @return The read number.\n */\n readFloat32(endianness?: Endianness) {\n const value = this._dataView.getFloat32(\n this.position,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 4;\n return value;\n }\n\n /**\n * Reads a 64-bit float from the DataStream with the desired endianness.\n *\n * @param endianness Endianness of the number.\n * @return The read number.\n */\n readFloat64(endianness?: Endianness) {\n const value = this._dataView.getFloat64(\n this.position,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 8;\n return value;\n }\n\n /**\n * Copies byteLength bytes from the src buffer at srcOffset to the\n * dst buffer at dstOffset.\n *\n * @param dst Destination ArrayBuffer to write to.\n * @param dstOffset Offset to the destination ArrayBuffer.\n * @param src Source ArrayBuffer to read from.\n * @param srcOffset Offset to the source ArrayBuffer.\n * @param byteLength Number of bytes to copy.\n */\n static memcpy(\n dst: ArrayBufferLike,\n dstOffset?: number,\n src?: ArrayBufferLike,\n srcOffset?: number,\n byteLength?: number,\n ) {\n const dstU8 = new Uint8Array(dst, dstOffset, byteLength);\n const srcU8 = new Uint8Array(src, srcOffset, byteLength);\n dstU8.set(srcU8);\n }\n\n /**\n * Converts array to native endianness in-place.\n *\n * @param typedArray Typed array to convert.\n * @param endianness True if the data in the array is\n * little-endian. Set false for big-endian.\n * @return The converted typed array.\n */\n static arrayToNative(typedArray: TypedArray, endianness?: Endianness) {\n if (endianness === DataStream.ENDIANNESS) {\n return typedArray;\n } else {\n return this.flipArrayEndianness(typedArray);\n }\n }\n\n /**\n * Converts native endianness array to desired endianness in-place.\n *\n * @param typedArray Typed array to convert.\n * @param littleEndian True if the converted array should be\n * little-endian. Set false for big-endian.\n * @return The converted typed array.\n */\n static nativeToEndian(typedArray: TypedArray, littleEndian: boolean) {\n if (littleEndian && DataStream.ENDIANNESS === Endianness.LITTLE_ENDIAN) {\n return typedArray;\n } else {\n return this.flipArrayEndianness(typedArray);\n }\n }\n\n /**\n * Flips typed array endianness in-place.\n *\n * @param typedArray Typed array to flip.\n * @return The converted typed array.\n */\n static flipArrayEndianness(typedArray: TypedArray) {\n const u8 = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);\n for (let i = 0; i < typedArray.byteLength; i += typedArray.BYTES_PER_ELEMENT) {\n for (let j = i + typedArray.BYTES_PER_ELEMENT - 1, k = i; j > k; j--, k++) {\n const tmp = u8[k];\n u8[k] = u8[j];\n u8[j] = tmp;\n }\n }\n return typedArray;\n }\n\n /**\n * Seek position where DataStream#readStruct ran into a problem.\n * Useful for debugging struct parsing.\n *\n * @type {number}\n */\n failurePosition = 0;\n\n /**\n * Read a string of desired length and encoding from the DataStream.\n *\n * @param length The length of the string to read in bytes.\n * @param encoding The encoding of the string data in the DataStream.\n * Defaults to ASCII.\n * @return The read string.\n */\n readString(length: number, encoding?: Charset): string {\n if (encoding === undefined || encoding === 'ASCII') {\n return fromCharCodeUint8(\n this.mapUint8Array(length === undefined ? this.byteLength - this.position : length),\n );\n } else {\n return new TextDecoder(encoding).decode(this.mapUint8Array(length));\n }\n }\n\n /**\n * Read null-terminated string of desired length from the DataStream. Truncates\n * the returned string so that the null byte is not a part of it.\n *\n * @param length The length of the string to read.\n * @return The read string.\n */\n readCString(length?: number): string {\n let i = 0;\n const blen = this.byteLength - this.position;\n const u8 = new Uint8Array(this._buffer, this._byteOffset + this.position);\n const len = length !== undefined ? Math.min(length, blen) : blen;\n for (; i < len && u8[i] !== 0; i++); // find first zero byte\n const s = fromCharCodeUint8(this.mapUint8Array(i));\n if (length !== undefined) {\n this.position += len - i;\n } else if (i !== blen) {\n this.position += 1; // trailing zero if not at end of buffer\n }\n return s;\n }\n\n readInt64() {\n return this.readInt32() * MAX_SIZE + this.readUint32();\n }\n readUint64() {\n return this.readUint32() * MAX_SIZE + this.readUint32();\n }\n\n readUint24() {\n return (this.readUint8() << 16) + (this.readUint8() << 8) + this.readUint8();\n }\n\n /**\n * Saves the DataStream contents to the given filename.\n * Uses Chrome's anchor download property to initiate download.\n *\n * @param filename Filename to save as.\n * @return\n * @bundle DataStream-write.js\n */\n save(filename: string) {\n const blob = new Blob([this.buffer]);\n // Modernized: Works in browser, and is a no-op (or throws) in Vitest/node\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n if (window.URL && URL.createObjectURL) {\n const url = window.URL.createObjectURL(blob);\n const a = document.createElement('a');\n document.body.appendChild(a);\n a.setAttribute('href', url);\n a.setAttribute('download', filename);\n a.setAttribute('target', '_self');\n a.click();\n window.URL.revokeObjectURL(url);\n document.body.removeChild(a);\n } else {\n throw new Error(\"DataStream.save: Can't create object URL.\");\n }\n }\n return blob;\n }\n\n /**\n * Whether to extend DataStream buffer when trying to write beyond its size.\n * If set, the buffer is reallocated to twice its current size until the\n * requested write fits the buffer.\n *\n * @type {boolean}\n * @bundle DataStream-write.js\n */\n _dynamicSize = 1;\n\n /** @bundle DataStream-write.js */\n get dynamicSize() {\n return this._dynamicSize;\n }\n\n /** @bundle DataStream-write.js */\n set dynamicSize(v) {\n if (!v) {\n this._trimAlloc();\n }\n this._dynamicSize = v;\n }\n\n /**\n * Internal function to trim the DataStream buffer when required.\n * Used for stripping out the first bytes when not needed anymore.\n *\n * @return\n * @bundle DataStream-write.js\n */\n shift(offset: number) {\n const buf = new MP4BoxBuffer(this._byteLength - offset);\n const dst = new Uint8Array(buf);\n const src = new Uint8Array(this._buffer, offset, dst.length);\n dst.set(src);\n this.buffer = buf;\n this.position -= offset;\n }\n\n /**\n * Writes an Int32Array of specified endianness to the DataStream.\n *\n * @param array The array to write.\n * @param endianness Endianness of the data to write.\n * @bundle DataStream-write.js\n */\n writeInt32Array(array: ArrayLike<number>, endianness?: Endianness) {\n this._realloc(array.length * 4);\n if (\n array instanceof Int32Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapInt32Array(array.length, endianness);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeInt32(array[i], endianness);\n }\n }\n }\n\n /**\n * Writes an Int16Array of specified endianness to the DataStream.\n *\n * @param array The array to write.\n * @param endianness Endianness of the data to write.\n * @bundle DataStream-write.js\n */\n writeInt16Array(array: ArrayLike<number>, endianness?: Endianness) {\n this._realloc(array.length * 2);\n if (\n array instanceof Int16Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapInt16Array(array.length, endianness);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeInt16(array[i], endianness);\n }\n }\n }\n\n /**\n * Writes an Int8Array to the DataStream.\n *\n * @param array The array to write.\n * @bundle DataStream-write.js\n */\n writeInt8Array(array: ArrayLike<number>) {\n this._realloc(array.length * 1);\n if (\n array instanceof Int8Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapInt8Array(array.length);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeInt8(array[i]);\n }\n }\n }\n\n /**\n * Writes a Uint32Array of specified endianness to the DataStream.\n *\n * @param array The array to write.\n * @param endianness Endianness of the data to write.\n * @bundle DataStream-write.js\n */\n writeUint32Array(array: ArrayLike<number>, endianness?: Endianness) {\n this._realloc(array.length * 4);\n if (\n array instanceof Uint32Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapUint32Array(array.length, endianness);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeUint32(array[i], endianness);\n }\n }\n }\n\n /**\n * Writes a Uint16Array of specified endianness to the DataStream.\n *\n * @param array The array to write.\n * @param endianness Endianness of the data to write.\n * @bundle DataStream-write.js\n */\n writeUint16Array(array: ArrayLike<number>, endianness?: Endianness) {\n this._realloc(array.length * 2);\n if (\n array instanceof Uint16Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapUint16Array(array.length, endianness);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeUint16(array[i], endianness);\n }\n }\n }\n\n /**\n * Writes a Uint8Array to the DataStream.\n *\n * @param array The array to write.\n * @bundle DataStream-write.js\n */\n writeUint8Array(array: ArrayLike<number>) {\n this._realloc(array.length * 1);\n if (\n array instanceof Uint8Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapUint8Array(array.length);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeUint8(array[i]);\n }\n }\n }\n\n /**\n * Writes a Float64Array of specified endianness to the DataStream.\n *\n * @param array The array to write.\n * @param endianness Endianness of the data to write.\n * @bundle DataStream-write.js\n */\n writeFloat64Array(array: ArrayLike<number>, endianness?: Endianness) {\n this._realloc(array.length * 8);\n if (\n array instanceof Float64Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapFloat64Array(array.length, endianness);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeFloat64(array[i], endianness);\n }\n }\n }\n\n /**\n * Writes a Float32Array of specified endianness to the DataStream.\n *\n * @param array The array to write.\n * @param endianness Endianness of the data to write.\n * @bundle DataStream-write.js\n */\n writeFloat32Array(array: ArrayLike<number>, endianness?: Endianness) {\n this._realloc(array.length * 4);\n if (\n array instanceof Float32Array &&\n this.byteOffset + (this.position % array.BYTES_PER_ELEMENT) === 0\n ) {\n DataStream.memcpy(\n this._buffer,\n this.byteOffset + this.position,\n array.buffer,\n 0,\n array.byteLength,\n );\n this.mapFloat32Array(array.length, endianness);\n } else {\n for (let i = 0; i < array.length; i++) {\n this.writeFloat32(array[i], endianness);\n }\n }\n }\n\n /**\n * Writes a 64-bit int to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeInt64(value: number, endianness?: Endianness) {\n this._realloc(8);\n this._dataView.setBigInt64(\n this.position,\n BigInt(value),\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 8;\n }\n\n /**\n * Writes a 32-bit int to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeInt32(value: number, endianness?: Endianness) {\n this._realloc(4);\n this._dataView.setInt32(\n this.position,\n value,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 4;\n }\n\n /**\n * Writes a 16-bit int to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeInt16(value: number, endianness?: Endianness) {\n this._realloc(2);\n this._dataView.setInt16(\n this.position,\n value,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 2;\n }\n\n /**\n * Writes an 8-bit int to the DataStream.\n *\n * @param value Number to write.\n * @bundle DataStream-write.js\n */\n writeInt8(value: number) {\n this._realloc(1);\n this._dataView.setInt8(this.position, value);\n this.position += 1;\n }\n\n /**\n * Writes a 32-bit unsigned int to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeUint32(value: number, endianness?: Endianness) {\n this._realloc(4);\n this._dataView.setUint32(\n this.position,\n value,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 4;\n }\n\n /**\n * Writes a 16-bit unsigned int to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeUint16(value: number, endianness?: Endianness) {\n this._realloc(2);\n this._dataView.setUint16(\n this.position,\n value,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 2;\n }\n\n /**\n * Writes an 8-bit unsigned int to the DataStream.\n *\n * @param value Number to write.\n * @bundle DataStream-write.js\n */\n writeUint8(value: number) {\n this._realloc(1);\n this._dataView.setUint8(this.position, value);\n this.position += 1;\n }\n\n /**\n * Writes a 32-bit float to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeFloat32(value: number, endianness?: Endianness) {\n this._realloc(4);\n this._dataView.setFloat32(\n this.position,\n value,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 4;\n }\n\n /**\n * Writes a 64-bit float to the DataStream with the desired endianness.\n *\n * @param value Number to write.\n * @param endianness Endianness of the number.\n * @bundle DataStream-write.js\n */\n writeFloat64(value: number, endianness?: Endianness) {\n this._realloc(8);\n this._dataView.setFloat64(\n this.position,\n value,\n (endianness ?? this.endianness) === Endianness.LITTLE_ENDIAN,\n );\n this.position += 8;\n }\n\n /**\n * Write a UCS-2 string of desired endianness to the DataStream. The\n * lengthOverride argument lets you define the number of characters to write.\n * If the string is shorter than lengthOverride, the extra space is padded with\n * zeroes.\n *\n * @param value The string to write.\n * @param endianness The endianness to use for the written string data.\n * @param lengthOverride The number of characters to write.\n * @bundle DataStream-write.js\n */\n writeUCS2String(value: string, endianness: Endianness, lengthOverride?: number) {\n if (lengthOverride === undefined) {\n lengthOverride = value.length;\n }\n let i: number;\n for (i = 0; i < value.length && i < lengthOverride; i++) {\n this.writeUint16(value.charCodeAt(i), endianness);\n }\n for (; i < lengthOverride; i++) {\n this.writeUint16(0);\n }\n }\n\n /**\n * Writes a string of desired length and encoding to the DataStream.\n *\n * @param value The string to write.\n * @param encoding The encoding for the written string data.\n * Defaults to ASCII.\n * @param length The number of characters to write.\n * @bundle DataStream-write.js\n */\n writeString(value: string, encoding?: string, length?: number) {\n let i = 0;\n if (encoding === undefined || encoding === 'ASCII') {\n if (length !== undefined) {\n const len = Math.min(value.length, length);\n for (i = 0; i < len; i++) {\n this.writeUint8(value.charCodeAt(i));\n }\n for (; i < length; i++) {\n this.writeUint8(0);\n }\n } else {\n for (i = 0; i < value.length; i++) {\n this.writeUint8(value.charCodeAt(i));\n }\n }\n } else {\n // @ts-expect-error FIXME: TextEncoder does not expect an encoding-parameter\n this.writeUint8Array(new TextEncoder(encoding).encode(value.substring(0, length)));\n }\n }\n\n /**\n * Writes a null-terminated string to DataStream and zero-pads it to length\n * bytes. If length is not given, writes the string followed by a zero.\n * If string is longer than length, the written part of the string does not have\n * a trailing zero.\n *\n * @param value The string to write.\n * @param length The number of characters to write.\n * @bundle DataStream-write.js\n */\n writeCString(value: string, length?: number) {\n let i = 0;\n if (length !== undefined) {\n const len = Math.min(value.length, length);\n for (i = 0; i < len; i++) {\n this.writeUint8(value.charCodeAt(i));\n }\n for (; i < length; i++) {\n this.writeUint8(0);\n }\n } else {\n for (i = 0; i < value.length; i++) {\n this.writeUint8(value.charCodeAt(i));\n }\n this.writeUint8(0);\n }\n }\n\n /**\n * Writes a struct to the DataStream. Takes a structDefinition that gives the\n * types and a struct object that gives the values. Refer to readStruct for the\n * structure of structDefinition.\n *\n * @param structDefinition Type definition of the struct.\n * @param struct The struct data object.\n * @bundle DataStream-write.js\n */\n writeStruct<const T extends StructDefinition>(\n structDefinition: T,\n struct: StructDataFromStructDefinition<T>,\n ) {\n for (let i = 0; i < structDefinition.length; i++) {\n const [structName, structType] = structDefinition[i];\n const structValue = struct[structName];\n this.writeType(structType, structValue, struct);\n }\n }\n\n /**\n * Writes object v of type t to the DataStream.\n *\n * @param type Type of data to write.\n * @param value Value of data to write.\n * @param struct Struct to pass to write callback functions.\n * @bundle DataStream-write.js\n */\n writeType<const T extends Type>(type: T, value: ValueFromType<T>, struct?: Record<string, Type>) {\n if (typeof type === 'function') {\n return type(this, value);\n } else if (typeof type === 'object' && !(type instanceof Array)) {\n return type.set(this, value, struct);\n }\n\n let lengthOverride: number;\n let charset: Charset = 'ASCII';\n const pos = this.position;\n\n let parsedType = type as ParsedType;\n\n if (typeof type === 'string' && /:/.test(type)) {\n const tp = type.split(':');\n parsedType = tp[0] as `cstring` | `string`;\n lengthOverride = parseInt(tp[1]);\n }\n if (typeof parsedType === 'string' && /,/.test(parsedType)) {\n const tp = parsedType.split(',');\n parsedType = tp[0] as `cstring` | `string`;\n // NOTE: this said `charset = parseInt(tp[1])` before;\n charset = tp[1] as Charset;\n }\n\n switch (parsedType) {\n case 'uint8':\n this.writeUint8(value);\n break;\n case 'int8':\n this.writeInt8(value);\n break;\n case 'uint16':\n this.writeUint16(value, this.endianness);\n break;\n case 'int16':\n this.writeInt16(value, this.endianness);\n break;\n case 'uint32':\n this.writeUint32(value, this.endianness);\n break;\n case 'int32':\n this.writeInt32(value, this.endianness);\n break;\n case 'float32':\n this.writeFloat32(value, this.endianness);\n break;\n case 'float64':\n this.writeFloat64(value, this.endianness);\n break;\n case 'uint16be':\n this.writeUint16(value, Endianness.BIG_ENDIAN);\n break;\n case 'int16be':\n this.writeInt16(value, Endianness.BIG_ENDIAN);\n break;\n case 'uint32be':\n this.writeUint32(value, Endianness.BIG_ENDIAN);\n break;\n case 'int32be':\n this.writeInt32(value, Endianness.BIG_ENDIAN);\n break;\n case 'float32be':\n this.writeFloat32(value, Endianness.BIG_ENDIAN);\n break;\n case 'float64be':\n this.writeFloat64(value, Endianness.BIG_ENDIAN);\n break;\n case 'uint16le':\n this.writeUint16(value, Endianness.LITTLE_ENDIAN);\n break;\n case 'int16le':\n this.writeInt16(value, Endianness.LITTLE_ENDIAN);\n break;\n case 'uint32le':\n this.writeUint32(value, Endianness.LITTLE_ENDIAN);\n break;\n case 'int32le':\n this.writeInt32(value, Endianness.LITTLE_ENDIAN);\n break;\n case 'float32le':\n this.writeFloat32(value, Endianness.LITTLE_ENDIAN);\n break;\n case 'float64le':\n this.writeFloat64(value, Endianness.LITTLE_ENDIAN);\n break;\n case 'cstring':\n this.writeCString(value, lengthOverride);\n break;\n case 'string':\n this.writeString(value, charset, lengthOverride);\n break;\n case 'u16string':\n this.writeUCS2String(value, this.endianness, lengthOverride);\n break;\n case 'u16stringle':\n this.writeUCS2String(value, Endianness.LITTLE_ENDIAN, lengthOverride);\n break;\n case 'u16stringbe':\n this.writeUCS2String(value, Endianness.BIG_ENDIAN, lengthOverride);\n break;\n default:\n if (this.#isTupleType(parsedType)) {\n const [, ta] = parsedType;\n for (let i = 0; i < value.length; i++) {\n this.writeType(ta, value[i]);\n }\n break;\n } else {\n this.writeStruct(parsedType, value);\n break;\n }\n }\n if (lengthOverride) {\n this.position = pos;\n this._realloc(lengthOverride);\n this.position = pos + lengthOverride;\n }\n }\n\n /** @bundle DataStream-write.js */\n writeUint64(value: number) {\n const h = Math.floor(value / MAX_SIZE);\n this.writeUint32(h);\n this.writeUint32(value & 0xffffffff);\n }\n\n /** @bundle DataStream-write.js */\n writeUint24(value: number) {\n this.writeUint8((value & 0x00ff0000) >> 16);\n this.writeUint8((value & 0x0000ff00) >> 8);\n this.writeUint8(value & 0x000000ff);\n }\n\n /** @bundle DataStream-write.js */\n adjustUint32(position: number, value: number) {\n const pos = this.position;\n this.seek(position);\n this.writeUint32(value);\n this.seek(pos);\n }\n\n /**\n * Reads a struct of data from the DataStream. The struct is defined as\n * an array of [name, type]-pairs. See the example below:\n *\n * ```ts\n * ds.readStruct([\n * ['headerTag', 'uint32'], // Uint32 in DataStream endianness.\n * ['headerTag2', 'uint32be'], // Big-endian Uint32.\n * ['headerTag3', 'uint32le'], // Little-endian Uint32.\n * ['array', ['[]', 'uint32', 16]], // Uint32Array of length 16.\n * ['array2', ['[]', 'uint32', 'array2Length']] // Uint32Array of length array2Length\n * ]);\n * ```\n *\n * The possible values for the type are as follows:\n *\n * ## Number types\n *\n * Unsuffixed number types use DataStream endianness.\n * To explicitly specify endianness, suffix the type with\n * 'le' for little-endian or 'be' for big-endian,\n * e.g. 'int32be' for big-endian int32.\n *\n * - `uint8` -- 8-bit unsigned int\n * - `uint16` -- 16-bit unsigned int\n * - `uint32` -- 32-bit unsigned int\n * - `int8` -- 8-bit int\n * - `int16` -- 16-bit int\n * - `int32` -- 32-bit int\n * - `float32` -- 32-bit float\n * - `float64` -- 64-bit float\n *\n * ## String types\n *\n * - `cstring` -- ASCII string terminated by a zero byte.\n * - `string:N` -- ASCII string of length N.\n * - `string,CHARSET:N` -- String of byteLength N encoded with given CHARSET.\n * - `u16string:N` -- UCS-2 string of length N in DataStream endianness.\n * - `u16stringle:N` -- UCS-2 string of length N in little-endian.\n * - `u16stringbe:N` -- UCS-2 string of length N in big-endian.\n *\n * ## Complex types\n *\n * ### Struct\n * ```ts\n * [[name, type], [name_2, type_2], ..., [name_N, type_N]]\n * ```\n *\n * ### Callback function to read and return data\n * ```ts\n * function(dataStream, struct) {}\n * ```\n *\n * ### Getter/setter functions\n * to read and return data, handy for using the same struct definition\n * for reading and writing structs.\n * ```ts\n * {\n * get: function(dataStream, struct) {},\n * set: function(dataStream, struct) {}\n * }\n * ```\n *\n * ### Array\n * Array of given type and length. The length can be either\n * - a number\n * - a string that references a previously-read field\n * - `*`\n * - a callback: `function(struct, dataStream, type){}`\n *\n * If length is `*`, reads in as many elements as it can.\n * ```ts\n * ['[]', type, length]\n * ```\n *\n * @param structDefinition Struct definition object.\n * @return The read struct. Null if failed to read struct.\n * @bundle DataStream-read-struct.js\n */\n readStruct<T extends StructDefinition>(structDefinition: T) {\n const struct = {} as StructDataFromStructDefinition<T>;\n const p = this.position;\n for (let i = 0; i < structDefinition.length; i += 1) {\n const t = structDefinition[i][1];\n const v = this.readType(t, struct);\n if (!v) {\n if (this.failurePosition === 0) {\n this.