UNPKG

@etothepii/satisfactory-file-parser

Version:

A file parser for satisfactory files. Includes save files and blueprint files.

242 lines (241 loc) 9.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ByteStreamReader = void 0; const alignment_enum_1 = require("../byte/alignment.enum"); const byte_writer_class_1 = require("../byte/byte-writer.class"); const parser_error_1 = require("../error/parser.error"); class ByteStreamReader { constructor(reader, onCloseCallback, timeout = 30000, maxBufferThreshold = 100 * 1000 * 1000, alignment = alignment_enum_1.Alignment.LITTLE_ENDIAN) { this.reader = reader; this.onCloseCallback = onCloseCallback; this.timeout = timeout; this.maxBufferThreshold = maxBufferThreshold; this.alignment = alignment; this.debug = false; this.closedAlready = false; this.totalNumberOfBytesRead = 0; this.trailingBufferSize = 0; this.continuousRead = async () => { while (!this.inputStreamIsDone && !this.paused) { const { done, value } = await this.reader.read(); if (!this.inputStreamIsDone) { if (value) { const trailingPos = Math.max(0, this.currentByte - this.trailingBufferSize); this.operatingStreamBuffer = new Uint8Array(byte_writer_class_1.ByteWriter.AppendBuffer(this.operatingStreamBuffer.slice(trailingPos), value)); this.operatingDataView = new DataView(this.operatingStreamBuffer.buffer); this.currentByte = Math.min(this.currentByte, this.trailingBufferSize); if (this.operatingStreamBuffer.byteLength > this.maxBufferThreshold) { console.warn('pausing due to size.'); this.pause(); } if (this.callbackAfterRead) { this.callbackAfterRead(); } } } else { this.close(); } this.inputStreamIsDone = done; } }; this.operatingStreamBuffer = new Uint8Array(); this.operatingDataView = new DataView(this.operatingStreamBuffer.buffer); this.inputStreamIsDone = false; this.currentByte = 0; this.neededNextAmountOfBytes = 0; this.paused = false; this.trailingBufferSize = 2000; this.readLoopHandle = setTimeout(this.continuousRead); this.waitingTimeoutHandle = undefined; } pause() { this.paused = true; clearTimeout(this.readLoopHandle); this.readLoopHandle = undefined; if (this.waitingTimeoutHandle) { clearTimeout(this.waitingTimeoutHandle); this.waitingTimeoutHandle = undefined; } } resume() { if (this.paused) { this.paused = false; this.readLoopHandle = setTimeout(this.continuousRead); } } hasInput() { return !this.inputStreamIsDone; } async close() { this.inputStreamIsDone = true; this.pause(); await this.onCloseCallback(); this.closedAlready = true; } async allocate(count) { if (count > this.maxBufferThreshold * 0.5) { console.warn(`you are waiting for a very big chunk here mate. ${count} bytes. U sure you have your numbers right?`); } if (this.getAmountAllocatedLeft() < count) { if (this.inputStreamIsDone) { throw new parser_error_1.CorruptSaveError('Input Stream has finished before needed data was received.'); } if (this.paused) { this.resume(); } await this.waitUntilNumBytesAvailable(count); } } waitUntilNumBytesAvailable(count) { this.neededNextAmountOfBytes = count; return new Promise((resolve, reject) => { if (this.getAmountAllocatedLeft() >= this.neededNextAmountOfBytes) { this.neededNextAmountOfBytes = 0; return resolve(); } this.callbackAfterRead = () => { if (this.getAmountAllocatedLeft() >= this.neededNextAmountOfBytes) { if (this.waitingTimeoutHandle) { clearTimeout(this.waitingTimeoutHandle); } this.neededNextAmountOfBytes = 0; this.callbackAfterRead = undefined; return resolve(); } }; if (this.waitingTimeoutHandle) { clearTimeout(this.waitingTimeoutHandle); } this.waitingTimeoutHandle = setTimeout(() => { if (!this.inputStreamIsDone) { reject(new parser_error_1.TimeoutError(`Timed out before ${this.neededNextAmountOfBytes} bytes were available to read next.`)); } return resolve(); }, this.timeout); }); } skipBytes(byteLength = 1) { this.currentByte += byteLength; this.totalNumberOfBytesRead += byteLength; return; } readByte() { this.totalNumberOfBytesRead += 1; return this.operatingDataView.getUint8(this.currentByte++); } readBytes(count) { this.totalNumberOfBytesRead += count; return new Uint8Array(new Array(count).fill(0).map(pl => this.operatingDataView.getUint8(this.currentByte++))); } readHex(hexLength) { let hexPart = []; for (let i = 0; i < hexLength; i++) { let currentHex = String.fromCharCode(this.operatingDataView.getUint8(this.currentByte++)); hexPart.push(currentHex); } this.totalNumberOfBytesRead += hexLength; return hexPart.join(''); } readInt8() { this.totalNumberOfBytesRead += 1; let data = this.operatingDataView.getInt8(this.currentByte++); return data; } readUint8() { this.totalNumberOfBytesRead += 1; let data = this.operatingDataView.getUint8(this.currentByte++); return data; } readInt16() { this.totalNumberOfBytesRead += 2; let data = this.operatingDataView.getInt16(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 2; return data; } readUint16() { this.totalNumberOfBytesRead += 2; let data = this.operatingDataView.getUint16(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 2; return data; } readInt32() { this.totalNumberOfBytesRead += 4; let data = this.operatingDataView.getInt32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 4; return data; } readUint32() { this.totalNumberOfBytesRead += 4; let data = this.operatingDataView.getUint32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 4; return data; } readLong() { this.totalNumberOfBytesRead += 8; let data = this.operatingDataView.getBigInt64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 8; return data; } readInt64() { return this.readLong(); } readUint64() { this.totalNumberOfBytesRead += 8; let data = this.operatingDataView.getBigUint64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 8; return data; } readFloat32() { this.totalNumberOfBytesRead += 4; let data = this.operatingDataView.getFloat32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 4; return data; } readDouble() { this.totalNumberOfBytesRead += 8; let data = this.operatingDataView.getFloat64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 8; return data; } readString() { let strLength = this.readInt32(); if (strLength === 0) { return ''; } if (strLength > (this.operatingDataView.buffer.byteLength - this.currentByte)) { let errorMessage = `Cannot read string of length ${strLength} at position ${this.currentByte} as it exceeds the end at ${this.operatingDataView.buffer.byteLength}`; throw new Error(errorMessage); } if (strLength < 0) { const string = new Array(-strLength - 1).fill('').map(c => String.fromCharCode(this.readUint16())); this.currentByte += 2; this.totalNumberOfBytesRead += 2; return string.join(''); } try { const string = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.readUint8())); this.currentByte += 1; this.totalNumberOfBytesRead += 1; return string.join(''); } catch (error) { let errorMessage = `Cannot read UTF8 string of length ${strLength} at position ${this.currentByte}.`; console.log(errorMessage, error); throw error; } } getBufferPosition() { return this.totalNumberOfBytesRead; } getBufferProgress() { throw new parser_error_1.UnimplementedError(); } getBufferLength() { return this.operatingStreamBuffer.byteLength; } getAmountAllocatedLeft() { return this.operatingStreamBuffer.byteLength - this.currentByte; } } exports.ByteStreamReader = ByteStreamReader;