From ab5b81d01e746c8a7521b0057ea78d11a8cf9648 Mon Sep 17 00:00:00 2001 From: VaDiM Date: Sat, 21 Feb 2026 03:20:05 +0300 Subject: [PATCH] Update span extensions --- AssetStudio/Classes/Mesh.cs | 37 +++--- AssetStudio/EndianSpanReader.cs | 95 --------------- AssetStudio/Extensions/SpanExtensions.cs | 148 +++++++++++++++++++++++ AssetStudio/FileReader.cs | 2 +- AssetStudioUtility/Texture2DConverter.cs | 14 +-- 5 files changed, 174 insertions(+), 122 deletions(-) delete mode 100644 AssetStudio/EndianSpanReader.cs create mode 100644 AssetStudio/Extensions/SpanExtensions.cs diff --git a/AssetStudio/Classes/Mesh.cs b/AssetStudio/Classes/Mesh.cs index 0428233..e649407 100644 --- a/AssetStudio/Classes/Mesh.cs +++ b/AssetStudio/Classes/Mesh.cs @@ -1,9 +1,9 @@ using System; -using System.Buffers.Binary; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; namespace AssetStudio { @@ -1463,7 +1463,6 @@ namespace AssetStudio } } - public static uint GetFormatSize(VertexFormat format) { switch (format) @@ -1493,8 +1492,13 @@ namespace AssetStudio return format >= VertexFormat.UInt8; } - public static float[] BytesToFloatArray(byte[] inputBytes, VertexFormat format) + public static float[] BytesToFloatArray(ReadOnlySpan inputBytes, VertexFormat format) { + if (format == VertexFormat.Float) + { + return MemoryMarshal.Cast(inputBytes).ToArray(); + } + var size = GetFormatSize(format); var len = inputBytes.Length / size; var result = new float[len]; @@ -1502,15 +1506,8 @@ namespace AssetStudio { switch (format) { - case VertexFormat.Float: -#if NET - result[i] = BinaryPrimitives.ReadSingleLittleEndian(inputBytes.AsSpan(i * 4)); -#else - result[i] = BitConverter.ToSingle(inputBytes, i * 4); -#endif - break; case VertexFormat.Float16: - result[i] = (float)HalfHelper.ToHalf(inputBytes, i * 2); + result[i] = (float)inputBytes.ReadHalf(i * 2); break; case VertexFormat.UNorm8: result[i] = inputBytes[i] / 255f; @@ -1519,10 +1516,10 @@ namespace AssetStudio result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f); break; case VertexFormat.UNorm16: - result[i] = BinaryPrimitives.ReadUInt16LittleEndian(inputBytes.AsSpan(i * 2)) / 65535f; + result[i] = inputBytes.ReadUInt16(i * 2) / 65535f; break; case VertexFormat.SNorm16: - result[i] = Math.Max(BinaryPrimitives.ReadInt16LittleEndian(inputBytes.AsSpan(i * 2)) / 32767f, -1f); + result[i] = Math.Max(inputBytes.ReadInt16(i * 2) / 32767f, -1f); break; } } @@ -1531,6 +1528,11 @@ namespace AssetStudio public static int[] BytesToIntArray(ReadOnlySpan inputBytes, VertexFormat format) { + if (format == VertexFormat.SInt32) + { + return MemoryMarshal.Cast(inputBytes).ToArray(); + } + var size = GetFormatSize(format); var len = inputBytes.Length / size; var result = new int[len]; @@ -1543,16 +1545,13 @@ namespace AssetStudio result[i] = inputBytes[i]; break; case VertexFormat.UInt16: - result[i] = BinaryPrimitives.ReadUInt16LittleEndian(inputBytes.Slice(i * 2)); + result[i] = inputBytes.ReadUInt16(i * 2); break; case VertexFormat.SInt16: - result[i] = BinaryPrimitives.ReadInt16LittleEndian(inputBytes.Slice(i * 2)); + result[i] = inputBytes.ReadInt16(i * 2); break; case VertexFormat.UInt32: - result[i] = (int)BinaryPrimitives.ReadUInt32LittleEndian(inputBytes.Slice(i * 4)); - break; - case VertexFormat.SInt32: - result[i] = BinaryPrimitives.ReadInt32LittleEndian(inputBytes.Slice(i * 4)); + result[i] = (int)inputBytes.ReadUInt32(i * 4); break; } } diff --git a/AssetStudio/EndianSpanReader.cs b/AssetStudio/EndianSpanReader.cs deleted file mode 100644 index 2657005..0000000 --- a/AssetStudio/EndianSpanReader.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Text; - -namespace AssetStudio -{ - public static class EndianSpanReader - { - public static uint ReadUInt32(this Span data, int start, bool isBigEndian) - { - return ReadUInt32((ReadOnlySpan)data, start, isBigEndian); - } - - public static uint ReadUInt32(this ReadOnlySpan data, int start, bool isBigEndian) - { - return isBigEndian - ? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start)) - : BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start)); - } - - public static long ReadUInt16(this Span data, int start, bool isBigEndian) - { - return ReadUInt16((ReadOnlySpan)data, start, isBigEndian); - } - - public static uint ReadUInt16(this ReadOnlySpan data, int start, bool isBigEndian) - { - return isBigEndian - ? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start)) - : BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start)); - } - - public static long ReadInt64(this Span data, int start, bool isBigEndian) - { - return ReadInt64((ReadOnlySpan)data, start, isBigEndian); - } - - public static long ReadInt64(this ReadOnlySpan data, int start, bool isBigEndian) - { - return isBigEndian - ? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start)) - : BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start)); - } - - public static float ReadSingle(this Span data, int start, bool isBigEndian) - { - return ReadSingle((ReadOnlySpan)data, start, isBigEndian); - } - -#if NETFRAMEWORK - public static float ReadSingle(this ReadOnlySpan data, int start, bool isBigEndian) - { - var bytes = data.Slice(start, 4).ToArray(); - if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian)) - bytes.AsSpan().Reverse(); - - return BitConverter.ToSingle(bytes, 0); - } -#else - public static float ReadSingle(this ReadOnlySpan data, int start, bool isBigEndian) - { - return isBigEndian - ? BinaryPrimitives.ReadSingleBigEndian(data[start..]) - : BinaryPrimitives.ReadSingleLittleEndian(data[start..]); - } -#endif - - public static string ReadStringToNull(this Span data, int maxLength = 32767) - { - return ReadStringToNull((ReadOnlySpan)data, maxLength); - } - - public static string ReadStringToNull(this ReadOnlySpan data, int maxLength = 32767) - { - Span bytes = stackalloc byte[maxLength]; - var count = 0; - while (count != data.Length && count < maxLength) - { - var b = data[count]; - if (b == 0) - { - break; - } - bytes[count] = b; - count++; - } - bytes = bytes.Slice(0, count); -#if NETFRAMEWORK - return Encoding.UTF8.GetString(bytes.ToArray()); -#else - return Encoding.UTF8.GetString(bytes); -#endif - } - } -} diff --git a/AssetStudio/Extensions/SpanExtensions.cs b/AssetStudio/Extensions/SpanExtensions.cs new file mode 100644 index 0000000..4891fa3 --- /dev/null +++ b/AssetStudio/Extensions/SpanExtensions.cs @@ -0,0 +1,148 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Text; + +namespace AssetStudio +{ + public static class SpanExtensions + { +#if NETFRAMEWORK + private static readonly byte[] Buff = new byte[4]; +#endif + + public static ushort ReadUInt16(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadUInt16((ReadOnlySpan)data, start, isBigEndian); + } + + public static ushort ReadUInt16(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start)) + : BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start)); + } + + public static short ReadInt16(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadInt16((ReadOnlySpan)data, start, isBigEndian); + } + + public static short ReadInt16(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadInt16BigEndian(data.Slice(start)) + : BinaryPrimitives.ReadInt16LittleEndian(data.Slice(start)); + } + + public static uint ReadUInt32(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadUInt32((ReadOnlySpan)data, start, isBigEndian); + } + + public static uint ReadUInt32(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start)) + : BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start)); + } + + public static int ReadInt32(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadInt32((ReadOnlySpan)data, start, isBigEndian); + } + + public static int ReadInt32(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadInt32BigEndian(data.Slice(start)) + : BinaryPrimitives.ReadInt32LittleEndian(data.Slice(start)); + } + + public static long ReadInt64(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadInt64((ReadOnlySpan)data, start, isBigEndian); + } + + public static long ReadInt64(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start)) + : BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start)); + } + + public static Half ReadHalf(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadHalf((ReadOnlySpan)data, start, isBigEndian); + } + + public static float ReadSingle(this Span data, int start = 0, bool isBigEndian = false) + { + return ReadSingle((ReadOnlySpan)data, start, isBigEndian); + } + +#if NETFRAMEWORK + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Half ReadHalf(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? throw new NotImplementedException() + : HalfHelper.ToHalf((ushort)BinaryPrimitives.ReadInt16LittleEndian(data.Slice(start))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ReadSingle(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + data = data.Slice(start, 4); + if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian)) + (Buff[0], Buff[1], Buff[2], Buff[3]) = (data[3], data[2], data[1], data[0]); + else + (Buff[0], Buff[1], Buff[2], Buff[3]) = (data[0], data[1], data[2], data[3]); + + return BitConverter.ToSingle(Buff, 0); + } +#else + public static Half ReadHalf(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadHalfBigEndian(data.Slice(start)) + : BinaryPrimitives.ReadHalfLittleEndian(data.Slice(start)); + } + + public static float ReadSingle(this ReadOnlySpan data, int start = 0, bool isBigEndian = false) + { + return isBigEndian + ? BinaryPrimitives.ReadSingleBigEndian(data[start..]) + : BinaryPrimitives.ReadSingleLittleEndian(data[start..]); + } +#endif + + public static string ReadStringToNull(this Span data, int start = 0, int maxLength = 32767) + { + return ReadStringToNull((ReadOnlySpan)data, start, maxLength); + } + + public static string ReadStringToNull(this ReadOnlySpan data, int start = 0, int maxLength = 32767) + { + Span bytes = stackalloc byte[maxLength]; + data = data.Slice(start); + var count = 0; + while (count != data.Length && count < maxLength) + { + var b = data[count]; + if (b == 0) + { + break; + } + bytes[count] = b; + count++; + } + bytes = bytes.Slice(0, count); +#if NETFRAMEWORK + return Encoding.UTF8.GetString(bytes.ToArray()); +#else + return Encoding.UTF8.GetString(bytes); +#endif + } + } +} diff --git a/AssetStudio/FileReader.cs b/AssetStudio/FileReader.cs index 74c8e6a..5a0d317 100644 --- a/AssetStudio/FileReader.cs +++ b/AssetStudio/FileReader.cs @@ -33,7 +33,7 @@ namespace AssetStudio var dataLen = Read(headerBuff, 0, headerBuffLen); Position = 0; - var signature = buff.ReadStringToNull(20); + var signature = buff.ReadStringToNull(maxLength: 20); switch (signature) { case "UnityWeb": diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index c521e6b..d93b12b 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -416,7 +416,7 @@ namespace AssetStudio { buff[i] = 0; buff[i + 1] = 0; - buff[i + 2] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i / 2) * 255f); + buff[i + 2] = (byte)MathF.Round((float)image_data.ReadHalf(i / 2) * 255f); buff[i + 3] = 255; } return true; @@ -427,8 +427,8 @@ namespace AssetStudio for (var i = 0; i < outPutDataSize; i += 4) { buff[i] = 0; - buff[i + 1] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i + 2) * 255f); - buff[i + 2] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i) * 255f); + buff[i + 1] = (byte)MathF.Round((float)image_data.ReadHalf(i + 2) * 255f); + buff[i + 2] = (byte)MathF.Round((float)image_data.ReadHalf(i) * 255f); buff[i + 3] = 255; } return true; @@ -438,10 +438,10 @@ namespace AssetStudio { for (var i = 0; i < outPutDataSize; i += 4) { - buff[i] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2 + 4) * 255f); - buff[i + 1] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2 + 2) * 255f); - buff[i + 2] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2) * 255f); - buff[i + 3] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2 + 6) * 255f); + buff[i] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2 + 4) * 255f); + buff[i + 1] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2 + 2) * 255f); + buff[i + 2] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2) * 255f); + buff[i + 3] = (byte)MathF.Round((float)image_data.ReadHalf(i * 2 + 6) * 255f); } return true; }