LibZNI/NBT/API/ByteLayer.cs

358 lines
9.6 KiB
C#

using System;
using System.IO;
using System.Text;
using System.Linq;
using System.IO.Compression;
using System.Collections.Generic;
public class ByteLayer
{
private byte[] _byteBuffer;
private int _position;
public ByteLayer()
{
_byteBuffer = new byte[0];
_position = 0;
}
public int Length => _byteBuffer.Length;
public int CurrentPosition => _position;
public byte[] Bytes => _byteBuffer.Take(_position).ToArray();
private void EnsureCapacity(int additionalBytes)
{
int requiredCapacity = _position + additionalBytes;
if (requiredCapacity > _byteBuffer.Length)
{
Array.Resize(ref _byteBuffer, requiredCapacity);
}
}
public void WriteInt(int value)
{
EnsureCapacity(4);
Array.Copy(BitConverter.GetBytes(value), 0, _byteBuffer, _position, 4);
if (BitConverter.IsLittleEndian)
Array.Reverse(_byteBuffer, _position, 4);
_position += 4;
}
public int ReadInt()
{
if (_position + 4 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
var value = _byteBuffer.Skip(_position).Take(4).ToArray();
if (BitConverter.IsLittleEndian) Array.Reverse(value);
_position += 4;
return BitConverter.ToInt32(value, 0);
}
public void WriteDouble(double value)
{
EnsureCapacity(8);
var bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
Array.Copy(bytes, 0, _byteBuffer, _position, 8);
_position += 8;
}
public double ReadDouble()
{
if (_position + 8 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
var value = _byteBuffer.Skip(_position).Take(8).ToArray();
if (BitConverter.IsLittleEndian) Array.Reverse(value);
_position += 8;
return BitConverter.ToDouble(value, 0);
}
public void WriteFloat(float value)
{
EnsureCapacity(4);
var bytes = BitConverter.GetBytes(value);
if(BitConverter.IsLittleEndian)
Array.Reverse(bytes);
Array.Copy(bytes, 0, _byteBuffer, _position, 4);
_position += 4;
}
public float ReadFloat()
{
if (_position + 4 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
var value = _byteBuffer.Skip(_position).Take(4).ToArray();
if (BitConverter.IsLittleEndian) Array.Reverse(value);
_position += 4;
return BitConverter.ToSingle(value, 0);
}
public void WriteString(string value)
{
var encoded = Encoding.UTF8.GetBytes(value);
WriteShort((short)encoded.Length);
EnsureCapacity(encoded.Length);
Array.Copy(encoded, 0, _byteBuffer, _position, encoded.Length);
_position += encoded.Length;
}
public string ReadString()
{
int length = ReadShort();
if (_position + length > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
var value = Encoding.UTF8.GetString(_byteBuffer, _position, length);
_position += length;
return value;
}
public void WriteShort(short value)
{
EnsureCapacity(2);
var bytes = BitConverter.GetBytes((short)value);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
Array.Copy(bytes, 0, _byteBuffer, _position, 2);
_position += 2;
}
public short ReadShort()
{
if (_position + 2 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
var value = _byteBuffer.Skip(_position).Take(2).ToArray();
if (BitConverter.IsLittleEndian) Array.Reverse(value);
_position += 2;
return BitConverter.ToInt16(value, 0);
}
public void WriteByte(byte value)
{
EnsureCapacity(1);
_byteBuffer[_position] = value;
_position++;
}
public void WriteBytes(List<byte> bytes)
{
EnsureCapacity(bytes.Count);
Array.Copy(bytes.ToArray(), 0, _byteBuffer, _position, bytes.Count);
_position += bytes.Count;
}
public byte ReadByte()
{
if (_position >= _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
return _byteBuffer[_position++];
}
public void ResetPosition() => _position = 0;
public void RestorePosition(int position)
{
if (position < 0 || position > _byteBuffer.Length)
throw new ArgumentOutOfRangeException(nameof(position));
_position = position;
}
public void Clear()
{
_position = 0;
_byteBuffer = new byte[0];
}
public void WriteToFile(string filePath)
{
File.WriteAllBytes(filePath, Bytes);
}
public void ReadFromFile(string filePath)
{
if (!File.Exists(filePath)) throw new FileNotFoundException("File does not exist.", filePath);
_byteBuffer = File.ReadAllBytes(filePath);
ResetPosition();
}
public void Compress()
{
using var memoryStream = new MemoryStream();
using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress))
{
gzipStream.Write(_byteBuffer, 0, _byteBuffer.Length);
}
_byteBuffer = memoryStream.ToArray();
_position = _byteBuffer.Length;
}
public void Decompress()
{
using var memoryStream = new MemoryStream(_byteBuffer);
using var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress);
using var decompressedStream = new MemoryStream();
gzipStream.CopyTo(decompressedStream);
_byteBuffer = decompressedStream.ToArray();
_position = _byteBuffer.Length;
}
public void WriteVarInt(int value)
{
while ((value & ~0x7F) != 0)
{
WriteByte((byte)((value & 0x7F) | 0x80));
value >>= 7;
}
WriteByte((byte)(value & 0x7F));
}
public int ReadVarInt()
{
int result = 0;
int shift = 0;
int byteRead;
do
{
byteRead = ReadByte();
result |= (byteRead & 0x7F) << shift;
shift += 7;
} while ((byteRead & 0x80) != 0);
return result;
}
public void WriteVarIntNoZigZag(int value)
{
while ((value & ~0x7F) != 0)
{
WriteByte((byte)((value & 0x7F) | 0x80));
value >>= 7;
}
WriteByte((byte)(value & 0x7F));
}
public int ReadVarIntNoZigZag()
{
int result = 0;
int shift = 0;
int byteRead;
do
{
byteRead = ReadByte();
result |= (byteRead & 0x7F) << shift;
shift += 7;
} while ((byteRead & 0x80) != 0);
return result;
}
public void WriteLong(long value)
{
EnsureCapacity(8);
Array.Copy(BitConverter.GetBytes(value), 0, _byteBuffer, _position, 8);
if (BitConverter.IsLittleEndian)
Array.Reverse(_byteBuffer, _position, 8);
_position += 8;
}
public long ReadLong()
{
if (_position + 8 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow.");
var value = _byteBuffer.Skip(_position).Take(8).ToArray();
if (BitConverter.IsLittleEndian) Array.Reverse(value);
_position += 8;
return BitConverter.ToInt64(value, 0);
}
public void WriteVarLongZigZag(long value)
{
value = (value << 1) ^ (value >> 63);
WriteVarLongNoZigZag(value);
}
public void WriteVarLongNoZigZag(long value)
{
while ((value & ~0x7F) != 0)
{
WriteByte((byte)((value & 0x7F) | 0x80));
value >>= 7;
}
WriteByte((byte)(value & 0x7F));
}
public long ReadVarLongZigZag()
{
long result = ReadVarLongNoZigZag();
return (result >> 1) ^ -(result & 1);
}
public long ReadVarLongNoZigZag()
{
long result = 0;
int shift = 0;
int byteRead;
do
{
byteRead = ReadByte();
result |= (byteRead & 0x7F) << shift;
shift += 7;
} while ((byteRead & 0x80) != 0);
return result;
}
public void SetBit(int position, byte maskToSet)
{
if (position < _byteBuffer.Length)
{
Seek(position);
byte current = ReadByte();
Seek(position);
current |= maskToSet;
WriteByte(current);
}
}
public void ClearBit(int position, byte maskToClear)
{
if (position < _byteBuffer.Length)
{
Seek(position);
byte current = ReadByte();
current &= (byte)~maskToClear;
Seek(position);
WriteByte(current);
}
}
public bool CheckBit(int position, byte mask)
{
if (position < _byteBuffer.Length)
{
Seek(position);
byte current = ReadByte();
return (current & mask) == mask;
}
return false;
}
public byte GetBit(int position)
{
if (position < _byteBuffer.Length)
{
Seek(position);
return ReadByte();
}
return 0;
}
public void Seek(int position)
{
if (position < 0 || position > _byteBuffer.Length)
throw new ArgumentOutOfRangeException(nameof(position));
_position = position;
}
public void InsertRandomBytes(int count)
{
Random rng = new Random();
for (int i = 0; i < count; i++)
{
WriteByte((byte)rng.Next(256));
}
}
}