mc-anvil
Version:
A Typescript library for reading Minecraft Anvil format files and Minecraft NBT format files in the browser.
214 lines (173 loc) • 5.97 kB
text/typescript
export class BinaryParser {
protected view: DataView;
protected position: number;
private _getByte: (byteOffset: number) => number = () => 0;
private _getShort: (byteOffset: number) => number = () => 0;
private _getUShort: (byteOffset: number) => number = () => 0;
private _getInt: (byteOffset: number) => number = () => 0;
private _getUInt: (byteOffset: number) => number = () => 0;
private _getFloat: (byteOffset: number) => number = () => 0;
private _getDouble: (byteOffset: number) => number = () => 0;
private _getInt64: (byteOffset: number) => bigint = () => BigInt(0);
private _getInt64LE: (byteOffset: number) => bigint = () => BigInt(0);
constructor(data: ArrayBuffer) {
this.view = new DataView(data);
this.position = 0;
this.bindReaders();
}
buffer(): ArrayBuffer {
return this.view.buffer;
}
protected bindReaders() {
this._getByte = this.view.getUint8.bind(this.view);
this._getShort = this.view.getInt16.bind(this.view);
this._getUShort = this.view.getUint16.bind(this.view);
this._getInt = this.view.getInt32.bind(this.view);
this._getUInt = this.view.getUint32.bind(this.view);
this._getFloat = this.view.getFloat32.bind(this.view);
this._getDouble = this.view.getFloat64.bind(this.view);
this._getInt64 = (byteOffset: number) => this.view.getBigInt64(byteOffset, true);
this._getInt64LE = (byteOffset: number) => this.view.getBigInt64(byteOffset);
}
seek(position: number) {
this.position = position;
}
currentPosition() {
return this.position;
}
remainingLength() {
return this.view.byteLength - this.position;
}
private getValue(readFunc: (position: number, littleEndian?: boolean) => number, positionIncrement: number) {
const retValue = readFunc(this.position);
this.position += positionIncrement;
return retValue;
}
private getBigValue(readFunc: (position: number, littleEndian?: boolean) => bigint, positionIncrement: number) {
const retValue = readFunc(this.position);
this.position += positionIncrement;
return retValue;
}
getByte() {
return this.getValue(this._getByte, 1);
}
setByte(value: number) {
this.view.setUint8(this.position++, value);
}
getShort() {
return this.getValue(this._getShort, 2);
}
setShort(value: number) {
this.view.setInt16(this.position, value);
this.position += 2;
}
getUShort() {
return this.getValue(this._getUShort, 2);
}
setUShort(value: number) {
this.view.setUint16(this.position, value);
this.position += 2;
}
getInt() {
return this.getValue(this._getInt, 4);
}
setInt(value: number) {
this.view.setInt32(this.position, value);
this.position += 4;
}
getUInt() {
return this.getValue(this._getUInt, 4);
}
setUInt(value: number) {
this.view.setUint32(this.position, value);
this.position += 4;
}
getFloat() {
return this.getValue(this._getFloat, 4);
}
setFloat(value: number) {
this.view.setFloat32(this.position, value);
this.position += 4;
}
getDouble() {
return this.getValue(this._getDouble, 8);
}
setDouble(value: number) {
this.view.setFloat64(this.position, value);
this.position += 8;
}
getNByteInteger(n: number) {
const b: Array<number> = [];
for (let i = 0; i < n; ++i) b[i] = this.view.getUint8(this.position + i);
let value = 0;
for (let i = 0; i < b.length; ++i) value = (value * 256) + b[i];
this.position += n;
return value;
}
setNByteInteger(value: number, n: number) {
for (let i = n - 1; i >= 0; --i) this.setByte(Math.floor(value / Math.pow(256, i)) % 256);
}
getUInt64(): bigint {
const v = this.view.getBigUint64(this.position);
this.position += 8;
return v;
}
setUInt64(value: bigint) {
this.view.setBigUint64(this.position, value);
this.position += 8;
}
getInt64(): bigint {
return this.getBigValue(this._getInt64, 8);
}
setInt64(value: bigint) {
this.view.setBigInt64(this.position, value);
this.position += 8;
}
getUInt64LE(): bigint {
const v = this.view.getBigUint64(this.position, true);
this.position += 8;
return v;
}
setUInt64LE(value: bigint) {
this.view.setBigUint64(this.position, value, true);
this.position += 8;
}
getInt64LE(): bigint {
return this.getBigValue(this._getInt64LE, 8);
}
setInt64LE(value: bigint) {
this.view.setBigUint64(this.position, value, true);
this.position += 8;
}
getString(len?: number) {
let s = "", c: number;
while ((c = this.view.getUint8(this.position++)) != 0) {
s += String.fromCharCode(c);
if (len && s.length == len) break;
}
return s;
}
setString(value: string) {
for (let i = 0; i < value.length; ++i) this.setByte(value.charCodeAt(i));
this.setByte(0);
}
getFixedLengthString(len: number) {
let s = "";
for (let i = 0; i < len; i++) {
const c = this.view.getUint8(this.position++);
if (c > 0) s += String.fromCharCode(c);
}
return s;
}
setFixedLengthString(value: string) {
for (let i = 0; i < value.length; ++i) this.setByte(value.charCodeAt(i));
}
getFixedLengthTrimmedString(len: number) {
let s = "";
for (let i = 0; i < len; i++) {
const c = this.view.getUint8(this.position++);
if (c > 32) s += String.fromCharCode(c);
}
return s;
}
}