UNPKG

@etothepii/satisfactory-file-parser

Version:

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

226 lines (225 loc) 9.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamParserReader = void 0; const alignment_enum_1 = require("../byte/alignment.enum"); class StreamParserReader { constructor(minBufferSize) { this.minBufferSize = minBufferSize; this.alignment = alignment_enum_1.Alignment.LITTLE_ENDIAN; this.getAmountLeftToRead = () => { return this.view.byteLength - this.currentByte; }; this.parseLogic = async () => { console.log('before allocate 1', this.getAmountLeftToRead(), this.inputBuffer.length); await this.allocate(100000); const unknownStuff = [ this.readInt32(), this.readInt32(), this.readString(), this.readInt32(), this.readInt32(), this.readInt32(), this.readString() ]; console.log('before allocate 2', unknownStuff, this.getAmountLeftToRead(), this.inputBuffer.length); await this.allocate(10000); }; this.allocate = async (amount) => { if (amount > this.minBufferSize) { throw new Error(`Can not attempt to allocate (${amount}) more than the size of the buffer (${this.minBufferSize}) is.`); } if (this.getAmountLeftToRead() >= amount) { return Promise.resolve(); } return new Promise((resolve, reject) => { console.log('Waiting for allocate', amount, this.getAmountLeftToRead()); this.readTilBufferFullOrInputEmpty(); console.log('shift?', this.inputBuffer.length); if (this.inputBuffer.length > 0 && this.inputBuffer.length + this.getAmountLeftToRead() >= amount) { this.shiftBufferIntoOperatingDataView(); return resolve(); } this.onInputDataAvailableCallback = () => { console.log('on available callback.', this.inputBuffer.length, this.getAmountLeftToRead(), amount); if (this.inputBuffer.length > 0 && this.inputBuffer.length + this.getAmountLeftToRead() >= amount) { this.shiftBufferIntoOperatingDataView(); console.log(this.getAmountLeftToRead()); this.onInputDataAvailableCallback = undefined; return resolve(); } }; }); }; this.waitForAmountLeftToRead = async (amount, callback = () => { }) => { while (this.getAmountLeftToRead() < amount) { if (this.hasInputStreamEnded) { throw new Error(`Unexpected end of stream. Waited to read ${amount} but stream ended prematurely.`); } console.log('###waiting... for ', amount); this.readTilBufferFullOrInputEmpty(); if (this.inputBuffer.length > 0) { console.log('###shift.'); this.shiftBufferIntoOperatingDataView(); } await this.wait(20); } console.log('amount left to read is no longer smaller than', amount, this.getAmountLeftToRead()); callback(); }; this.wait = (ms) => new Promise((resolve, reject) => { setTimeout(() => { return resolve(ms); }, ms); }); this.shiftBufferIntoOperatingDataView = (amountOfTrailingBytesToKeep = 300) => { const resultingTrailingBytes = Math.min(this.currentByte, amountOfTrailingBytesToKeep); const trailingPlusLeftToRead = new Uint8Array(this.buffer.slice(this.currentByte - resultingTrailingBytes)); const { length, ...chunks } = this.inputBuffer; const inBuffer = new Uint8Array(Buffer.concat(Object.values(chunks))); this.inputBuffer = { length: 0 }; this.buffer = new Uint8Array(trailingPlusLeftToRead.byteLength + inBuffer.byteLength); this.buffer.set(trailingPlusLeftToRead, 0); this.buffer.set(inBuffer, trailingPlusLeftToRead.byteLength); this.currentByte = resultingTrailingBytes; this.view = new DataView(this.buffer.buffer); }; this.readTilBufferFullOrInputEmpty = () => { let chunk; console.log(this.hasDataToRead, this.inputBuffer.length, this.minBufferSize); while (this.hasDataToRead && this.inputBuffer.length < this.minBufferSize && (chunk = this.input.read()) != null) { console.log('reading chunk', chunk); this.inputBuffer[chunk.byteLength] = chunk; this.inputBuffer.length += chunk.length; } if (chunk === null) { console.log('chunk is null, input empty for now.'); this.hasDataToRead = false; } else if (this.inputBuffer.length >= this.minBufferSize) { console.log('buffer is full, ignoring for now.', this.inputBuffer.length); } console.log('data available callback?', this.onInputDataAvailableCallback); if (this.onInputDataAvailableCallback) { this.onInputDataAvailableCallback(); } }; this.buffer = new Uint8Array(0); this.view = new DataView(this.buffer.buffer); this.currentByte = 0; this.hasInputStreamEnded = false; this.inputBuffer = { length: 0 }; this.hasDataToRead = false; } startReading(input) { this.input = input; this.input.pause(); this.input.on('readable', () => { console.log('on readable. Reading til buffer full or input empty.'); this.hasDataToRead = true; this.readTilBufferFullOrInputEmpty(); }); this.input.on('end', () => { this.hasDataToRead = false; this.hasInputStreamEnded = true; }); this.input.on('close', () => { this.hasDataToRead = false; this.hasInputStreamEnded = true; }); } skipBytes(byteLength = 1) { this.currentByte += byteLength; return; } readByte() { return parseInt(this.view.getUint8(this.currentByte++).toString()); } readBytes(count) { return new Uint8Array(new Array(count).fill(0).map(pl => this.view.getUint8(this.currentByte++))); } readHex(hexLength) { let hexPart = []; for (let i = 0; i < hexLength; i++) { let currentHex = String.fromCharCode(this.view.getUint8(this.currentByte++)); hexPart.push(currentHex); } return hexPart.join(''); } readInt8() { let data = this.view.getInt8(this.currentByte++); return data; } readUint8() { let data = this.view.getUint8(this.currentByte++); return data; } readInt16() { let data = this.view.getInt16(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 2; return data; } readUint16() { let data = this.view.getUint16(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 2; return data; } readInt32() { let data = this.view.getInt32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 4; return data; } readUint32() { let data = this.view.getUint32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 4; return data; } readLong() { let data = this.view.getBigInt64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 8; return data; } readInt64() { return this.readLong(); } readUint64() { let data = this.view.getBigUint64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 8; return data; } readFloat32() { let data = this.view.getFloat32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN); this.currentByte += 4; return data; } readDouble() { let data = this.view.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.view.buffer.byteLength - this.currentByte)) { let errorMessage = `Cannot read string of length ${strLength} at position ${this.currentByte} as it exceeds the end at ${this.view.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; return string.join(''); } try { const string = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.readUint8())); this.currentByte += 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; } } } exports.StreamParserReader = StreamParserReader;