mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-17 04:22:54 +08:00
500 lines
16 KiB
C#
500 lines
16 KiB
C#
using CodeWalker.GameFiles;
|
|
using CommunityToolkit.Diagnostics;
|
|
using CommunityToolkit.HighPerformance;
|
|
using SharpDX;
|
|
using System;
|
|
using System.Buffers;
|
|
using System.Buffers.Binary;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.Contracts;
|
|
using System.IO;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace CodeWalker.Core.Utils
|
|
{
|
|
public static class StreamingExtensions
|
|
{
|
|
public static Task<int> ReadAsync(this BinaryReader br, byte[] buffer, int index, int count)
|
|
{
|
|
return br.BaseStream.ReadAsync(buffer, index, count);
|
|
}
|
|
}
|
|
|
|
public static class SequenceReaderExtensions
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out uint value)
|
|
{
|
|
reader.TryReadLittleEndian(out int _value);
|
|
value = (uint) _value;
|
|
return true;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out ushort value)
|
|
{
|
|
reader.TryReadLittleEndian(out short _value);
|
|
value = (ushort)_value;
|
|
return true;
|
|
}
|
|
|
|
public static long ReadInt64(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out long value);
|
|
return value;
|
|
}
|
|
|
|
public static long ReadInt64BigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadBigEndian(out long value);
|
|
return value;
|
|
}
|
|
|
|
public static ulong ReadUInt64(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out ulong value);
|
|
return value;
|
|
}
|
|
|
|
public static ulong ReadUInt64BigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadBigEndian(out long value);
|
|
return (ulong)value;
|
|
}
|
|
|
|
public static uint ReadUInt32(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out uint value);
|
|
return value;
|
|
}
|
|
|
|
public static uint ReadUInt32BigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadBigEndian(out int value);
|
|
return (uint)value;
|
|
}
|
|
|
|
public static int ReadInt32(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadLittleEndian(out int value);
|
|
return value;
|
|
}
|
|
|
|
public static int ReadInt32BigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadBigEndian(out int value);
|
|
return value;
|
|
}
|
|
|
|
public static short ReadInt16(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadLittleEndian(out short value);
|
|
return value;
|
|
}
|
|
|
|
public static short ReadInt16BigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadBigEndian(out short value);
|
|
return value;
|
|
}
|
|
|
|
public static ushort ReadUInt16(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadLittleEndian(out ushort value);
|
|
return value;
|
|
}
|
|
|
|
public static ushort ReadUInt16BigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadBigEndian(out short value);
|
|
return (ushort)value;
|
|
}
|
|
|
|
public static byte ReadByte(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out byte value);
|
|
return value;
|
|
}
|
|
|
|
public static float ReadSingle(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out float value);
|
|
return value;
|
|
}
|
|
|
|
public static double ReadDouble(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out double value);
|
|
return value;
|
|
}
|
|
|
|
public static float ReadSingleBigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out int value);
|
|
|
|
return BitConverter.Int32BitsToSingle(BinaryPrimitives.ReverseEndianness(value));
|
|
}
|
|
|
|
public static double ReadDoubleBigEndian(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryRead(out long value);
|
|
|
|
return BitConverter.Int64BitsToDouble(BinaryPrimitives.ReverseEndianness(value));
|
|
}
|
|
|
|
public static Vector3 ReadVector3(ref this SequenceReader<byte> reader)
|
|
{
|
|
Vector3 v = new Vector3 {
|
|
X = ReadSingle(ref reader),
|
|
Y = ReadSingle(ref reader),
|
|
Z = ReadSingle(ref reader),
|
|
};
|
|
return v;
|
|
}
|
|
public static Vector4 ReadVector4(ref this SequenceReader<byte> reader)
|
|
{
|
|
Vector4 v = new Vector4
|
|
{
|
|
X = ReadSingle(ref reader),
|
|
Y = ReadSingle(ref reader),
|
|
Z = ReadSingle(ref reader),
|
|
W = ReadSingle(ref reader)
|
|
};
|
|
return v;
|
|
}
|
|
|
|
public static Matrix ReadMatrix(ref this SequenceReader<byte> reader)
|
|
{
|
|
Matrix m = new Matrix
|
|
{
|
|
M11 = ReadSingle(ref reader),
|
|
M21 = ReadSingle(ref reader),
|
|
M31 = ReadSingle(ref reader),
|
|
M41 = ReadSingle(ref reader),
|
|
M12 = ReadSingle(ref reader),
|
|
M22 = ReadSingle(ref reader),
|
|
M32 = ReadSingle(ref reader),
|
|
M42 = ReadSingle(ref reader),
|
|
M13 = ReadSingle(ref reader),
|
|
M23 = ReadSingle(ref reader),
|
|
M33 = ReadSingle(ref reader),
|
|
M43 = ReadSingle(ref reader),
|
|
M14 = ReadSingle(ref reader),
|
|
M24 = ReadSingle(ref reader),
|
|
M34 = ReadSingle(ref reader),
|
|
M44 = ReadSingle(ref reader)
|
|
};
|
|
return m;
|
|
}
|
|
|
|
public static ReadOnlySpan<ulong> ReadUlongsAt(ref this SequenceReader<byte> reader, ulong position, uint count, bool cache = true)
|
|
{
|
|
if ((position <= 0) || (count == 0))
|
|
return ReadOnlySpan<ulong>.Empty;
|
|
|
|
var length = count * sizeof(ulong);
|
|
|
|
var data = reader.ReadBytesAt(position, length, false);
|
|
if (data.IsEmpty)
|
|
{
|
|
return ReadOnlySpan<ulong>.Empty;
|
|
}
|
|
var result = MemoryMarshal.Cast<byte, ulong>(data);
|
|
|
|
return result;
|
|
}
|
|
|
|
public static ReadOnlySpan<byte> ReadBytes(ref this SequenceReader<byte> reader, int count)
|
|
{
|
|
var unread = reader.UnreadSpan;
|
|
if (unread.Length > count)
|
|
{
|
|
var resultSpan = unread.Slice(0, count);
|
|
reader.Advance(count);
|
|
return resultSpan;
|
|
}
|
|
|
|
var result = reader.UnreadSequence.Slice(0, count).ToArray();
|
|
reader.Advance(count);
|
|
return result;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static ReadOnlySpan<byte> ReadBytesAt(ref this SequenceReader<byte> reader, ulong position, uint count, bool cache = true)
|
|
{
|
|
return reader.ReadBytesAt(position, (int)count, cache);
|
|
}
|
|
|
|
public static ReadOnlySpan<byte> ReadBytesAt(ref this SequenceReader<byte> reader, ulong position, int count, bool cache = true)
|
|
{
|
|
var positionBackup = reader.Consumed;
|
|
|
|
reader.SetPosition((long)position);
|
|
var result = reader.ReadBytes(count);
|
|
reader.SetPosition((long)positionBackup);
|
|
|
|
return result;
|
|
}
|
|
|
|
public static ReadOnlySequence<byte> ReadSubSequence(ref this SequenceReader<byte> reader, int count)
|
|
{
|
|
var result = reader.UnreadSequence.Slice(0, count);
|
|
reader.Advance(count);
|
|
return result;
|
|
}
|
|
|
|
[SkipLocalsInit]
|
|
public static string ReadStringLength(ref this SequenceReader<byte> reader, int length, bool ignoreNullTerminator = true)
|
|
{
|
|
if (length == 0)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var bytes = reader.ReadBytes(length);
|
|
|
|
if (!ignoreNullTerminator)
|
|
{
|
|
var nullTerminatorIndex = bytes.IndexOf((byte)0);
|
|
|
|
if (nullTerminatorIndex != -1)
|
|
{
|
|
bytes.Slice(0, nullTerminatorIndex);
|
|
}
|
|
}
|
|
|
|
return Encoding.UTF8.GetString(bytes);
|
|
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
|
|
}
|
|
|
|
public static string ReadString(ref this SequenceReader<byte> reader, int maxLength)
|
|
{
|
|
reader.TryReadTo(out ReadOnlySpan<byte> span, 0);
|
|
|
|
if (span.Length > maxLength)
|
|
{
|
|
reader.Rewind(span.Length - maxLength);
|
|
return Encoding.UTF8.GetString(span.Slice(0, maxLength));
|
|
}
|
|
|
|
return Encoding.UTF8.GetString(span);
|
|
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
|
|
}
|
|
|
|
public static string ReadString(ref this SequenceReader<byte> reader)
|
|
{
|
|
reader.TryReadTo(out ReadOnlySpan<byte> span, 0);
|
|
|
|
return Encoding.UTF8.GetString(span);
|
|
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static unsafe bool TryRead<T>(ref this SequenceReader<byte> reader, out T value) where T : unmanaged
|
|
{
|
|
ReadOnlySpan<byte> span = reader.UnreadSpan;
|
|
if (span.Length < sizeof(T))
|
|
return TryReadMultisegment(ref reader, out value);
|
|
|
|
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
|
|
reader.Advance(Marshal.SizeOf<T>());
|
|
return true;
|
|
}
|
|
|
|
private static unsafe bool TryReadMultisegment<T>(ref SequenceReader<byte> reader, out T value) where T : unmanaged
|
|
{
|
|
Debug.Assert(reader.UnreadSpan.Length < sizeof(T));
|
|
|
|
// Not enough data in the current segment, try to peek for the data we need.
|
|
T buffer = default;
|
|
Span<byte> tempSpan = new Span<byte>(&buffer, sizeof(T));
|
|
|
|
if (!reader.TryCopyTo(tempSpan))
|
|
{
|
|
value = default;
|
|
return false;
|
|
}
|
|
|
|
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(tempSpan));
|
|
reader.Advance(sizeof(T));
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out short value)
|
|
{
|
|
if (reader.TryRead(out value))
|
|
{
|
|
value = BinaryPrimitives.ReverseEndianness(value);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static ConcurrentDictionary<Type, bool> cacheableTypes = new ConcurrentDictionary<Type, bool>();
|
|
private static bool fetchUsePool(Type type)
|
|
{
|
|
return !typeof(IResourceNoCacheBlock).IsAssignableFrom(type);
|
|
}
|
|
private static bool usePool<T>() where T : IResourceBlock, new()
|
|
{
|
|
return cacheableTypes.GetOrAdd(typeof(T), fetchUsePool);
|
|
}
|
|
|
|
public static T validate<T>(Func<T> instantiator)
|
|
where T : IResourceBlock
|
|
{
|
|
return instantiator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a block.
|
|
/// </summary>
|
|
public static T ReadBlock<T>(this ref SequenceReader<byte> reader, params object[] parameters) where T : IResourceBlockSpan, IResourceBlock, new()
|
|
{
|
|
var result = validate(() => new T());
|
|
|
|
|
|
// replace with correct type...
|
|
if (result is IResourceXXSytemBlockSpan block)
|
|
{
|
|
result = (T)block.GetType(ref reader, parameters);
|
|
}
|
|
|
|
result.Read(ref reader, parameters);
|
|
|
|
return result;
|
|
}
|
|
|
|
public static void SetPosition(this ref SequenceReader<byte> reader, long position)
|
|
{
|
|
var consumed = reader.Consumed;
|
|
if (position == consumed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (consumed > position)
|
|
{
|
|
reader.Rewind(consumed - position);
|
|
}
|
|
else
|
|
{
|
|
reader.Advance(position - consumed);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a block at a specified position.
|
|
/// </summary>
|
|
public static T ReadBlockAt<T>(this ref SequenceReader<byte> reader, ulong position, params object[] parameters) where T : IResourceBlock, IResourceBlockSpan, new()
|
|
{
|
|
if (position != 0)
|
|
{
|
|
var positionBackup = reader.Consumed;
|
|
|
|
reader.SetPosition((long)position);
|
|
var result = reader.ReadBlock<T>(parameters);
|
|
reader.SetPosition((long)positionBackup);
|
|
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return default(T);
|
|
}
|
|
}
|
|
|
|
public static ReadOnlySpan<T> ReadStructsAt<T>(this ref SequenceReader<byte> reader, ulong position, uint count, bool cache = true) where T : struct
|
|
{
|
|
return reader.ReadStructsAt<T>(position, (int)count, cache);
|
|
}
|
|
|
|
public static ReadOnlySpan<T> ReadStructsAt<T>(this ref SequenceReader<byte> reader, ulong position, int count, bool cache = true) where T : struct
|
|
{
|
|
if ((position <= 0) || (count == 0))
|
|
return null;
|
|
|
|
var structsize = Marshal.SizeOf(typeof(T));
|
|
var length = (int)(count * structsize);
|
|
var data = reader.ReadBytesAt(position, length, false);
|
|
|
|
var resultSpan = MemoryMarshal.Cast<byte, T>(data);
|
|
|
|
return resultSpan;
|
|
}
|
|
}
|
|
|
|
public static class SpanExtension
|
|
{
|
|
public static ReadOnlySpan<T> ReadTill<T>(this ReadOnlySpan<T> span, T searchFor) where T : IEquatable<T>?
|
|
{
|
|
var index = span.IndexOf(searchFor);
|
|
if (index < 0)
|
|
{
|
|
return span;
|
|
}
|
|
|
|
return span.Slice(index);
|
|
}
|
|
|
|
public static Span<T> ReadTill<T>(this Span<T> span, T searchFor) where T : IEquatable<T>?
|
|
{
|
|
var index = span.IndexOf(searchFor);
|
|
if (index < 0)
|
|
{
|
|
return span;
|
|
}
|
|
|
|
return span.Slice(0, index);
|
|
}
|
|
}
|
|
|
|
public ref struct SpanStream
|
|
{
|
|
public Span<byte> Buffer { get; private set; }
|
|
private int _position;
|
|
|
|
public SpanStream(Span<byte> buffer)
|
|
{
|
|
Buffer = buffer;
|
|
_position = 0;
|
|
}
|
|
|
|
private ReadOnlySpan<byte> InternalRead(int count)
|
|
{
|
|
int origPos = _position;
|
|
int newPos = origPos + count;
|
|
|
|
if ((uint)newPos > (uint)Buffer.Length)
|
|
{
|
|
_position = Buffer.Length;
|
|
throw new EndOfStreamException();
|
|
return default;
|
|
}
|
|
|
|
var span = Buffer.Slice(origPos, count);
|
|
_position = newPos;
|
|
return span;
|
|
}
|
|
|
|
public short ReadInt16() => BinaryPrimitives.ReadInt16LittleEndian(InternalRead(sizeof(short)));
|
|
|
|
public ushort ReadUInt16() => BinaryPrimitives.ReadUInt16LittleEndian(InternalRead(sizeof(ushort)));
|
|
|
|
public int ReadInt32() => BinaryPrimitives.ReadInt32LittleEndian(InternalRead(sizeof(int)));
|
|
public uint ReadUInt32() => BinaryPrimitives.ReadUInt32LittleEndian(InternalRead(sizeof(uint)));
|
|
public long ReadInt64() => BinaryPrimitives.ReadInt64LittleEndian(InternalRead(sizeof(long)));
|
|
public ulong ReadUInt64() => BinaryPrimitives.ReadUInt64LittleEndian(InternalRead(sizeof(ulong)));
|
|
public unsafe System.Half ReadHalf() => BinaryPrimitives.ReadHalfLittleEndian(InternalRead(sizeof(System.Half)));
|
|
public unsafe float ReadSingle() => BinaryPrimitives.ReadSingleLittleEndian(InternalRead(sizeof(float)));
|
|
public unsafe double ReadDouble() => BinaryPrimitives.ReadDoubleLittleEndian(InternalRead(sizeof(double)));
|
|
}
|
|
}
|