using Supercell.GUT.Titan.Math; using System.Buffers.Binary; using System.Runtime.CompilerServices; using TextEncoding = System.Text.Encoding; namespace Supercell.GUT.Titan.Encoding.Streamed; public class ByteStream : ChecksumEncoder { public byte[] Buffer { get; set; } private int _bitIndex; private int _length; public int Offset { get; set; } public int Length => _length > 0 ? _length : Offset; public ByteStream(byte[] buffer, int length) { Buffer = buffer; _length = length; } public ByteStream(int capacity) { Buffer = new byte[capacity]; } public ByteStream() { Buffer = new byte[10]; } public void SetByteArray(byte[] buffer, int length) { Buffer = buffer; _length = length; } public bool ReadBoolean() { if (_bitIndex == 0) ++Offset; bool value = (Buffer[Offset - 1] & 1 << _bitIndex) != 0; _bitIndex = _bitIndex + 1 & 7; return value; } public byte ReadByte() { _bitIndex = 0; return Buffer[Offset++]; } public short ReadShort() { _bitIndex = 0; short ret = BinaryPrimitives.ReadInt16BigEndian(Buffer.AsSpan()[Offset..]); Offset += 2; return ret; } public int ReadInt() { _bitIndex = 0; int ret = BinaryPrimitives.ReadInt32BigEndian(Buffer.AsSpan()[Offset..]); Offset += 4; return ret; } public int ReadVInt() { _bitIndex = 0; int value = 0; byte byteValue = Buffer[Offset++]; if ((byteValue & 0x40) != 0) { value |= byteValue & 0x3F; if ((byteValue & 0x80) != 0) { value |= ((byteValue = Buffer[Offset++]) & 0x7F) << 6; if ((byteValue & 0x80) != 0) { value |= ((byteValue = Buffer[Offset++]) & 0x7F) << 13; if ((byteValue & 0x80) != 0) { value |= ((byteValue = Buffer[Offset++]) & 0x7F) << 20; if ((byteValue & 0x80) != 0) { value |= ((_ = Buffer[Offset++]) & 0x7F) << 27; return (int)(value | 0x80000000); } return (int)(value | 0xF8000000); } return (int)(value | 0xFFF00000); } return (int)(value | 0xFFFFE000); } return (int)(value | 0xFFFFFFC0); } value |= byteValue & 0x3F; if ((byteValue & 0x80) != 0) { value |= ((byteValue = Buffer[Offset++]) & 0x7F) << 6; if ((byteValue & 0x80) != 0) { value |= ((byteValue = Buffer[Offset++]) & 0x7F) << 13; if ((byteValue & 0x80) != 0) { value |= ((byteValue = Buffer[Offset++]) & 0x7F) << 20; if ((byteValue & 0x80) != 0) { value |= ((_ = Buffer[Offset++]) & 0x7F) << 27; } } } } return value; } public LogicLong ReadLong() { LogicLong ll = new(); ll.Decode(this); return ll; } public string? ReadString() { int length = ReadInt(); if (length is < 0 or > 900000) return null; else if (length == 0) return string.Empty; string ret = TextEncoding.UTF8.GetString(Buffer, Offset, length); Offset += length; return ret; } public string ReadStringReference() { int length = ReadInt(); if (length is <= 0 or > 900000) return string.Empty; string ret = TextEncoding.UTF8.GetString(Buffer, Offset, length); Offset += length; return ret; } public byte[] ReadBytes(int length) { _bitIndex = 0; byte[] ret = Buffer.Skip(Offset).Take(length).ToArray(); Offset += length; return ret; } public override ChecksumEncoder WriteBoolean(bool value) { base.WriteBoolean(value); if (_bitIndex == 0) { EnsureCapacity(1); Buffer[Offset++] = 0; } Buffer[Offset - 1] |= (byte)(value ? 1 : 0); _bitIndex = _bitIndex + 1 & 7; return this; } public override ChecksumEncoder WriteByte(byte value) { base.WriteByte(value); EnsureCapacity(1); Buffer[Offset++] = value; return this; } public override ChecksumEncoder WriteShort(short value) { base.WriteShort(value); EnsureCapacity(2); BinaryPrimitives.WriteInt16BigEndian(Buffer.AsSpan()[Offset..], value); Offset += 2; return this; } public override ChecksumEncoder WriteInt(int value) { base.WriteInt(value); EnsureCapacity(4); WriteIntToByteArray(value); return this; } public override ChecksumEncoder WriteString(string? value) { base.WriteString(value); if (value == null) { WriteIntToByteArray(-1); return this; } int size = TextEncoding.UTF8.GetByteCount(value); WriteIntToByteArray(size); EnsureCapacity(size); TextEncoding.UTF8.GetBytes(value, Buffer.AsSpan()[Offset..]); Offset += size; return this; } public override ChecksumEncoder WriteBytes(ReadOnlySpan value) { base.WriteBytes(value); WriteIntToByteArray(value.Length); EnsureCapacity(value.Length); value.CopyTo(Buffer.AsSpan()[Offset..]); Offset += value.Length; return this; } public override bool IsCheckSumOnlyMode => false; [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteIntToByteArray(int value) { EnsureCapacity(4); BinaryPrimitives.WriteInt32BigEndian(Buffer.AsSpan()[Offset..], value); Offset += 4; } public void EnsureCapacity(int capacity) { _bitIndex = 0; int bufferLength = Buffer.Length; if (Offset + capacity > bufferLength) { byte[] tmpBuffer = new byte[Buffer.Length + capacity + 100]; System.Buffer.BlockCopy(Buffer, 0, tmpBuffer, 0, bufferLength); Buffer = tmpBuffer; } } public bool IsAtEnd() { return Offset >= Buffer.Length; } public void ResetOffset() { this.Offset = 0; } public void Destruct() { ; } }