import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; class ByteLayer { Uint8List _byteBuffer = Uint8List(0); int _position = 0; ByteLayer() { _byteBuffer = Uint8List(0); // Initial size, can be adjusted _position = 0; } int get length => _byteBuffer.length; int get currentPosition => _position; Uint8List get bytes => _byteBuffer.sublist(0, _position); void _ensureCapacity(int additionalBytes) { final requiredCapacity = _position + additionalBytes; if (requiredCapacity > _byteBuffer.length) { final newCapacity = _position + additionalBytes; // Adjust capacity as needed final newBuffer = Uint8List(newCapacity); newBuffer.setAll(0, _byteBuffer); _byteBuffer = newBuffer; } } void writeInt(int value) { _ensureCapacity(4); _byteBuffer.buffer.asByteData().setInt32(_position, value, Endian.big); _position += 4; } void writeDouble(double value) { _ensureCapacity(8); _byteBuffer.buffer.asByteData().setFloat64(_position, value, Endian.big); _position += 8; } void writeString(String value) { final encoded = utf8.encode(value); writeShort(encoded.length); _ensureCapacity(encoded.length); _byteBuffer.setAll(_position, encoded); _position += encoded.length; } int readInt() { final value = _byteBuffer.buffer.asByteData().getInt32(_position, Endian.big); _position += 4; return value; } double readDouble() { final value = _byteBuffer.buffer.asByteData().getFloat64(_position, Endian.big); _position += 8; return value; } String readString() { final length = readShort(); final encoded = _byteBuffer.sublist(_position, _position + length); _position += length; return utf8.decode(encoded); } void writeIntZigZag(int value) { final zigzag = (value << 1) ^ (value >> 31); writeInt(zigzag); } int readIntZigZag() { final zigzag = readInt(); final value = (zigzag >> 1) ^ -(zigzag & 1); return value; } void writeByte(int value) { _ensureCapacity(1); _byteBuffer[_position] = value & 0xFF; _position++; } void writeUnsignedByte(int value) { _ensureCapacity(1); _byteBuffer.buffer.asByteData().setUint8(_position, value); _position++; } int readByte() { final value = _byteBuffer[_position]; _position++; return value; } int readUnsignedByte() { final value = _byteBuffer.buffer.asByteData().getUint8(_position); _position++; return value; } void writeVarInt(int value) { while ((value & ~0x7F) != 0) { writeByte((value & 0x7F) | 0x80); value = (value >> 7) & 0x1FFFFFFF; } writeByte(value & 0x7F); } int readVarInt() { int result = 0; int shift = 0; int byte; do { byte = readByte(); result |= (byte & 0x7F) << shift; if ((byte & 0x80) == 0) { break; } shift += 7; } while (true); return result; } void writeVarIntNoZigZag(int value) { while ((value & ~0x7F) != 0) { writeByte((value & 0x7F) | 0x80); value >>= 7; } writeByte(value & 0x7F); } int readVarIntNoZigZag() { int result = 0; int shift = 0; int byte; do { byte = readByte(); result |= (byte & 0x7F) << shift; if ((byte & 0x80) == 0) { break; } shift += 7; } while (true); return result; } void writeShort(int value) { _ensureCapacity(2); _byteBuffer.buffer.asByteData().setInt16(_position, value, Endian.big); _position += 2; } int readShort() { final value = _byteBuffer.buffer.asByteData().getInt16(_position, Endian.big); _position += 2; return value; } void writeFloat(double value) { _ensureCapacity(4); // Round the float to 8 decimal places before writing value = double.parse(value.toStringAsFixed(8)); _byteBuffer.buffer.asByteData().setFloat32(_position, value, Endian.big); _position += 4; } double readFloat() { final value = _byteBuffer.buffer.asByteData().getFloat32(_position, Endian.big); _position += 4; // Round the float to 8 decimal places after reading return double.parse(value.toStringAsFixed(8)); } void writeTagName(String name) { final encodedName = utf8.encode(name); writeShort(encodedName.length); _ensureCapacity(encodedName.length); _byteBuffer.setAll(_position, encodedName); _position += encodedName.length; } String readTagName() { final length = readShort(); final encodedName = _byteBuffer.sublist(_position, _position + length); _position += length; return utf8.decode(encodedName); } void resetPosition() { _position = 0; } void restorePosition(int position) { _position = position; } void clear() { resetPosition(); _byteBuffer = Uint8List(0); } Future writeToFile(String filePath) async { final file = File(filePath); try { if (file.existsSync()) file.deleteSync(); // Ensure we flush the file to 0 bytes } catch (E) { // If it fails then it fails, just fail silently, writing should still succeed. } var rac = await file.open(mode: FileMode.write); for (var byte in bytes) { await rac.writeByte(byte); } await rac.flush(); await rac.close(); } Future readFromFile(String filePath) async { final file = File(filePath); final exists = await file.exists(); if (!exists) { print('File does not exist.'); return; } RandomAccessFile rac = await file.open(mode: FileMode.read); var length = await rac.length(); _byteBuffer = await rac.read(length); await rac.close(); resetPosition(); } Future compress() async { final gzip = GZipCodec(); final compressedData = gzip.encode(_byteBuffer); _byteBuffer = Uint8List.fromList(compressedData); _position = _byteBuffer.length; } Future decompress() async { final gzip = GZipCodec(); final decompressedData = gzip.decode(_byteBuffer); _byteBuffer = Uint8List.fromList(decompressedData); _position = _byteBuffer.length; } void writeLong(int value) { _ensureCapacity(8); _byteBuffer.buffer.asByteData().setInt64(_position, value, Endian.big); _position += 8; } void writeUnsignedLong(int value) { _ensureCapacity(8); _byteBuffer.buffer.asByteData().setUint64(_position, value, Endian.big); _position += 8; } int readLong() { final value = _byteBuffer.buffer.asByteData().getInt64(_position, Endian.big); _position += 8; return value; } int readUnsignedLong() { final value = _byteBuffer.buffer.asByteData().getUint64(_position, Endian.big); _position += 8; return value; } void writeVarLongNoZigZag(int value) { while (true) { if ((value & ~0x7F) == 0) { writeByte(value); return; } writeByte((value & 0x7F) | 0x80); value = value >> 7; } } void writeVarLongZigZag(int value) { value = (value << 1) ^ (value >> 63); writeVarLongNoZigZag(value); } void writeLongZigZag(int value) { value = (value << 1) & (value >> 63); _byteBuffer.buffer.asByteData().setInt64(_position, value, Endian.big); } int readVarLongNoZigZag() { int result = 0; int shift = 0; int b; do { b = readByte(); result |= (b & 0x7F) << shift; shift += 7; } while ((b & 0x80) != 0); return result; } int readVarLongZigZag() { int result = readVarLongNoZigZag(); return (result >> 1) ^ -(result & 1); } int readLongZigZag() { int value = readLong(); return (value >> 1) ^ (-(value & 1)); } void writeBytes(Iterable bytes) { for (int byte in bytes) { writeUnsignedByte(byte); } } List readBytes(int num) { List lst = []; for (int i = 0; i < num; i++) { lst.add(readUnsignedByte()); } return lst; } void setBit(int position, int maskToSet) { if (position < _byteBuffer.length) { // Set the value now seek(position); int current = readUnsignedByte(); seek(position); current |= maskToSet; writeUnsignedByte(current); } } void clearBit(int position, int maskToClear) { if (position < _byteBuffer.length) { // Lets clear the bit seek(position); int current = readUnsignedByte(); current = current & ~maskToClear; seek(position); writeUnsignedByte(current); } } bool checkBit(int position, int mask) { if (position < _byteBuffer.length) { seek(position); int current = readUnsignedByte(); return (current & mask) == mask; } else { return false; } } int getBit(int position) { if (position < _byteBuffer.length) { seek(position); return readUnsignedByte(); } else { return 0; } } void seek(int position) { _position = 0; _ensureCapacity(position); _position = position; } /// Unsets a mask, then sets a mask void unsetSetBit(int position, int maskToClear, int maskToSet) { clearBit(position, maskToClear); setBit(position, maskToSet); } void insertRandomBytes(int count) { Random rng = Random(); for (int a = 0; a < count; a++) { writeByte(rng.nextInt(255)); } } } class StringBuilder { String _buffer = ""; StringBuilder(); bool get isEmpty => _buffer.isEmpty; void append(String value) { _buffer += value; } void clear() { _buffer = ""; } @override String toString() { return "${_buffer}"; } } class StringReader { final String _buffer; int _position = 0; int _lastPostion = 0; bool _quotedString = false; StringReader(this._buffer); /// Check if there's more to read bool get canRead => _canRead(); bool _canRead() { skipWhitespace(); return _position < _buffer.length; } // Get the number of chars seeked int get getSeeked => _lastPostion - _position; // Read the next character String next() { if (canRead) { skipWhitespace(); return _buffer[_position++]; } else { throw Exception("End of buffer reached"); } } /// Generates a snapshot of the text location if applicable String getSnapshot() { if (canRead) { if (_position + 64 < _buffer.length) { return _buffer.substring(_position, _position + 64); } else { return _buffer.substring(_position); } } else return ""; } // Peek the next character without advancing the position String peek() { skipWhitespace(); if (canRead) { return _buffer[_position]; } else { throw Exception("End of buffer reached"); } } // Skip any whitespace characters void skipWhitespace() { if (_quotedString) return; // We need whitespace for strings while ((_position < _buffer.length) && isWhitespace(_buffer[_position])) { _position++; } } // Check if a character is a whitespace bool isWhitespace(String char) { return char.trim().isEmpty; } // Read until a specific character is found String readUntil(String stopChar, int maxChars) { StringBuffer result = StringBuffer(); int index = 0; while (canRead && peek() != stopChar && index < maxChars) { result.write(next()); index++; } return result.toString(); } /// Read a string enclosed in double quotes String readQuotedString() { _quotedString = true; var nxtChar = next(); if (nxtChar != '"' && nxtChar != "'") { throw Exception( 'Expected double quotes or single quotes at the start of a string'); } StringBuffer result = StringBuffer(); bool escaping = false; String quoteDigit = nxtChar; while (canRead) { String char = next(); if (char == '"' && quoteDigit == "\"") { break; } else if (char == '\\' && peek() == '\'' && quoteDigit == '\'') { escaping = true; continue; } else if (char == '\'' && quoteDigit == '\'') { if (!escaping) break; } escaping = false; result.write(char); } _quotedString = false; return result.toString(); } // Read a number (int or double) String readNumber() { StringBuffer result = StringBuffer(); while (canRead && (isDigit(peek()) || peek() == '.' || peek() == '-' || peek() == "+")) { result.write(next()); } return result.toString(); } // Check if a character is a digit bool isDigit(String char) { return int.tryParse(char) != null; } // Read an unquoted string (used for keys in SNBT) String readUnquotedString() { StringBuffer result = StringBuffer(); while (canRead && !isWhitespace(peek()) && peek() != ':' && peek() != ',' && peek() != '{' && peek() != '}' && peek() != '[' && peek() != ']') { result.write(next()); } return result.toString(); } String readString() { if (peek() == "\"" || peek() == "'") { return readQuotedString(); } else return readUnquotedString(); } /// Read a specific character and throw an exception if it's not found /// /// Parameter is case-insensitive void expect(String expectedChar) { if (next().toLowerCase() != expectedChar.toLowerCase()) { throw Exception('Expected $expectedChar'); } } void startSeek() { _lastPostion = _position; } void endSeek() { _position = _lastPostion; _lastPostion = 0; } @override String toString() { // Returns the entire value starting from position return _buffer.substring(_position); } }