2023-01-31 20:23:52 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using GTA.Math;
|
|
|
|
|
|
|
|
|
|
namespace RageCoop.Core
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
public unsafe abstract class Buffer
|
2023-01-31 20:23:52 +08:00
|
|
|
|
{
|
2023-02-01 21:21:07 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Size of this buffer in memory
|
|
|
|
|
/// </summary>
|
2023-01-31 20:23:52 +08:00
|
|
|
|
public int Size { get; protected set; }
|
2023-02-01 21:21:07 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The current read/write index
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int Position { get; protected set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Pointer to the start of this buffer
|
|
|
|
|
/// </summary>
|
2023-01-31 20:23:52 +08:00
|
|
|
|
public byte* Address { get; protected set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Ensure memory safety and advance position by specified number of bytes
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="cbSize"></param>
|
|
|
|
|
/// <returns>Pointer to the current position in the buffer</returns>
|
|
|
|
|
protected abstract byte* Alloc(int cbSize);
|
|
|
|
|
|
|
|
|
|
protected T* Alloc<T>(int count = 1) where T : unmanaged
|
|
|
|
|
=> (T*)Alloc(count * sizeof(T));
|
2023-02-01 21:21:07 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reset position to the start of this buffer
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
Position = 0;
|
|
|
|
|
}
|
2023-01-31 20:23:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-03 13:37:20 +08:00
|
|
|
|
public unsafe sealed class BufferWriter : Buffer
|
2023-01-31 20:23:52 +08:00
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
public BufferWriter(int size)
|
2023-01-31 20:23:52 +08:00
|
|
|
|
{
|
|
|
|
|
Resize(size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Resize(int size)
|
|
|
|
|
{
|
|
|
|
|
if (size < Size)
|
|
|
|
|
{
|
|
|
|
|
Size = size;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var newAddr = (byte*)Marshal.AllocHGlobal(size);
|
|
|
|
|
if (Address != null)
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
System.Buffer.MemoryCopy(Address, newAddr, size, Size);
|
2023-01-31 20:23:52 +08:00
|
|
|
|
Marshal.FreeHGlobal((IntPtr)Address);
|
|
|
|
|
}
|
|
|
|
|
Size = size;
|
|
|
|
|
Address = newAddr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override byte* Alloc(int cbSize)
|
|
|
|
|
{
|
2023-02-01 21:21:07 +08:00
|
|
|
|
var index = Position;
|
|
|
|
|
Position += cbSize;
|
2023-01-31 20:23:52 +08:00
|
|
|
|
|
|
|
|
|
// Resize the buffer by at least 50% if there's no sufficient space
|
2023-02-01 21:21:07 +08:00
|
|
|
|
if (Position > Size)
|
|
|
|
|
Resize(Math.Max(Position + 1, (int)(Size * 1.5f)));
|
2023-01-31 20:23:52 +08:00
|
|
|
|
|
|
|
|
|
return Address + index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write<T>(ref T value) where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var addr = Alloc<T>();
|
|
|
|
|
*addr = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// For passing struct smaller than word size (4/8 bytes on 32/64 bit system)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
public void WriteVal<T>(T value) where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var addr = Alloc<T>();
|
|
|
|
|
*addr = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void Write(ReadOnlySpan<char> str)
|
|
|
|
|
{
|
|
|
|
|
// Prefixed by its size in bytes
|
|
|
|
|
var cbBody = Encoding.UTF8.GetByteCount(str);
|
|
|
|
|
WriteVal(cbBody);
|
|
|
|
|
|
|
|
|
|
// Allocate and write string body
|
|
|
|
|
var pBody = Alloc(cbBody);
|
|
|
|
|
Encoding.UTF8.GetBytes(str, new(pBody, cbBody));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Struct in GTA.Math have pack/padding for memory alignment, we don't want it to waste the bandwidth
|
|
|
|
|
|
|
|
|
|
public void Write(ref Vector2 vec2)
|
|
|
|
|
{
|
|
|
|
|
var faddr = Alloc<float>(2);
|
|
|
|
|
faddr[0] = vec2.X;
|
|
|
|
|
faddr[1] = vec2.Y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(ref Vector3 vec3)
|
|
|
|
|
{
|
|
|
|
|
var faddr = Alloc<float>(3);
|
|
|
|
|
faddr[0] = vec3.X;
|
|
|
|
|
faddr[1] = vec3.Y;
|
|
|
|
|
faddr[2] = vec3.Z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(ref Quaternion quat)
|
|
|
|
|
{
|
|
|
|
|
var faddr = Alloc<float>(4);
|
|
|
|
|
faddr[0] = quat.X;
|
|
|
|
|
faddr[1] = quat.Y;
|
|
|
|
|
faddr[2] = quat.Z;
|
|
|
|
|
faddr[3] = quat.W;
|
|
|
|
|
}
|
2023-02-01 21:21:07 +08:00
|
|
|
|
|
|
|
|
|
public void Write<T>(ReadOnlySpan<T> source) where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var len = source.Length;
|
|
|
|
|
fixed (T* pSource = source)
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
System.Buffer.MemoryCopy(pSource, Alloc(sizeof(T) * len), len, len);
|
2023-02-01 21:21:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write<T>(Span<T> source) where T : unmanaged => Write((ReadOnlySpan<T>)source);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2023-02-03 13:37:20 +08:00
|
|
|
|
/// Write an array, prefix the data with its length so it can latter be read using <see cref="BufferReader.ReadArray{T}"/>
|
2023-02-01 21:21:07 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
public void WriteArray<T>(T[] values) where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var len = values.Length;
|
|
|
|
|
WriteVal(len);
|
|
|
|
|
fixed (T* pFrom = values)
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
System.Buffer.MemoryCopy(pFrom, Alloc(sizeof(T) * len), len, len);
|
2023-02-01 21:21:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Allocate a byte array on managed heap and copy the data of specified size to it
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="cbSize"></param>
|
|
|
|
|
/// <returns>The newly created managed byte array</returns>
|
|
|
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
|
|
|
public byte[] ToByteArray(int cbSize)
|
|
|
|
|
{
|
|
|
|
|
if (cbSize > Size)
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(cbSize));
|
|
|
|
|
|
|
|
|
|
var result = new byte[cbSize];
|
|
|
|
|
fixed (byte* pResult = result)
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
System.Buffer.MemoryCopy(Address, pResult, cbSize, cbSize);
|
2023-02-01 21:21:07 +08:00
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Free the associated memory allocated on the unmanaged heap
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Free() => Marshal.FreeHGlobal((IntPtr)Address);
|
2023-01-31 20:23:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-03 13:37:20 +08:00
|
|
|
|
public unsafe sealed class BufferReader : Buffer
|
2023-01-31 20:23:52 +08:00
|
|
|
|
{
|
2023-02-01 21:21:07 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initialize an empty instance, needs to call <see cref="Initialise(byte*, int)"/> before reading data
|
|
|
|
|
/// </summary>
|
2023-02-03 13:37:20 +08:00
|
|
|
|
public BufferReader()
|
2023-02-01 21:21:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
2023-02-03 13:37:20 +08:00
|
|
|
|
public BufferReader(byte* address, int size) => Initialise(address, size);
|
2023-02-01 21:21:07 +08:00
|
|
|
|
|
|
|
|
|
public void Initialise(byte* address, int size)
|
2023-01-31 20:23:52 +08:00
|
|
|
|
{
|
|
|
|
|
Address = address;
|
|
|
|
|
Size = size;
|
2023-02-01 21:21:07 +08:00
|
|
|
|
Reset();
|
2023-01-31 20:23:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override byte* Alloc(int cbSize)
|
|
|
|
|
{
|
2023-02-01 21:21:07 +08:00
|
|
|
|
if (Address == null)
|
|
|
|
|
throw new NullReferenceException("Address is null");
|
|
|
|
|
|
|
|
|
|
var index = Position;
|
|
|
|
|
Position += cbSize;
|
2023-01-31 20:23:52 +08:00
|
|
|
|
|
2023-02-01 21:21:07 +08:00
|
|
|
|
if (Position > Size)
|
2023-01-31 20:23:52 +08:00
|
|
|
|
throw new InvalidOperationException("Attempting to read beyond the existing buffer");
|
|
|
|
|
|
|
|
|
|
return Address + index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public T ReadVal<T>() where T : unmanaged => *Alloc<T>();
|
|
|
|
|
|
|
|
|
|
public void Read<T>(out T result) where T : unmanaged
|
|
|
|
|
=> result = *Alloc<T>();
|
|
|
|
|
|
|
|
|
|
public void Read(out string str)
|
|
|
|
|
{
|
|
|
|
|
var cbBody = ReadVal<int>();
|
|
|
|
|
str = Encoding.UTF8.GetString(Alloc(cbBody), cbBody);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Read(out Vector2 vec)
|
|
|
|
|
{
|
|
|
|
|
var faddr = Alloc<float>(2);
|
|
|
|
|
vec = new()
|
|
|
|
|
{
|
|
|
|
|
X = faddr[0],
|
|
|
|
|
Y = faddr[1],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Read(out Vector3 vec)
|
|
|
|
|
{
|
|
|
|
|
var faddr = Alloc<float>(3);
|
|
|
|
|
vec = new()
|
|
|
|
|
{
|
|
|
|
|
X = faddr[0],
|
|
|
|
|
Y = faddr[1],
|
|
|
|
|
Z = faddr[2],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Read(out Quaternion quat)
|
|
|
|
|
{
|
|
|
|
|
var faddr = Alloc<float>(4);
|
|
|
|
|
quat = new()
|
|
|
|
|
{
|
|
|
|
|
X = faddr[0],
|
|
|
|
|
Y = faddr[1],
|
|
|
|
|
Z = faddr[2],
|
|
|
|
|
W = faddr[3],
|
|
|
|
|
};
|
|
|
|
|
}
|
2023-02-01 21:21:07 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Read a span of type <typeparamref name="T"/> from current position to <paramref name="destination"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="destination"></param>
|
|
|
|
|
public void Read<T>(Span<T> destination) where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var len = destination.Length;
|
|
|
|
|
fixed (T* pTo = destination)
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
System.Buffer.MemoryCopy(Alloc(len * sizeof(T)), pTo, len, len);
|
2023-02-01 21:21:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2023-02-03 13:37:20 +08:00
|
|
|
|
/// Reads an array previously written using <see cref="BufferWriter.WriteArray{T}(T[])"/>
|
2023-02-01 21:21:07 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public T[] ReadArray<T>() where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var len = ReadVal<int>();
|
|
|
|
|
var from = Alloc<T>(len);
|
|
|
|
|
var result = new T[len];
|
|
|
|
|
fixed (T* pTo = result)
|
|
|
|
|
{
|
2023-02-03 13:37:20 +08:00
|
|
|
|
System.Buffer.MemoryCopy(from, pTo, len, len);
|
2023-02-01 21:21:07 +08:00
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2023-01-31 20:23:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|