diff --git a/Assets/TEngine/Runtime/Utility/Log.cs b/Assets/TEngine/Runtime/Log/Log.cs
similarity index 100%
rename from Assets/TEngine/Runtime/Utility/Log.cs
rename to Assets/TEngine/Runtime/Log/Log.cs
diff --git a/Assets/TEngine/Runtime/Utility/Log.cs.meta b/Assets/TEngine/Runtime/Log/Log.cs.meta
similarity index 100%
rename from Assets/TEngine/Runtime/Utility/Log.cs.meta
rename to Assets/TEngine/Runtime/Log/Log.cs.meta
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Assembly.cs b/Assets/TEngine/Runtime/Utility/Utility.Assembly.cs
new file mode 100644
index 00000000..e9f51a68
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Assembly.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 程序集相关的实用函数。
+ ///
+ public static class Assembly
+ {
+ private static readonly System.Reflection.Assembly[] s_Assemblies = null;
+ private static readonly Dictionary s_CachedTypes = new Dictionary(StringComparer.Ordinal);
+
+ static Assembly()
+ {
+ s_Assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ }
+
+ ///
+ /// 获取已加载的程序集。
+ ///
+ /// 已加载的程序集。
+ public static System.Reflection.Assembly[] GetAssemblies()
+ {
+ return s_Assemblies;
+ }
+
+ ///
+ /// 获取已加载的程序集中的所有类型。
+ ///
+ /// 已加载的程序集中的所有类型。
+ public static Type[] GetTypes()
+ {
+ List results = new List();
+ foreach (System.Reflection.Assembly assembly in s_Assemblies)
+ {
+ results.AddRange(assembly.GetTypes());
+ }
+
+ return results.ToArray();
+ }
+
+ ///
+ /// 获取已加载的程序集中的所有类型。
+ ///
+ /// 已加载的程序集中的所有类型。
+ public static void GetTypes(List results)
+ {
+ if (results == null)
+ {
+ throw new GameFrameworkException("Results is invalid.");
+ }
+
+ results.Clear();
+ foreach (System.Reflection.Assembly assembly in s_Assemblies)
+ {
+ results.AddRange(assembly.GetTypes());
+ }
+ }
+
+ ///
+ /// 获取已加载的程序集中的指定类型。
+ ///
+ /// 要获取的类型名。
+ /// 已加载的程序集中的指定类型。
+ public static Type GetType(string typeName)
+ {
+ if (string.IsNullOrEmpty(typeName))
+ {
+ throw new GameFrameworkException("Type name is invalid.");
+ }
+
+ Type type = null;
+ if (s_CachedTypes.TryGetValue(typeName, out type))
+ {
+ return type;
+ }
+
+ type = Type.GetType(typeName);
+ if (type != null)
+ {
+ s_CachedTypes.Add(typeName, type);
+ return type;
+ }
+
+ foreach (System.Reflection.Assembly assembly in s_Assemblies)
+ {
+ type = Type.GetType(Text.Format("{0}, {1}", typeName, assembly.FullName));
+ if (type != null)
+ {
+ s_CachedTypes.Add(typeName, type);
+ return type;
+ }
+ }
+
+ return null;
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Assembly.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Assembly.cs.meta
new file mode 100644
index 00000000..db7cdf79
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Assembly.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7609018db739fe545ba5be5d32f7eb3c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Compression.ICompressionHelper.cs b/Assets/TEngine/Runtime/Utility/Utility.Compression.ICompressionHelper.cs
new file mode 100644
index 00000000..56b15eaa
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Compression.ICompressionHelper.cs
@@ -0,0 +1,52 @@
+using System.IO;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ public static partial class Compression
+ {
+ ///
+ /// 压缩解压缩辅助器接口。
+ ///
+ public interface ICompressionHelper
+ {
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 要压缩的数据的二进制流的偏移。
+ /// 要压缩的数据的二进制流的长度。
+ /// 压缩后的数据的二进制流。
+ /// 是否压缩数据成功。
+ bool Compress(byte[] bytes, int offset, int length, Stream compressedStream);
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 压缩后的数据的二进制流。
+ /// 是否压缩数据成功。
+ bool Compress(Stream stream, Stream compressedStream);
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 要解压缩的数据的二进制流的偏移。
+ /// 要解压缩的数据的二进制流的长度。
+ /// 解压缩后的数据的二进制流。
+ /// 是否解压缩数据成功。
+ bool Decompress(byte[] bytes, int offset, int length, Stream decompressedStream);
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 解压缩后的数据的二进制流。
+ /// 是否解压缩数据成功。
+ bool Decompress(Stream stream, Stream decompressedStream);
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Compression.ICompressionHelper.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Compression.ICompressionHelper.cs.meta
new file mode 100644
index 00000000..0a96ad30
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Compression.ICompressionHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3d82356f5e654704daa0769074b9e5a7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Compression.cs b/Assets/TEngine/Runtime/Utility/Utility.Compression.cs
new file mode 100644
index 00000000..ab15faf3
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Compression.cs
@@ -0,0 +1,337 @@
+using System;
+using System.IO;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 压缩解压缩相关的实用函数。
+ ///
+ public static partial class Compression
+ {
+ private static ICompressionHelper s_CompressionHelper = null;
+
+ ///
+ /// 设置压缩解压缩辅助器。
+ ///
+ /// 要设置的压缩解压缩辅助器。
+ public static void SetCompressionHelper(ICompressionHelper compressionHelper)
+ {
+ s_CompressionHelper = compressionHelper;
+ }
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 压缩后的数据的二进制流。
+ public static byte[] Compress(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ return Compress(bytes, 0, bytes.Length);
+ }
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 压缩后的数据的二进制流。
+ /// 是否压缩数据成功。
+ public static bool Compress(byte[] bytes, Stream compressedStream)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ return Compress(bytes, 0, bytes.Length, compressedStream);
+ }
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 要压缩的数据的二进制流的偏移。
+ /// 要压缩的数据的二进制流的长度。
+ /// 压缩后的数据的二进制流。
+ public static byte[] Compress(byte[] bytes, int offset, int length)
+ {
+ using (MemoryStream compressedStream = new MemoryStream())
+ {
+ if (Compress(bytes, offset, length, compressedStream))
+ {
+ return compressedStream.ToArray();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 要压缩的数据的二进制流的偏移。
+ /// 要压缩的数据的二进制流的长度。
+ /// 压缩后的数据的二进制流。
+ /// 是否压缩数据成功。
+ public static bool Compress(byte[] bytes, int offset, int length, Stream compressedStream)
+ {
+ if (s_CompressionHelper == null)
+ {
+ throw new GameFrameworkException("Compressed helper is invalid.");
+ }
+
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ if (offset < 0 || length < 0 || offset + length > bytes.Length)
+ {
+ throw new GameFrameworkException("Offset or length is invalid.");
+ }
+
+ if (compressedStream == null)
+ {
+ throw new GameFrameworkException("Compressed stream is invalid.");
+ }
+
+ try
+ {
+ return s_CompressionHelper.Compress(bytes, offset, length, compressedStream);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not compress with exception '{0}'.", exception), exception);
+ }
+ }
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 压缩后的数据的二进制流。
+ public static byte[] Compress(Stream stream)
+ {
+ using (MemoryStream compressedStream = new MemoryStream())
+ {
+ if (Compress(stream, compressedStream))
+ {
+ return compressedStream.ToArray();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///
+ /// 压缩数据。
+ ///
+ /// 要压缩的数据的二进制流。
+ /// 压缩后的数据的二进制流。
+ /// 是否压缩数据成功。
+ public static bool Compress(Stream stream, Stream compressedStream)
+ {
+ if (s_CompressionHelper == null)
+ {
+ throw new GameFrameworkException("Compressed helper is invalid.");
+ }
+
+ if (stream == null)
+ {
+ throw new GameFrameworkException("Stream is invalid.");
+ }
+
+ if (compressedStream == null)
+ {
+ throw new GameFrameworkException("Compressed stream is invalid.");
+ }
+
+ try
+ {
+ return s_CompressionHelper.Compress(stream, compressedStream);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not compress with exception '{0}'.", exception), exception);
+ }
+ }
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 解压缩后的数据的二进制流。
+ public static byte[] Decompress(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ return Decompress(bytes, 0, bytes.Length);
+ }
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 解压缩后的数据的二进制流。
+ /// 是否解压缩数据成功。
+ public static bool Decompress(byte[] bytes, Stream decompressedStream)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ return Decompress(bytes, 0, bytes.Length, decompressedStream);
+ }
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 要解压缩的数据的二进制流的偏移。
+ /// 要解压缩的数据的二进制流的长度。
+ /// 解压缩后的数据的二进制流。
+ public static byte[] Decompress(byte[] bytes, int offset, int length)
+ {
+ using (MemoryStream decompressedStream = new MemoryStream())
+ {
+ if (Decompress(bytes, offset, length, decompressedStream))
+ {
+ return decompressedStream.ToArray();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 要解压缩的数据的二进制流的偏移。
+ /// 要解压缩的数据的二进制流的长度。
+ /// 解压缩后的数据的二进制流。
+ /// 是否解压缩数据成功。
+ public static bool Decompress(byte[] bytes, int offset, int length, Stream decompressedStream)
+ {
+ if (s_CompressionHelper == null)
+ {
+ throw new GameFrameworkException("Compressed helper is invalid.");
+ }
+
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ if (offset < 0 || length < 0 || offset + length > bytes.Length)
+ {
+ throw new GameFrameworkException("Offset or length is invalid.");
+ }
+
+ if (decompressedStream == null)
+ {
+ throw new GameFrameworkException("Decompressed stream is invalid.");
+ }
+
+ try
+ {
+ return s_CompressionHelper.Decompress(bytes, offset, length, decompressedStream);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not decompress with exception '{0}'.", exception), exception);
+ }
+ }
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 是否解压缩数据成功。
+ public static byte[] Decompress(Stream stream)
+ {
+ using (MemoryStream decompressedStream = new MemoryStream())
+ {
+ if (Decompress(stream, decompressedStream))
+ {
+ return decompressedStream.ToArray();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///
+ /// 解压缩数据。
+ ///
+ /// 要解压缩的数据的二进制流。
+ /// 解压缩后的数据的二进制流。
+ /// 是否解压缩数据成功。
+ public static bool Decompress(Stream stream, Stream decompressedStream)
+ {
+ if (s_CompressionHelper == null)
+ {
+ throw new GameFrameworkException("Compressed helper is invalid.");
+ }
+
+ if (stream == null)
+ {
+ throw new GameFrameworkException("Stream is invalid.");
+ }
+
+ if (decompressedStream == null)
+ {
+ throw new GameFrameworkException("Decompressed stream is invalid.");
+ }
+
+ try
+ {
+ return s_CompressionHelper.Decompress(stream, decompressedStream);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not decompress with exception '{0}'.", exception), exception);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Compression.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Compression.cs.meta
new file mode 100644
index 00000000..d07061b3
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Compression.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f1ec2480f190a1e419297e6efd755541
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Converter.cs b/Assets/TEngine/Runtime/Utility/Utility.Converter.cs
new file mode 100644
index 00000000..f0d8502c
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Converter.cs
@@ -0,0 +1,841 @@
+using System;
+using System.Text;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 类型转换相关的实用函数。
+ ///
+ public static class Converter
+ {
+ private const float InchesToCentimeters = 2.54f; // 1 inch = 2.54 cm
+ private const float CentimetersToInches = 1f / InchesToCentimeters; // 1 cm = 0.3937 inches
+
+ ///
+ /// 获取数据在此计算机结构中存储时的字节顺序。
+ ///
+ public static bool IsLittleEndian
+ {
+ get
+ {
+ return BitConverter.IsLittleEndian;
+ }
+ }
+
+ ///
+ /// 获取或设置屏幕每英寸点数。
+ ///
+ public static float ScreenDpi
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// 将像素转换为厘米。
+ ///
+ /// 像素。
+ /// 厘米。
+ public static float GetCentimetersFromPixels(float pixels)
+ {
+ if (ScreenDpi <= 0)
+ {
+ throw new GameFrameworkException("You must set screen DPI first.");
+ }
+
+ return InchesToCentimeters * pixels / ScreenDpi;
+ }
+
+ ///
+ /// 将厘米转换为像素。
+ ///
+ /// 厘米。
+ /// 像素。
+ public static float GetPixelsFromCentimeters(float centimeters)
+ {
+ if (ScreenDpi <= 0)
+ {
+ throw new GameFrameworkException("You must set screen DPI first.");
+ }
+
+ return CentimetersToInches * centimeters * ScreenDpi;
+ }
+
+ ///
+ /// 将像素转换为英寸。
+ ///
+ /// 像素。
+ /// 英寸。
+ public static float GetInchesFromPixels(float pixels)
+ {
+ if (ScreenDpi <= 0)
+ {
+ throw new GameFrameworkException("You must set screen DPI first.");
+ }
+
+ return pixels / ScreenDpi;
+ }
+
+ ///
+ /// 将英寸转换为像素。
+ ///
+ /// 英寸。
+ /// 像素。
+ public static float GetPixelsFromInches(float inches)
+ {
+ if (ScreenDpi <= 0)
+ {
+ throw new GameFrameworkException("You must set screen DPI first.");
+ }
+
+ return inches * ScreenDpi;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的布尔值。
+ ///
+ /// 要转换的布尔值。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(bool value)
+ {
+ byte[] buffer = new byte[1];
+ GetBytes(value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的布尔值。
+ ///
+ /// 要转换的布尔值。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(bool value, byte[] buffer)
+ {
+ GetBytes(value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的布尔值。
+ ///
+ /// 要转换的布尔值。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static void GetBytes(bool value, byte[] buffer, int startIndex)
+ {
+ if (buffer == null)
+ {
+ throw new GameFrameworkException("Buffer is invalid.");
+ }
+
+ if (startIndex < 0 || startIndex + 1 > buffer.Length)
+ {
+ throw new GameFrameworkException("Start index is invalid.");
+ }
+
+ buffer[startIndex] = value ? (byte)1 : (byte)0;
+ }
+
+ ///
+ /// 返回由字节数组中首字节转换来的布尔值。
+ ///
+ /// 字节数组。
+ /// 如果 value 中的首字节非零,则为 true,否则为 false。
+ public static bool GetBoolean(byte[] value)
+ {
+ return BitConverter.ToBoolean(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的一个字节转换来的布尔值。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 如果 value 中指定位置的字节非零,则为 true,否则为 false。
+ public static bool GetBoolean(byte[] value, int startIndex)
+ {
+ return BitConverter.ToBoolean(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 Unicode 字符值。
+ ///
+ /// 要转换的字符。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(char value)
+ {
+ byte[] buffer = new byte[2];
+ GetBytes((short)value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 Unicode 字符值。
+ ///
+ /// 要转换的字符。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(char value, byte[] buffer)
+ {
+ GetBytes((short)value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 Unicode 字符值。
+ ///
+ /// 要转换的字符。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static void GetBytes(char value, byte[] buffer, int startIndex)
+ {
+ GetBytes((short)value, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组中前两个字节转换来的 Unicode 字符。
+ ///
+ /// 字节数组。
+ /// 由两个字节构成的字符。
+ public static char GetChar(byte[] value)
+ {
+ return BitConverter.ToChar(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的两个字节转换来的 Unicode 字符。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由两个字节构成的字符。
+ public static char GetChar(byte[] value, int startIndex)
+ {
+ return BitConverter.ToChar(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 16 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(short value)
+ {
+ byte[] buffer = new byte[2];
+ GetBytes(value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 16 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(short value, byte[] buffer)
+ {
+ GetBytes(value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 16 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static unsafe void GetBytes(short value, byte[] buffer, int startIndex)
+ {
+ if (buffer == null)
+ {
+ throw new GameFrameworkException("Buffer is invalid.");
+ }
+
+ if (startIndex < 0 || startIndex + 2 > buffer.Length)
+ {
+ throw new GameFrameworkException("Start index is invalid.");
+ }
+
+ fixed (byte* valueRef = buffer)
+ {
+ *(short*)(valueRef + startIndex) = value;
+ }
+ }
+
+ ///
+ /// 返回由字节数组中前两个字节转换来的 16 位有符号整数。
+ ///
+ /// 字节数组。
+ /// 由两个字节构成的 16 位有符号整数。
+ public static short GetInt16(byte[] value)
+ {
+ return BitConverter.ToInt16(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的两个字节转换来的 16 位有符号整数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由两个字节构成的 16 位有符号整数。
+ public static short GetInt16(byte[] value, int startIndex)
+ {
+ return BitConverter.ToInt16(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 16 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(ushort value)
+ {
+ byte[] buffer = new byte[2];
+ GetBytes((short)value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 16 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(ushort value, byte[] buffer)
+ {
+ GetBytes((short)value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 16 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static void GetBytes(ushort value, byte[] buffer, int startIndex)
+ {
+ GetBytes((short)value, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组中前两个字节转换来的 16 位无符号整数。
+ ///
+ /// 字节数组。
+ /// 由两个字节构成的 16 位无符号整数。
+ public static ushort GetUInt16(byte[] value)
+ {
+ return BitConverter.ToUInt16(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的两个字节转换来的 16 位无符号整数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由两个字节构成的 16 位无符号整数。
+ public static ushort GetUInt16(byte[] value, int startIndex)
+ {
+ return BitConverter.ToUInt16(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 32 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(int value)
+ {
+ byte[] buffer = new byte[4];
+ GetBytes(value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 32 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(int value, byte[] buffer)
+ {
+ GetBytes(value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 32 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static unsafe void GetBytes(int value, byte[] buffer, int startIndex)
+ {
+ if (buffer == null)
+ {
+ throw new GameFrameworkException("Buffer is invalid.");
+ }
+
+ if (startIndex < 0 || startIndex + 4 > buffer.Length)
+ {
+ throw new GameFrameworkException("Start index is invalid.");
+ }
+
+ fixed (byte* valueRef = buffer)
+ {
+ *(int*)(valueRef + startIndex) = value;
+ }
+ }
+
+ ///
+ /// 返回由字节数组中前四个字节转换来的 32 位有符号整数。
+ ///
+ /// 字节数组。
+ /// 由四个字节构成的 32 位有符号整数。
+ public static int GetInt32(byte[] value)
+ {
+ return BitConverter.ToInt32(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的四个字节转换来的 32 位有符号整数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由四个字节构成的 32 位有符号整数。
+ public static int GetInt32(byte[] value, int startIndex)
+ {
+ return BitConverter.ToInt32(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 32 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(uint value)
+ {
+ byte[] buffer = new byte[4];
+ GetBytes((int)value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 32 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(uint value, byte[] buffer)
+ {
+ GetBytes((int)value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 32 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static void GetBytes(uint value, byte[] buffer, int startIndex)
+ {
+ GetBytes((int)value, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组中前四个字节转换来的 32 位无符号整数。
+ ///
+ /// 字节数组。
+ /// 由四个字节构成的 32 位无符号整数。
+ public static uint GetUInt32(byte[] value)
+ {
+ return BitConverter.ToUInt32(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的四个字节转换来的 32 位无符号整数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由四个字节构成的 32 位无符号整数。
+ public static uint GetUInt32(byte[] value, int startIndex)
+ {
+ return BitConverter.ToUInt32(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 64 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(long value)
+ {
+ byte[] buffer = new byte[8];
+ GetBytes(value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 64 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(long value, byte[] buffer)
+ {
+ GetBytes(value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 64 位有符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static unsafe void GetBytes(long value, byte[] buffer, int startIndex)
+ {
+ if (buffer == null)
+ {
+ throw new GameFrameworkException("Buffer is invalid.");
+ }
+
+ if (startIndex < 0 || startIndex + 8 > buffer.Length)
+ {
+ throw new GameFrameworkException("Start index is invalid.");
+ }
+
+ fixed (byte* valueRef = buffer)
+ {
+ *(long*)(valueRef + startIndex) = value;
+ }
+ }
+
+ ///
+ /// 返回由字节数组中前八个字节转换来的 64 位有符号整数。
+ ///
+ /// 字节数组。
+ /// 由八个字节构成的 64 位有符号整数。
+ public static long GetInt64(byte[] value)
+ {
+ return BitConverter.ToInt64(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的八个字节转换来的 64 位有符号整数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由八个字节构成的 64 位有符号整数。
+ public static long GetInt64(byte[] value, int startIndex)
+ {
+ return BitConverter.ToInt64(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 64 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(ulong value)
+ {
+ byte[] buffer = new byte[8];
+ GetBytes((long)value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 64 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static void GetBytes(ulong value, byte[] buffer)
+ {
+ GetBytes((long)value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的 64 位无符号整数值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static void GetBytes(ulong value, byte[] buffer, int startIndex)
+ {
+ GetBytes((long)value, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组中前八个字节转换来的 64 位无符号整数。
+ ///
+ /// 字节数组。
+ /// 由八个字节构成的 64 位无符号整数。
+ public static ulong GetUInt64(byte[] value)
+ {
+ return BitConverter.ToUInt64(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的八个字节转换来的 64 位无符号整数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由八个字节构成的 64 位无符号整数。
+ public static ulong GetUInt64(byte[] value, int startIndex)
+ {
+ return BitConverter.ToUInt64(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的单精度浮点值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static unsafe byte[] GetBytes(float value)
+ {
+ byte[] buffer = new byte[4];
+ GetBytes(*(int*)&value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的单精度浮点值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static unsafe void GetBytes(float value, byte[] buffer)
+ {
+ GetBytes(*(int*)&value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的单精度浮点值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static unsafe void GetBytes(float value, byte[] buffer, int startIndex)
+ {
+ GetBytes(*(int*)&value, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组中前四个字节转换来的单精度浮点数。
+ ///
+ /// 字节数组。
+ /// 由四个字节构成的单精度浮点数。
+ public static float GetSingle(byte[] value)
+ {
+ return BitConverter.ToSingle(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的四个字节转换来的单精度浮点数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由四个字节构成的单精度浮点数。
+ public static float GetSingle(byte[] value, int startIndex)
+ {
+ return BitConverter.ToSingle(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的双精度浮点值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static unsafe byte[] GetBytes(double value)
+ {
+ byte[] buffer = new byte[8];
+ GetBytes(*(long*)&value, buffer, 0);
+ return buffer;
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的双精度浮点值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ public static unsafe void GetBytes(double value, byte[] buffer)
+ {
+ GetBytes(*(long*)&value, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定的双精度浮点值。
+ ///
+ /// 要转换的数字。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ public static unsafe void GetBytes(double value, byte[] buffer, int startIndex)
+ {
+ GetBytes(*(long*)&value, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组中前八个字节转换来的双精度浮点数。
+ ///
+ /// 字节数组。
+ /// 由八个字节构成的双精度浮点数。
+ public static double GetDouble(byte[] value)
+ {
+ return BitConverter.ToDouble(value, 0);
+ }
+
+ ///
+ /// 返回由字节数组中指定位置的八个字节转换来的双精度浮点数。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 由八个字节构成的双精度浮点数。
+ public static double GetDouble(byte[] value, int startIndex)
+ {
+ return BitConverter.ToDouble(value, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取 UTF-8 编码的字符串。
+ ///
+ /// 要转换的字符串。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(string value)
+ {
+ return GetBytes(value, Encoding.UTF8);
+ }
+
+ ///
+ /// 以字节数组的形式获取 UTF-8 编码的字符串。
+ ///
+ /// 要转换的字符串。
+ /// 用于存放结果的字节数组。
+ /// buffer 内实际填充了多少字节。
+ public static int GetBytes(string value, byte[] buffer)
+ {
+ return GetBytes(value, Encoding.UTF8, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取 UTF-8 编码的字符串。
+ ///
+ /// 要转换的字符串。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ /// buffer 内实际填充了多少字节。
+ public static int GetBytes(string value, byte[] buffer, int startIndex)
+ {
+ return GetBytes(value, Encoding.UTF8, buffer, startIndex);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定编码的字符串。
+ ///
+ /// 要转换的字符串。
+ /// 要使用的编码。
+ /// 用于存放结果的字节数组。
+ public static byte[] GetBytes(string value, Encoding encoding)
+ {
+ if (value == null)
+ {
+ throw new GameFrameworkException("Value is invalid.");
+ }
+
+ if (encoding == null)
+ {
+ throw new GameFrameworkException("Encoding is invalid.");
+ }
+
+ return encoding.GetBytes(value);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定编码的字符串。
+ ///
+ /// 要转换的字符串。
+ /// 要使用的编码。
+ /// 用于存放结果的字节数组。
+ /// buffer 内实际填充了多少字节。
+ public static int GetBytes(string value, Encoding encoding, byte[] buffer)
+ {
+ return GetBytes(value, encoding, buffer, 0);
+ }
+
+ ///
+ /// 以字节数组的形式获取指定编码的字符串。
+ ///
+ /// 要转换的字符串。
+ /// 要使用的编码。
+ /// 用于存放结果的字节数组。
+ /// buffer 内的起始位置。
+ /// buffer 内实际填充了多少字节。
+ public static int GetBytes(string value, Encoding encoding, byte[] buffer, int startIndex)
+ {
+ if (value == null)
+ {
+ throw new GameFrameworkException("Value is invalid.");
+ }
+
+ if (encoding == null)
+ {
+ throw new GameFrameworkException("Encoding is invalid.");
+ }
+
+ return encoding.GetBytes(value, 0, value.Length, buffer, startIndex);
+ }
+
+ ///
+ /// 返回由字节数组使用 UTF-8 编码转换成的字符串。
+ ///
+ /// 字节数组。
+ /// 转换后的字符串。
+ public static string GetString(byte[] value)
+ {
+ return GetString(value, Encoding.UTF8);
+ }
+
+ ///
+ /// 返回由字节数组使用指定编码转换成的字符串。
+ ///
+ /// 字节数组。
+ /// 要使用的编码。
+ /// 转换后的字符串。
+ public static string GetString(byte[] value, Encoding encoding)
+ {
+ if (value == null)
+ {
+ throw new GameFrameworkException("Value is invalid.");
+ }
+
+ if (encoding == null)
+ {
+ throw new GameFrameworkException("Encoding is invalid.");
+ }
+
+ return encoding.GetString(value);
+ }
+
+ ///
+ /// 返回由字节数组使用 UTF-8 编码转换成的字符串。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 长度。
+ /// 转换后的字符串。
+ public static string GetString(byte[] value, int startIndex, int length)
+ {
+ return GetString(value, startIndex, length, Encoding.UTF8);
+ }
+
+ ///
+ /// 返回由字节数组使用指定编码转换成的字符串。
+ ///
+ /// 字节数组。
+ /// value 内的起始位置。
+ /// 长度。
+ /// 要使用的编码。
+ /// 转换后的字符串。
+ public static string GetString(byte[] value, int startIndex, int length, Encoding encoding)
+ {
+ if (value == null)
+ {
+ throw new GameFrameworkException("Value is invalid.");
+ }
+
+ if (encoding == null)
+ {
+ throw new GameFrameworkException("Encoding is invalid.");
+ }
+
+ return encoding.GetString(value, startIndex, length);
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Converter.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Converter.cs.meta
new file mode 100644
index 00000000..474171d6
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Converter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7362a66e3741e8440bcc831815b4426c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Encryption.cs b/Assets/TEngine/Runtime/Utility/Utility.Encryption.cs
new file mode 100644
index 00000000..5063057c
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Encryption.cs
@@ -0,0 +1,127 @@
+using System;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 加密解密相关的实用函数。
+ ///
+ public static class Encryption
+ {
+ internal const int QuickEncryptLength = 220;
+
+ ///
+ /// 将 bytes 使用 code 做异或运算的快速版本。
+ ///
+ /// 原始二进制流。
+ /// 异或二进制流。
+ /// 异或后的二进制流。
+ public static byte[] GetQuickXorBytes(byte[] bytes, byte[] code)
+ {
+ return GetXorBytes(bytes, 0, QuickEncryptLength, code);
+ }
+
+ ///
+ /// 将 bytes 使用 code 做异或运算的快速版本。此方法将复用并改写传入的 bytes 作为返回值,而不额外分配内存空间。
+ ///
+ /// 原始及异或后的二进制流。
+ /// 异或二进制流。
+ public static void GetQuickSelfXorBytes(byte[] bytes, byte[] code)
+ {
+ GetSelfXorBytes(bytes, 0, QuickEncryptLength, code);
+ }
+
+ ///
+ /// 将 bytes 使用 code 做异或运算。
+ ///
+ /// 原始二进制流。
+ /// 异或二进制流。
+ /// 异或后的二进制流。
+ public static byte[] GetXorBytes(byte[] bytes, byte[] code)
+ {
+ if (bytes == null)
+ {
+ return null;
+ }
+
+ return GetXorBytes(bytes, 0, bytes.Length, code);
+ }
+
+ ///
+ /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值,而不额外分配内存空间。
+ ///
+ /// 原始及异或后的二进制流。
+ /// 异或二进制流。
+ public static void GetSelfXorBytes(byte[] bytes, byte[] code)
+ {
+ if (bytes == null)
+ {
+ return;
+ }
+
+ GetSelfXorBytes(bytes, 0, bytes.Length, code);
+ }
+
+ ///
+ /// 将 bytes 使用 code 做异或运算。
+ ///
+ /// 原始二进制流。
+ /// 异或计算的开始位置。
+ /// 异或计算长度,若小于 0,则计算整个二进制流。
+ /// 异或二进制流。
+ /// 异或后的二进制流。
+ public static byte[] GetXorBytes(byte[] bytes, int startIndex, int length, byte[] code)
+ {
+ if (bytes == null)
+ {
+ return null;
+ }
+
+ int bytesLength = bytes.Length;
+ byte[] results = new byte[bytesLength];
+ Array.Copy(bytes, 0, results, 0, bytesLength);
+ GetSelfXorBytes(results, startIndex, length, code);
+ return results;
+ }
+
+ ///
+ /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值,而不额外分配内存空间。
+ ///
+ /// 原始及异或后的二进制流。
+ /// 异或计算的开始位置。
+ /// 异或计算长度。
+ /// 异或二进制流。
+ public static void GetSelfXorBytes(byte[] bytes, int startIndex, int length, byte[] code)
+ {
+ if (bytes == null)
+ {
+ return;
+ }
+
+ if (code == null)
+ {
+ throw new GameFrameworkException("Code is invalid.");
+ }
+
+ int codeLength = code.Length;
+ if (codeLength <= 0)
+ {
+ throw new GameFrameworkException("Code length is invalid.");
+ }
+
+ if (startIndex < 0 || length < 0 || startIndex + length > bytes.Length)
+ {
+ throw new GameFrameworkException("Start index or length is invalid.");
+ }
+
+ int codeIndex = startIndex % codeLength;
+ for (int i = startIndex; i < length; i++)
+ {
+ bytes[i] ^= code[codeIndex++];
+ codeIndex %= codeLength;
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Encryption.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Encryption.cs.meta
new file mode 100644
index 00000000..09a69ef3
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Encryption.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a279fdc81b03f4847a1b1a209af2a68e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.File.cs b/Assets/TEngine/Runtime/Utility/Utility.File.cs
new file mode 100644
index 00000000..8eba4910
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.File.cs
@@ -0,0 +1,232 @@
+using System;
+using System.IO;
+using System.Text;
+using UnityEditor;
+using UnityEngine;
+
+namespace TEngine
+{
+ ///
+ /// Unity平台路径类型。
+ ///
+ public enum UnityPlatformPathType : int
+ {
+ dataPath = 0,
+ streamingAssetsPath,
+ persistentDataPath,
+ temporaryCachePath,
+ }
+
+ public static partial class Utility
+ {
+ ///
+ /// 文件相关的实用函数。
+ ///
+ public static class File
+ {
+ public static bool CreateFile(string filePath, bool isCreateDir = true)
+ {
+ if (!System.IO.File.Exists(filePath))
+ {
+ string dir = System.IO.Path.GetDirectoryName(filePath);
+ if (!Directory.Exists(dir))
+ {
+ if (isCreateDir)
+ {
+ Directory.CreateDirectory(dir);
+ }
+ else
+ {
+ Log.Error("文件夹不存在 Path=" + dir);
+ return false;
+ }
+ }
+
+ System.IO.File.Create(filePath);
+ }
+
+ return true;
+ }
+
+ public static bool CreateFile(string filePath, string info, bool isCreateDir = true)
+ {
+ StreamWriter sw;
+ FileInfo t = new FileInfo(filePath);
+ if (!t.Exists)
+ {
+ string dir = System.IO.Path.GetDirectoryName(filePath);
+ if (!Directory.Exists(dir))
+ {
+ if (isCreateDir)
+ {
+ Directory.CreateDirectory(dir);
+ }
+ else
+ {
+#if UNITY_EDITOR
+ EditorUtility.DisplayDialog("Tips", "文件夹不存在", "CANCEL");
+#endif
+ Log.Error("文件夹不存在 Path=" + dir);
+ return false;
+ }
+ }
+
+ sw = t.CreateText();
+ }
+ else
+ {
+ sw = t.AppendText();
+ }
+
+ sw.WriteLine(info);
+ sw.Close();
+ sw.Dispose();
+ return true;
+ }
+
+ public static string GetPersistentDataPlatformPath(string filePath)
+ {
+ filePath =
+#if UNITY_ANDROID && !UNITY_EDITOR
+ Application.dataPath + "!assets" + "/" + filePath;
+#else
+ Application.streamingAssetsPath + "/" + filePath;
+#endif
+ return filePath;
+ }
+
+ public static string GetPath(string path)
+ {
+ return path.Replace("\\", "/");
+ }
+
+ public static string Md5ByPathName(string pathName)
+ {
+ try
+ {
+ FileStream file = new FileStream(pathName, FileMode.Open);
+ System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
+ byte[] retVal = md5.ComputeHash(file);
+ file.Close();
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < retVal.Length; i++)
+ {
+ sb.Append(retVal[i].ToString("x2"));
+ }
+
+ return sb.ToString();
+ }
+ catch (Exception ex)
+ {
+ Log.Error("to md5 fail,error:" + ex.Message);
+ return "Error";
+ }
+ }
+
+ public static string GetLengthString(long length)
+ {
+ if (length < 1024)
+ {
+ return $"{length.ToString()} Bytes";
+ }
+
+ if (length < 1024 * 1024)
+ {
+ return $"{(length / 1024f):F2} KB";
+ }
+
+ return length < 1024 * 1024 * 1024 ? $"{(length / 1024f / 1024f):F2} MB" : $"{(length / 1024f / 1024f / 1024f):F2} GB";
+ }
+
+ public static string GetByteLengthString(long byteLength)
+ {
+ if (byteLength < 1024L) // 2 ^ 10
+ {
+ return Utility.Text.Format("{0} Bytes", byteLength.ToString());
+ }
+
+ if (byteLength < 1048576L) // 2 ^ 20
+ {
+ return Utility.Text.Format("{0} KB", (byteLength / 1024f).ToString("F2"));
+ }
+
+ if (byteLength < 1073741824L) // 2 ^ 30
+ {
+ return Utility.Text.Format("{0} MB", (byteLength / 1048576f).ToString("F2"));
+ }
+
+ if (byteLength < 1099511627776L) // 2 ^ 40
+ {
+ return Utility.Text.Format("{0} GB", (byteLength / 1073741824f).ToString("F2"));
+ }
+
+ if (byteLength < 1125899906842624L) // 2 ^ 50
+ {
+ return Utility.Text.Format("{0} TB", (byteLength / 1099511627776f).ToString("F2"));
+ }
+
+ if (byteLength < 1152921504606846976L) // 2 ^ 60
+ {
+ return Utility.Text.Format("{0} PB", (byteLength / 1125899906842624f).ToString("F2"));
+ }
+
+ return Utility.Text.Format("{0} EB", (byteLength / 1152921504606846976f).ToString("F2"));
+ }
+
+ public static string BinToUtf8(byte[] total)
+ {
+ byte[] result = total;
+ if (total[0] == 0xef && total[1] == 0xbb && total[2] == 0xbf)
+ {
+ // utf8文件的前三个字节为特殊占位符,要跳过
+ result = new byte[total.Length - 3];
+ System.Array.Copy(total, 3, result, 0, total.Length - 3);
+ }
+
+ string utf8string = System.Text.Encoding.UTF8.GetString(result);
+ return utf8string;
+ }
+
+ ///
+ /// 数据格式转换
+ ///
+ /// 数据
+ ///
+ public static string FormatData(long data)
+ {
+ string result = "";
+ if (data < 0)
+ data = 0;
+
+ if (data > 1024 * 1024)
+ {
+ result = ((int)(data / (1024 * 1024))).ToString() + "MB";
+ }
+ else if (data > 1024)
+ {
+ result = ((int)(data / 1024)).ToString() + "KB";
+ }
+ else
+ {
+ result = data + "B";
+ }
+
+ return result;
+ }
+
+ ///
+ /// 获取文件大小
+ ///
+ /// 文件路径
+ ///
+ public static long GetFileSize(string path)
+ {
+ using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ return file.Length;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/TEngine/Runtime/Utility/Utility.File.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.File.cs.meta
new file mode 100644
index 00000000..41e1974a
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.File.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6d1483cbdd562774a95cb697ed574e43
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Folder.cs b/Assets/TEngine/Runtime/Utility/Utility.Folder.cs
new file mode 100644
index 00000000..b8e3169f
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Folder.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// Folder 相关的实用函数。
+ ///
+ public static partial class Folder
+ {
+ ///
+ /// 清理文件夹。
+ ///
+ /// 文件夹路径。
+ /// 操作成功。
+ public static bool ClearFolder(string path)
+ {
+ try
+ {
+ var di = new DirectoryInfo(path);
+ if (!di.Exists)
+ {
+ return false;
+ }
+
+ foreach (var file in di.GetFiles())
+ {
+ file.Delete();
+ }
+
+ foreach (var dir in di.GetDirectories())
+ {
+ dir.Delete(true);
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ throw new GameFrameworkException($"ClearFolder invalid:{e.Message}");
+ }
+ }
+
+ ///
+ /// 拷贝文件到根目录。
+ ///
+ /// 源文件目录。
+ /// 目标文件目录。
+ /// 查找选项。
+ /// 操作成功。
+ public static bool CopyFilesToRootPath(string sourceRootPath, string destRootPath, SearchOption searchOption = SearchOption.AllDirectories)
+ {
+ string[] fileNames = Directory.GetFiles(sourceRootPath, "*", searchOption);
+ foreach (string fileName in fileNames)
+ {
+ string destFileName = System.IO.Path.Combine(destRootPath, fileName.Substring(sourceRootPath.Length));
+ FileInfo destFileInfo = new FileInfo(destFileName);
+ if (destFileInfo.Directory != null && !destFileInfo.Directory.Exists)
+ {
+ destFileInfo.Directory.Create();
+ }
+
+ System.IO.File.Copy(fileName, destFileName, true);
+ }
+
+ return true;
+ }
+
+ ///
+ /// 拷贝文件夹。
+ ///
+ /// 需要被拷贝的文件夹路径。
+ /// 拷贝目标路径。
+ public static bool CopyFolder(string srcPath, string tarPath)
+ {
+ if (!Directory.Exists(srcPath))
+ {
+ return false;
+ }
+
+ if (!Directory.Exists(tarPath))
+ {
+ Directory.CreateDirectory(tarPath);
+ }
+
+ //获得源文件下所有文件
+ List files = new List(Directory.GetFiles(srcPath));
+ files.ForEach(f =>
+ {
+ string destFile = System.IO.Path.Combine(tarPath, System.IO.Path.GetFileName(f));
+ System.IO.File.Copy(f, destFile, true); //覆盖模式
+ });
+
+ //获得源文件下所有目录文件
+ List folders = new List(Directory.GetDirectories(srcPath));
+ folders.ForEach(f =>
+ {
+ string destDir = System.IO.Path.Combine(tarPath, System.IO.Path.GetFileName(f));
+ CopyFolder(f, destDir); //递归实现子文件夹拷贝
+ });
+ return true;
+ }
+
+ ///
+ /// 拷贝文件。
+ ///
+ /// 源文件目录。
+ /// 目标文件目录。
+ /// 搜索选项。
+ /// 操作成功。
+ public static bool CopyFiles(string sourceRootPath, string destRootPath, SearchOption searchOption = SearchOption.AllDirectories)
+ {
+ string[] fileNames = Directory.GetFiles(sourceRootPath, "*", searchOption);
+ foreach (string fileName in fileNames)
+ {
+ FileInfo sourceFileInfo = new FileInfo(fileName);
+ string destFileName = System.IO.Path.Combine(destRootPath, sourceFileInfo.Name);
+ FileInfo destFileInfo = new FileInfo(destFileName);
+ if (destFileInfo.Directory != null && !destFileInfo.Directory.Exists)
+ {
+ destFileInfo.Directory.Create();
+ }
+
+ System.IO.File.Copy(fileName, destFileName, true);
+ }
+
+ return true;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Folder.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Folder.cs.meta
new file mode 100644
index 00000000..504065f3
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Folder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 30d9951d450df9647a77c814ea8f68f6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Json.IJsonHelper.cs b/Assets/TEngine/Runtime/Utility/Utility.Json.IJsonHelper.cs
new file mode 100644
index 00000000..06c2bfd7
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Json.IJsonHelper.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ public static partial class Json
+ {
+ ///
+ /// JSON 辅助器接口。
+ ///
+ public interface IJsonHelper
+ {
+ ///
+ /// 将对象序列化为 JSON 字符串。
+ ///
+ /// 要序列化的对象。
+ /// 序列化后的 JSON 字符串。
+ string ToJson(object obj);
+
+ ///
+ /// 将 JSON 字符串反序列化为对象。
+ ///
+ /// 对象类型。
+ /// 要反序列化的 JSON 字符串。
+ /// 反序列化后的对象。
+ T ToObject(string json);
+
+ ///
+ /// 将 JSON 字符串反序列化为对象。
+ ///
+ /// 对象类型。
+ /// 要反序列化的 JSON 字符串。
+ /// 反序列化后的对象。
+ object ToObject(Type objectType, string json);
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Json.IJsonHelper.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Json.IJsonHelper.cs.meta
new file mode 100644
index 00000000..70e988d8
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Json.IJsonHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 54fba14fdfb53ab4f9edcd7eb3a9bc4c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Json.cs b/Assets/TEngine/Runtime/Utility/Utility.Json.cs
new file mode 100644
index 00000000..34d14604
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Json.cs
@@ -0,0 +1,112 @@
+using System;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// JSON 相关的实用函数。
+ ///
+ public static partial class Json
+ {
+ private static IJsonHelper s_JsonHelper = null;
+
+ ///
+ /// 设置 JSON 辅助器。
+ ///
+ /// 要设置的 JSON 辅助器。
+ public static void SetJsonHelper(IJsonHelper jsonHelper)
+ {
+ s_JsonHelper = jsonHelper;
+ }
+
+ ///
+ /// 将对象序列化为 JSON 字符串。
+ ///
+ /// 要序列化的对象。
+ /// 序列化后的 JSON 字符串。
+ public static string ToJson(object obj)
+ {
+ if (s_JsonHelper == null)
+ {
+ throw new GameFrameworkException("JSON helper is invalid.");
+ }
+
+ try
+ {
+ return s_JsonHelper.ToJson(obj);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not convert to JSON with exception '{0}'.", exception), exception);
+ }
+ }
+
+ ///
+ /// 将 JSON 字符串反序列化为对象。
+ ///
+ /// 对象类型。
+ /// 要反序列化的 JSON 字符串。
+ /// 反序列化后的对象。
+ public static T ToObject(string json)
+ {
+ if (s_JsonHelper == null)
+ {
+ throw new GameFrameworkException("JSON helper is invalid.");
+ }
+
+ try
+ {
+ return s_JsonHelper.ToObject(json);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not convert to object with exception '{0}'.", exception), exception);
+ }
+ }
+
+ ///
+ /// 将 JSON 字符串反序列化为对象。
+ ///
+ /// 对象类型。
+ /// 要反序列化的 JSON 字符串。
+ /// 反序列化后的对象。
+ public static object ToObject(Type objectType, string json)
+ {
+ if (s_JsonHelper == null)
+ {
+ throw new GameFrameworkException("JSON helper is invalid.");
+ }
+
+ if (objectType == null)
+ {
+ throw new GameFrameworkException("Object type is invalid.");
+ }
+
+ try
+ {
+ return s_JsonHelper.ToObject(objectType, json);
+ }
+ catch (Exception exception)
+ {
+ if (exception is GameFrameworkException)
+ {
+ throw;
+ }
+
+ throw new GameFrameworkException(Text.Format("Can not convert to object with exception '{0}'.", exception), exception);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Json.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Json.cs.meta
new file mode 100644
index 00000000..ba6d03e1
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Json.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4ee7fcac5a1ebab449eba2456434d3e6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Marshal.cs b/Assets/TEngine/Runtime/Utility/Utility.Marshal.cs
new file mode 100644
index 00000000..61b9c83d
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Marshal.cs
@@ -0,0 +1,233 @@
+using System;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// Marshal 相关的实用函数。
+ ///
+ public static class Marshal
+ {
+ private const int BlockSize = 1024 * 4;
+ private static IntPtr s_CachedHGlobalPtr = IntPtr.Zero;
+ private static int s_CachedHGlobalSize = 0;
+
+ ///
+ /// 获取缓存的从进程的非托管内存中分配的内存的大小。
+ ///
+ public static int CachedHGlobalSize
+ {
+ get
+ {
+ return s_CachedHGlobalSize;
+ }
+ }
+
+ ///
+ /// 确保从进程的非托管内存中分配足够大小的内存并缓存。
+ ///
+ /// 要确保从进程的非托管内存中分配内存的大小。
+ public static void EnsureCachedHGlobalSize(int ensureSize)
+ {
+ if (ensureSize < 0)
+ {
+ throw new GameFrameworkException("Ensure size is invalid.");
+ }
+
+ if (s_CachedHGlobalPtr == IntPtr.Zero || s_CachedHGlobalSize < ensureSize)
+ {
+ FreeCachedHGlobal();
+ int size = (ensureSize - 1 + BlockSize) / BlockSize * BlockSize;
+ s_CachedHGlobalPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
+ s_CachedHGlobalSize = size;
+ }
+ }
+
+ ///
+ /// 释放缓存的从进程的非托管内存中分配的内存。
+ ///
+ public static void FreeCachedHGlobal()
+ {
+ if (s_CachedHGlobalPtr != IntPtr.Zero)
+ {
+ System.Runtime.InteropServices.Marshal.FreeHGlobal(s_CachedHGlobalPtr);
+ s_CachedHGlobalPtr = IntPtr.Zero;
+ s_CachedHGlobalSize = 0;
+ }
+ }
+
+ ///
+ /// 将数据从对象转换为二进制流。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象。
+ /// 存储转换结果的二进制流。
+ public static byte[] StructureToBytes(T structure)
+ {
+ return StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)));
+ }
+
+ ///
+ /// 将数据从对象转换为二进制流。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象。
+ /// 要转换的对象的大小。
+ /// 存储转换结果的二进制流。
+ internal static byte[] StructureToBytes(T structure, int structureSize)
+ {
+ if (structureSize < 0)
+ {
+ throw new GameFrameworkException("Structure size is invalid.");
+ }
+
+ EnsureCachedHGlobalSize(structureSize);
+ System.Runtime.InteropServices.Marshal.StructureToPtr(structure, s_CachedHGlobalPtr, true);
+ byte[] result = new byte[structureSize];
+ System.Runtime.InteropServices.Marshal.Copy(s_CachedHGlobalPtr, result, 0, structureSize);
+ return result;
+ }
+
+ ///
+ /// 将数据从对象转换为二进制流。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象。
+ /// 存储转换结果的二进制流。
+ public static void StructureToBytes(T structure, byte[] result)
+ {
+ StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), result, 0);
+ }
+
+ ///
+ /// 将数据从对象转换为二进制流。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象。
+ /// 要转换的对象的大小。
+ /// 存储转换结果的二进制流。
+ internal static void StructureToBytes(T structure, int structureSize, byte[] result)
+ {
+ StructureToBytes(structure, structureSize, result, 0);
+ }
+
+ ///
+ /// 将数据从对象转换为二进制流。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象。
+ /// 存储转换结果的二进制流。
+ /// 写入存储转换结果的二进制流的起始位置。
+ public static void StructureToBytes(T structure, byte[] result, int startIndex)
+ {
+ StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), result, startIndex);
+ }
+
+ ///
+ /// 将数据从对象转换为二进制流。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象。
+ /// 要转换的对象的大小。
+ /// 存储转换结果的二进制流。
+ /// 写入存储转换结果的二进制流的起始位置。
+ internal static void StructureToBytes(T structure, int structureSize, byte[] result, int startIndex)
+ {
+ if (structureSize < 0)
+ {
+ throw new GameFrameworkException("Structure size is invalid.");
+ }
+
+ if (result == null)
+ {
+ throw new GameFrameworkException("Result is invalid.");
+ }
+
+ if (startIndex < 0)
+ {
+ throw new GameFrameworkException("Start index is invalid.");
+ }
+
+ if (startIndex + structureSize > result.Length)
+ {
+ throw new GameFrameworkException("Result length is not enough.");
+ }
+
+ EnsureCachedHGlobalSize(structureSize);
+ System.Runtime.InteropServices.Marshal.StructureToPtr(structure, s_CachedHGlobalPtr, true);
+ System.Runtime.InteropServices.Marshal.Copy(s_CachedHGlobalPtr, result, startIndex, structureSize);
+ }
+
+ ///
+ /// 将数据从二进制流转换为对象。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的二进制流。
+ /// 存储转换结果的对象。
+ public static T BytesToStructure(byte[] buffer)
+ {
+ return BytesToStructure(System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), buffer, 0);
+ }
+
+ ///
+ /// 将数据从二进制流转换为对象。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的二进制流。
+ /// 读取要转换的二进制流的起始位置。
+ /// 存储转换结果的对象。
+ public static T BytesToStructure(byte[] buffer, int startIndex)
+ {
+ return BytesToStructure(System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), buffer, startIndex);
+ }
+
+ ///
+ /// 将数据从二进制流转换为对象。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象的大小。
+ /// 要转换的二进制流。
+ /// 存储转换结果的对象。
+ internal static T BytesToStructure(int structureSize, byte[] buffer)
+ {
+ return BytesToStructure(structureSize, buffer, 0);
+ }
+
+ ///
+ /// 将数据从二进制流转换为对象。
+ ///
+ /// 要转换的对象的类型。
+ /// 要转换的对象的大小。
+ /// 要转换的二进制流。
+ /// 读取要转换的二进制流的起始位置。
+ /// 存储转换结果的对象。
+ internal static T BytesToStructure(int structureSize, byte[] buffer, int startIndex)
+ {
+ if (structureSize < 0)
+ {
+ throw new GameFrameworkException("Structure size is invalid.");
+ }
+
+ if (buffer == null)
+ {
+ throw new GameFrameworkException("Buffer is invalid.");
+ }
+
+ if (startIndex < 0)
+ {
+ throw new GameFrameworkException("Start index is invalid.");
+ }
+
+ if (startIndex + structureSize > buffer.Length)
+ {
+ throw new GameFrameworkException("Buffer length is not enough.");
+ }
+
+ EnsureCachedHGlobalSize(structureSize);
+ System.Runtime.InteropServices.Marshal.Copy(buffer, startIndex, s_CachedHGlobalPtr, structureSize);
+ return (T)System.Runtime.InteropServices.Marshal.PtrToStructure(s_CachedHGlobalPtr, typeof(T));
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Marshal.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Marshal.cs.meta
new file mode 100644
index 00000000..bff1a979
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Marshal.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e4eaf023a908b6847bc7c1b16e025fcd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Path.cs b/Assets/TEngine/Runtime/Utility/Utility.Path.cs
new file mode 100644
index 00000000..f14e2094
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Path.cs
@@ -0,0 +1,93 @@
+using System.IO;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 路径相关的实用函数。
+ ///
+ public static class Path
+ {
+ ///
+ /// 获取规范的路径。
+ ///
+ /// 要规范的路径。
+ /// 规范的路径。
+ public static string GetRegularPath(string path)
+ {
+ if (path == null)
+ {
+ return null;
+ }
+
+ return path.Replace('\\', '/');
+ }
+
+ ///
+ /// 获取远程格式的路径(带有file:// 或 http:// 前缀)。
+ ///
+ /// 原始路径。
+ /// 远程格式路径。
+ public static string GetRemotePath(string path)
+ {
+ string regularPath = GetRegularPath(path);
+ if (regularPath == null)
+ {
+ return null;
+ }
+
+ return regularPath.Contains("://") ? regularPath : ("file:///" + regularPath).Replace("file:////", "file:///");
+ }
+
+ ///
+ /// 移除空文件夹。
+ ///
+ /// 要处理的文件夹名称。
+ /// 是否移除空文件夹成功。
+ public static bool RemoveEmptyDirectory(string directoryName)
+ {
+ if (string.IsNullOrEmpty(directoryName))
+ {
+ throw new GameFrameworkException("Directory name is invalid.");
+ }
+
+ try
+ {
+ if (!Directory.Exists(directoryName))
+ {
+ return false;
+ }
+
+ // 不使用 SearchOption.AllDirectories,以便于在可能产生异常的环境下删除尽可能多的目录
+ string[] subDirectoryNames = Directory.GetDirectories(directoryName, "*");
+ int subDirectoryCount = subDirectoryNames.Length;
+ foreach (string subDirectoryName in subDirectoryNames)
+ {
+ if (RemoveEmptyDirectory(subDirectoryName))
+ {
+ subDirectoryCount--;
+ }
+ }
+
+ if (subDirectoryCount > 0)
+ {
+ return false;
+ }
+
+ if (Directory.GetFiles(directoryName, "*").Length > 0)
+ {
+ return false;
+ }
+
+ Directory.Delete(directoryName);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Path.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Path.cs.meta
new file mode 100644
index 00000000..77bcd4dd
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Path.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ffffdb31a0e4d6e49a95d47aa1976e94
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Random.cs b/Assets/TEngine/Runtime/Utility/Utility.Random.cs
new file mode 100644
index 00000000..f11a3646
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Random.cs
@@ -0,0 +1,72 @@
+using System;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 随机相关的实用函数。
+ ///
+ public static class Random
+ {
+ private static System.Random s_Random = new System.Random((int)DateTime.UtcNow.Ticks);
+
+ ///
+ /// 设置随机数种子。
+ ///
+ /// 随机数种子。
+ public static void SetSeed(int seed)
+ {
+ s_Random = new System.Random(seed);
+ }
+
+ ///
+ /// 返回非负随机数。
+ ///
+ /// 大于等于零且小于 System.Int32.MaxValue 的 32 位带符号整数。
+ public static int GetRandom()
+ {
+ return s_Random.Next();
+ }
+
+ ///
+ /// 返回一个小于所指定最大值的非负随机数。
+ ///
+ /// 要生成的随机数的上界(随机数不能取该上界值)。maxValue 必须大于等于零。
+ /// 大于等于零且小于 maxValue 的 32 位带符号整数,即:返回值的范围通常包括零但不包括 maxValue。不过,如果 maxValue 等于零,则返回 maxValue。
+ public static int GetRandom(int maxValue)
+ {
+ return s_Random.Next(maxValue);
+ }
+
+ ///
+ /// 返回一个指定范围内的随机数。
+ ///
+ /// 返回的随机数的下界(随机数可取该下界值)。
+ /// 返回的随机数的上界(随机数不能取该上界值)。maxValue 必须大于等于 minValue。
+ /// 一个大于等于 minValue 且小于 maxValue 的 32 位带符号整数,即:返回的值范围包括 minValue 但不包括 maxValue。如果 minValue 等于 maxValue,则返回 minValue。
+ public static int GetRandom(int minValue, int maxValue)
+ {
+ return s_Random.Next(minValue, maxValue);
+ }
+
+ ///
+ /// 返回一个介于 0.0 和 1.0 之间的随机数。
+ ///
+ /// 大于等于 0.0 并且小于 1.0 的双精度浮点数。
+ public static double GetRandomDouble()
+ {
+ return s_Random.NextDouble();
+ }
+
+ ///
+ /// 用随机数填充指定字节数组的元素。
+ ///
+ /// 包含随机数的字节数组。
+ public static void GetRandomBytes(byte[] buffer)
+ {
+ s_Random.NextBytes(buffer);
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Random.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Random.cs.meta
new file mode 100644
index 00000000..0da5fa3b
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Random.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d141c00a7ebf38141b1661ed1acb48f1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Unity.cs b/Assets/TEngine/Runtime/Utility/Utility.Unity.cs
new file mode 100644
index 00000000..76e25b5d
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Unity.cs
@@ -0,0 +1,391 @@
+using System;
+using System.Collections;
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.Internal;
+using Object = UnityEngine.Object;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// Unity相关的实用函数。
+ ///
+ public static partial class Unity
+ {
+ private static GameObject _entity;
+ private static MainBehaviour _behaviour;
+
+ #region 控制协程Coroutine
+
+ public static Coroutine StartCoroutine(string methodName)
+ {
+ if (string.IsNullOrEmpty(methodName))
+ {
+ return null;
+ }
+
+ _MakeEntity();
+ return _behaviour.StartCoroutine(methodName);
+ }
+
+ public static Coroutine StartCoroutine(IEnumerator routine)
+ {
+ if (routine == null)
+ {
+ return null;
+ }
+
+ _MakeEntity();
+ return _behaviour.StartCoroutine(routine);
+ }
+
+ public static Coroutine StartCoroutine(string methodName, [DefaultValue("null")] object value)
+ {
+ if (string.IsNullOrEmpty(methodName))
+ {
+ return null;
+ }
+
+ _MakeEntity();
+ return _behaviour.StartCoroutine(methodName, value);
+ }
+
+ public static void StopCoroutine(string methodName)
+ {
+ if (string.IsNullOrEmpty(methodName))
+ {
+ return;
+ }
+
+ if (_entity != null)
+ {
+ _behaviour.StopCoroutine(methodName);
+ }
+ }
+
+ public static void StopCoroutine(IEnumerator routine)
+ {
+ if (routine == null)
+ {
+ return;
+ }
+
+ if (_entity != null)
+ {
+ _behaviour.StopCoroutine(routine);
+ }
+ }
+
+ public static void StopCoroutine(Coroutine routine)
+ {
+ if (routine == null)
+ return;
+
+ if (_entity != null)
+ {
+ _behaviour.StopCoroutine(routine);
+ routine = null;
+ }
+ }
+
+ public static void StopAllCoroutines()
+ {
+ if (_entity != null)
+ {
+ _behaviour.StopAllCoroutines();
+ }
+ }
+
+ #endregion
+
+ #region 注入UnityUpdate/FixedUpdate/LateUpdate
+
+ ///
+ /// 为给外部提供的 添加帧更新事件。
+ ///
+ ///
+ public static void AddUpdateListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.AddUpdateListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的 添加物理帧更新事件。
+ ///
+ ///
+ public static void AddFixedUpdateListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.AddFixedUpdateListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的 添加Late帧更新事件。
+ ///
+ ///
+ public static void AddLateUpdateListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.AddLateUpdateListener(fun);
+ }
+
+ ///
+ /// 移除帧更新事件。
+ ///
+ ///
+ public static void RemoveUpdateListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.RemoveUpdateListener(fun);
+ }
+
+ ///
+ /// 移除物理帧更新事件。
+ ///
+ ///
+ public static void RemoveFixedUpdateListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.RemoveFixedUpdateListener(fun);
+ }
+
+ ///
+ /// 移除Late帧更新事件。
+ ///
+ ///
+ public static void RemoveLateUpdateListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.RemoveLateUpdateListener(fun);
+ }
+
+ #endregion
+
+ #region Unity Events 注入
+ ///
+ /// 为给外部提供的Destroy注册事件。
+ ///
+ ///
+ public static void AddDestroyListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.AddDestroyListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的Destroy反注册事件。
+ ///
+ ///
+ public static void RemoveDestroyListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.RemoveDestroyListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的OnDrawGizmos注册事件。
+ ///
+ ///
+ public static void AddOnDrawGizmosListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.RemoveDestroyListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的OnDrawGizmos反注册事件。
+ ///
+ ///
+ public static void RemoveOnDrawGizmosListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.RemoveDestroyListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的OnApplicationPause注册事件。
+ ///
+ ///
+ public static void AddOnApplicationPauseListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.AddOnApplicationPauseListener(fun);
+ }
+
+ ///
+ /// 为给外部提供的OnApplicationPause反注册事件。
+ ///
+ ///
+ public static void RemoveOnApplicationPauseListener(UnityAction fun)
+ {
+ _MakeEntity();
+ _behaviour.AddOnApplicationPauseListener(fun);
+ }
+ #endregion
+
+ ///
+ /// 释放Behaviour生命周期。
+ ///
+ public static void Release()
+ {
+ _MakeEntity();
+ _behaviour.Release();
+ }
+
+ private static void _MakeEntity()
+ {
+ if (_entity != null)
+ {
+ return;
+ }
+
+ _entity = new GameObject("__MonoUtility__")
+ {
+ hideFlags = HideFlags.HideAndDontSave
+ };
+ _entity.SetActive(true);
+
+#if UNITY_EDITOR
+ if (Application.isPlaying)
+#endif
+ {
+ Object.DontDestroyOnLoad(_entity);
+ }
+
+ UnityEngine.Assertions.Assert.IsFalse(_behaviour);
+ _behaviour = _entity.AddComponent();
+ }
+
+ private class MainBehaviour : MonoBehaviour
+ {
+ private event UnityAction updateEvent;
+ private event UnityAction fixedUpdateEvent;
+ private event UnityAction lateUpdateEvent;
+ private event UnityAction destroyEvent;
+ private event UnityAction onDrawGizmosEvent;
+ private event UnityAction onApplicationPause;
+
+ void Update()
+ {
+ if (updateEvent != null)
+ {
+ updateEvent();
+ }
+ }
+
+ void FixedUpdate()
+ {
+ if (fixedUpdateEvent != null)
+ {
+ fixedUpdateEvent();
+ }
+ }
+
+ void LateUpdate()
+ {
+ if (lateUpdateEvent != null)
+ {
+ lateUpdateEvent();
+ }
+ }
+
+ private void OnDestroy()
+ {
+ if (destroyEvent != null)
+ {
+ destroyEvent();
+ }
+ }
+
+ private void OnDrawGizmos()
+ {
+ if (onDrawGizmosEvent != null)
+ {
+ onDrawGizmosEvent();
+ }
+ }
+
+ private void OnApplicationPause(bool pauseStatus)
+ {
+ if (onApplicationPause != null)
+ {
+ onApplicationPause(pauseStatus);
+ }
+ }
+
+ public void AddLateUpdateListener(UnityAction fun)
+ {
+ lateUpdateEvent += fun;
+ }
+
+ public void RemoveLateUpdateListener(UnityAction fun)
+ {
+ lateUpdateEvent -= fun;
+ }
+
+ public void AddFixedUpdateListener(UnityAction fun)
+ {
+ fixedUpdateEvent += fun;
+ }
+
+ public void RemoveFixedUpdateListener(UnityAction fun)
+ {
+ fixedUpdateEvent -= fun;
+ }
+
+ public void AddUpdateListener(UnityAction fun)
+ {
+ updateEvent += fun;
+ }
+
+ public void RemoveUpdateListener(UnityAction fun)
+ {
+ updateEvent -= fun;
+ }
+
+ public void AddDestroyListener(UnityAction fun)
+ {
+ destroyEvent += fun;
+ }
+
+ public void RemoveDestroyListener(UnityAction fun)
+ {
+ destroyEvent -= fun;
+ }
+
+ public void AddOnDrawGizmosListener(UnityAction fun)
+ {
+ onDrawGizmosEvent += fun;
+ }
+
+ public void RemoveOnDrawGizmosListener(UnityAction fun)
+ {
+ onDrawGizmosEvent -= fun;
+ }
+
+ public void AddOnApplicationPauseListener(UnityAction fun)
+ {
+ onApplicationPause += fun;
+ }
+
+ public void RemoveOnApplicationPauseListener(UnityAction fun)
+ {
+ onApplicationPause -= fun;
+ }
+
+ public void Release()
+ {
+ updateEvent = null;
+ fixedUpdateEvent = null;
+ lateUpdateEvent = null;
+ onDrawGizmosEvent = null;
+ destroyEvent = null;
+ onApplicationPause = null;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Unity.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Unity.cs.meta
new file mode 100644
index 00000000..5f785dea
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Unity.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1b5e063f21f5cba4d85471c343c33271
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Verifier.Crc32.cs b/Assets/TEngine/Runtime/Utility/Utility.Verifier.Crc32.cs
new file mode 100644
index 00000000..7c44a9a7
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Verifier.Crc32.cs
@@ -0,0 +1,87 @@
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ public static partial class Verifier
+ {
+ ///
+ /// CRC32 算法。
+ ///
+ private sealed class Crc32
+ {
+ private const int TableLength = 256;
+ private const uint DefaultPolynomial = 0xedb88320;
+ private const uint DefaultSeed = 0xffffffff;
+
+ private readonly uint m_Seed;
+ private readonly uint[] m_Table;
+ private uint m_Hash;
+
+ public Crc32()
+ : this(DefaultPolynomial, DefaultSeed)
+ {
+ }
+
+ public Crc32(uint polynomial, uint seed)
+ {
+ m_Seed = seed;
+ m_Table = InitializeTable(polynomial);
+ m_Hash = seed;
+ }
+
+ public void Initialize()
+ {
+ m_Hash = m_Seed;
+ }
+
+ public void HashCore(byte[] bytes, int offset, int length)
+ {
+ m_Hash = CalculateHash(m_Table, m_Hash, bytes, offset, length);
+ }
+
+ public uint HashFinal()
+ {
+ return ~m_Hash;
+ }
+
+ private static uint CalculateHash(uint[] table, uint value, byte[] bytes, int offset, int length)
+ {
+ int last = offset + length;
+ for (int i = offset; i < last; i++)
+ {
+ unchecked
+ {
+ value = (value >> 8) ^ table[bytes[i] ^ value & 0xff];
+ }
+ }
+
+ return value;
+ }
+
+ private static uint[] InitializeTable(uint polynomial)
+ {
+ uint[] table = new uint[TableLength];
+ for (int i = 0; i < TableLength; i++)
+ {
+ uint entry = (uint)i;
+ for (int j = 0; j < 8; j++)
+ {
+ if ((entry & 1) == 1)
+ {
+ entry = (entry >> 1) ^ polynomial;
+ }
+ else
+ {
+ entry >>= 1;
+ }
+ }
+
+ table[i] = entry;
+ }
+
+ return table;
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Verifier.Crc32.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Verifier.Crc32.cs.meta
new file mode 100644
index 00000000..b683582b
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Verifier.Crc32.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9a7d0ce566dde9749b214b5ae065ac3d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Verifier.cs b/Assets/TEngine/Runtime/Utility/Utility.Verifier.cs
new file mode 100644
index 00000000..5fd036c6
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Verifier.cs
@@ -0,0 +1,188 @@
+using System;
+using System.IO;
+
+namespace TEngine
+{
+ public static partial class Utility
+ {
+ ///
+ /// 校验相关的实用函数。
+ ///
+ public static partial class Verifier
+ {
+ private const int CachedBytesLength = 0x1000;
+ private static readonly byte[] s_CachedBytes = new byte[CachedBytesLength];
+ private static readonly Crc32 s_Algorithm = new Crc32();
+
+ ///
+ /// 计算二进制流的 CRC32。
+ ///
+ /// 指定的二进制流。
+ /// 计算后的 CRC32。
+ public static int GetCrc32(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ return GetCrc32(bytes, 0, bytes.Length);
+ }
+
+ ///
+ /// 计算二进制流的 CRC32。
+ ///
+ /// 指定的二进制流。
+ /// 二进制流的偏移。
+ /// 二进制流的长度。
+ /// 计算后的 CRC32。
+ public static int GetCrc32(byte[] bytes, int offset, int length)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Bytes is invalid.");
+ }
+
+ if (offset < 0 || length < 0 || offset + length > bytes.Length)
+ {
+ throw new GameFrameworkException("Offset or length is invalid.");
+ }
+
+ s_Algorithm.HashCore(bytes, offset, length);
+ int result = (int)s_Algorithm.HashFinal();
+ s_Algorithm.Initialize();
+ return result;
+ }
+
+ ///
+ /// 计算二进制流的 CRC32。
+ ///
+ /// 指定的二进制流。
+ /// 计算后的 CRC32。
+ public static int GetCrc32(Stream stream)
+ {
+ if (stream == null)
+ {
+ throw new GameFrameworkException("Stream is invalid.");
+ }
+
+ while (true)
+ {
+ int bytesRead = stream.Read(s_CachedBytes, 0, CachedBytesLength);
+ if (bytesRead > 0)
+ {
+ s_Algorithm.HashCore(s_CachedBytes, 0, bytesRead);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ int result = (int)s_Algorithm.HashFinal();
+ s_Algorithm.Initialize();
+ Array.Clear(s_CachedBytes, 0, CachedBytesLength);
+ return result;
+ }
+
+ ///
+ /// 获取 CRC32 数值的二进制数组。
+ ///
+ /// CRC32 数值。
+ /// CRC32 数值的二进制数组。
+ public static byte[] GetCrc32Bytes(int crc32)
+ {
+ return new byte[] { (byte)((crc32 >> 24) & 0xff), (byte)((crc32 >> 16) & 0xff), (byte)((crc32 >> 8) & 0xff), (byte)(crc32 & 0xff) };
+ }
+
+ ///
+ /// 获取 CRC32 数值的二进制数组。
+ ///
+ /// CRC32 数值。
+ /// 要存放结果的数组。
+ public static void GetCrc32Bytes(int crc32, byte[] bytes)
+ {
+ GetCrc32Bytes(crc32, bytes, 0);
+ }
+
+ ///
+ /// 获取 CRC32 数值的二进制数组。
+ ///
+ /// CRC32 数值。
+ /// 要存放结果的数组。
+ /// CRC32 数值的二进制数组在结果数组内的起始位置。
+ public static void GetCrc32Bytes(int crc32, byte[] bytes, int offset)
+ {
+ if (bytes == null)
+ {
+ throw new GameFrameworkException("Result is invalid.");
+ }
+
+ if (offset < 0 || offset + 4 > bytes.Length)
+ {
+ throw new GameFrameworkException("Offset or length is invalid.");
+ }
+
+ bytes[offset] = (byte)((crc32 >> 24) & 0xff);
+ bytes[offset + 1] = (byte)((crc32 >> 16) & 0xff);
+ bytes[offset + 2] = (byte)((crc32 >> 8) & 0xff);
+ bytes[offset + 3] = (byte)(crc32 & 0xff);
+ }
+
+ internal static int GetCrc32(Stream stream, byte[] code, int length)
+ {
+ if (stream == null)
+ {
+ throw new GameFrameworkException("Stream is invalid.");
+ }
+
+ if (code == null)
+ {
+ throw new GameFrameworkException("Code is invalid.");
+ }
+
+ int codeLength = code.Length;
+ if (codeLength <= 0)
+ {
+ throw new GameFrameworkException("Code length is invalid.");
+ }
+
+ int bytesLength = (int)stream.Length;
+ if (length < 0 || length > bytesLength)
+ {
+ length = bytesLength;
+ }
+
+ int codeIndex = 0;
+ while (true)
+ {
+ int bytesRead = stream.Read(s_CachedBytes, 0, CachedBytesLength);
+ if (bytesRead > 0)
+ {
+ if (length > 0)
+ {
+ for (int i = 0; i < bytesRead && i < length; i++)
+ {
+ s_CachedBytes[i] ^= code[codeIndex++];
+ codeIndex %= codeLength;
+ }
+
+ length -= bytesRead;
+ }
+
+ s_Algorithm.HashCore(s_CachedBytes, 0, bytesRead);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ int result = (int)s_Algorithm.HashFinal();
+ s_Algorithm.Initialize();
+ Array.Clear(s_CachedBytes, 0, CachedBytesLength);
+ return result;
+ }
+ }
+ }
+}
diff --git a/Assets/TEngine/Runtime/Utility/Utility.Verifier.cs.meta b/Assets/TEngine/Runtime/Utility/Utility.Verifier.cs.meta
new file mode 100644
index 00000000..be93c9a2
--- /dev/null
+++ b/Assets/TEngine/Runtime/Utility/Utility.Verifier.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 968c23f2e5a22ec4f884c9932c8fa90b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: