using CSNetwork.Protocols; using System; using System.Collections.Generic; using System.IO; using System.Net; // using System.Reflection.PortableExecutable; using System.Text; using static CSNetwork.Protocols.selectrole; namespace CSNetwork { public class OctetsStream : IDisposable { private Octets _octets; private int _position; private static readonly Encoding GbkEncoding = Encoding.GetEncoding(936); private const int MaxSpare = 16384; private int _transactionPosition; public enum Transaction { Begin, Commit, Rollback } public int Length => _octets.Length; // Position within the stream for reading/writing public int Position { get => _position; set { // Basic bounds check, could be more robust if (value < 0 || value > _octets.Length) throw new ArgumentOutOfRangeException(nameof(value), "Position is outside the bounds of the stream."); _position = value; } } // Access to the underlying Octets object public Octets UnderlyingOctets => _octets; // Access to the raw buffer for in-place operations (like ISecurity.Update) public byte[] RawBuffer => _octets.RawBuffer; public bool IsEof => _position >= _octets.Length; public OctetsStream(Octets octets = null) { _octets = octets ?? new Octets(); _position = 0; } // Helper methods for big-endian conversion private byte[] GetBigEndianBytes(ushort value) { if (BitConverter.IsLittleEndian) { return new[] { (byte)(value >> 8), (byte)value }; } return BitConverter.GetBytes(value); } private byte[] GetBigEndianBytes(uint value) { if (BitConverter.IsLittleEndian) { return new[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; } return BitConverter.GetBytes(value); } // Primitive type support public void Write(byte value) => _octets.Insert(_position++, new[] { value }); public void Write(short value) { var bytes = GetBigEndianBytes((ushort)value); _octets.Insert(_position, bytes); _position += 2; } public void Write(ushort value) { var bytes = GetBigEndianBytes(value); _octets.Insert(_position, bytes); _position += 2; } public void Write(int value) { var bytes = GetBigEndianBytes((uint)value); _octets.Insert(_position, bytes); _position += 4; } public void Write(uint value) { var bytes = GetBigEndianBytes(value); _octets.Insert(_position, bytes); _position += 4; } /// Write int32 in little-endian (for GNET protocol compatibility with C++ client/server). public void WriteInt32LE(int value) { var bytes = BitConverter.GetBytes(value); _octets.Insert(_position, bytes); _position += 4; } /// Read int32 in little-endian (for GNET protocol compatibility with C++ client/server). public int ReadInt32LE() { if (_position + 4 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(4); return BitConverter.ToInt32(bytes, 0); } public void Write(float value) { var bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); _octets.Insert(_position, bytes); _position += 4; } public void Write(double value) { var bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); _octets.Insert(_position, bytes); _position += 8; } // Read methods for primitive types public byte ReadByte() { if (_position + 1 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); return ReadBytes(1)[0]; } public short ReadInt16() { if (_position + 2 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(2); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToInt16(bytes, 0); } public ushort ReadUInt16() { if (_position + 2 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(2); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToUInt16(bytes, 0); } public int ReadInt32() { if (_position + 4 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(4); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToInt32(bytes, 0); } public uint ReadUInt32() { if (_position + 4 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(4); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToUInt32(bytes, 0); } public float ReadFloat() { if (_position + 4 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(4); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToSingle(bytes, 0); } public double ReadDouble() { if (_position + 8 > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var bytes = ReadBytes(8); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return BitConverter.ToDouble(bytes, 0); } // Compact integer support public void WriteCompactInt(int value) { if (value >= 0) { WriteCompactUInt((uint)value); } else { uint unsignedValue = (uint)(-value); unsignedValue = (unsignedValue << 1) | 1; WriteCompactUInt(unsignedValue); } } public int ReadCompactInt() { uint value = ReadCompactUInt(); if ((value & 1) == 0) return (int)(value >> 1); else return -(int)(value >> 1); } // Transaction support public void HandleTransaction(Transaction trans) { switch (trans) { case Transaction.Begin: _transactionPosition = _position; break; case Transaction.Rollback: _position = _transactionPosition; break; case Transaction.Commit: if (_position >= MaxSpare) { // Optimize memory usage var newOctets = new byte[_octets.Length - _position]; Array.Copy(_octets.ToArray(), _position, newOctets, 0, newOctets.Length); _octets.Dispose(); _octets = new Octets(newOctets.Length); _position = 0; } break; } } // Container support public void WriteList(IList list) where T : IMarshallable { WriteCompactUInt((uint)list.Count); foreach (var item in list) { item.Marshal(this); } } public void ReadList(IList list) where T : IMarshallable, new() { uint count = ReadCompactUInt(); for (int i = 0; i < count; i++) { var item = new T(); item.Unmarshal(this); list.Add(item); } } public void WriteListInt(IList listInt) { if (listInt?.Count <= 0) return; WriteCompactUInt((uint)listInt.Count); foreach(var item in listInt) { Write(item); } } public void ReadListInt(IList listInt) { uint count = ReadCompactUInt(); for (int i = 0; i < count; i++) { listInt.Add(ReadInt32()); } } public void WriteCompactUInt(uint value) { if (value < 0x80) { _octets.Insert(_position, new[] { (byte)value }); _position++; } else if (value < 0x4000) { var bytes = GetBigEndianBytes((ushort)(value | 0x8000)); _octets.Insert(_position, bytes); _position += 2; } else if (value < 0x20000000) { var bytes = GetBigEndianBytes(value | 0xc0000000); _octets.Insert(_position, bytes); _position += 4; } else { _octets.Insert(_position, new[] { (byte)0xe0 }); _octets.Insert(_position + 1, GetBigEndianBytes(value)); _position += 5; } } public void GetFirstBytes() { IPrefixedLogger _logger = LoggerFactory.GetLogger(nameof(Protocol)); var tempBytes = ReadByteWithoutAdvancePos(41); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 41; i++) { sb.Append(tempBytes[i]); sb.Append(' '); } _logger.Debug($"Try Decode: Protocol 10 first bytes : {sb}"); } public uint ReadCompactUInt() { byte flag = ReadBytes(1)[0]; IPrefixedLogger _logger = LoggerFactory.GetLogger(nameof(Protocol)); if ((flag & 0xE0) == 0xE0) { var bytes = ReadBytes(4); return BitConverter.IsLittleEndian ? ((uint)bytes[0] << 24) | ((uint)bytes[1] << 16) | ((uint)bytes[2] << 8) | bytes[3] : BitConverter.ToUInt32(bytes, 0); } else if ((flag & 0xE0) == 0xC0) { var bytes = ReadBytes(3); uint value = 0; if (BitConverter.IsLittleEndian) { value = ((uint)bytes[0] << 16) | ((uint)bytes[1] << 8) | bytes[2]; } else { value = BitConverter.ToUInt32(bytes, 0); } return (value | ((uint)flag << 24)) & ~0xC0000000; } else if ((flag & 0xE0) == 0x80 || (flag & 0xE0) == 0xA0) { var bytes = ReadBytes(1); return (uint)((bytes[0] | (flag << 8)) & ~0x8000); } return flag; } public void WriteString(string value) { var bytes = GbkEncoding.GetBytes(value); WriteCompactUInt((uint)bytes.Length); _octets.Insert(_position, bytes); _position += bytes.Length; } public string ReadString() { uint length = ReadCompactUInt(); var bytes = ReadBytes((int)length); return GbkEncoding.GetString(bytes); } public byte[] ReadBytes(int count) { if (_position + count > _octets.Length) throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream."); var result = new byte[count]; Array.Copy(_octets.ToArray(), _position, result, 0, count); _position += count; return result; } public byte[] ReadByteWithoutAdvancePos(int count) { if (_position + count > _octets.Length) return new byte[count]; var result = new byte[count]; Array.Copy(_octets.ToArray(), _position, result, 0, count); return result; } public byte[] ToArray() => _octets.ToArray(); public void Dispose() => _octets?.Dispose(); public void WriteBytes(byte[] data) { _octets.Insert(_position, data); _position += data.Length; } // Add these methods for Octets support public void Write(Octets value) { WriteCompactUInt((uint)value.Size); WriteBytes(value.ToArray()); } public Octets ReadOctets() { uint size = ReadCompactUInt(); byte[] data = ReadBytes((int)size); return new Octets(data); } public void Write(List list) { WriteCompactUInt((uint)list.Count); foreach (var octets in list) { Write(octets); } } public List ReadOctetsList() { uint count = ReadCompactUInt(); var list = new List(); for (int i = 0; i < count; i++) { list.Add(ReadOctets()); } return list; } // Erase a section of the underlying buffer public void Erase(int pos, int length) { _octets.Erase(pos, length); // Adjust position if the erasure happened before the current position if (pos < _position) { _position = Math.Max(pos, _position - length); } } // Compact the stream by removing processed data (0 to Position) public void Compact() { if (_position > 0) { _octets.Erase(0, _position); _position = 0; // Reset position after compacting } } } }