using System; using System.Runtime.InteropServices; namespace MeatKit { public static class UnityNativeHelper { private delegate IntPtr StringAssignType(IntPtr ptr, string str, ulong len, IntPtr nul); private static readonly StringAssignType AssignNativeString; static UnityNativeHelper() { if (!EditorVersion.IsSupportedVersion) return; AssignNativeString = (StringAssignType) NativeHookManager.GetDelegateForFunctionPointer(EditorVersion.Current.FunctionOffsets.StringAssign); } /// /// Reads a native string structure from unmanaged memory /// /// The pointer to the structure /// Offset to the pointer /// A copy of the structure in managed memory public static string ReadNativeString(IntPtr ptr, int ofs) { // Apply the offset var real = (IntPtr) (ptr.ToInt64() + ofs); // Get the pointer to the string in memory var stringPointer = Marshal.ReadIntPtr(real); if (stringPointer == IntPtr.Zero) { // If the pointer is null, that means it's stored in the struct directly. // In this format, the string is 16 chars or less. var length = Marshal.ReadInt64(real, 24); return Marshal.PtrToStringAnsi((IntPtr) (real.ToInt64() + 8), (int) length); } else { // If it isn't null, we can just go out into that memory location and read it. var length = Marshal.ReadInt64(real, 24); return Marshal.PtrToStringAnsi(stringPointer, (int) length); } } /// /// Writes a managed string to the unmanaged memory location of a string structure /// /// The pointer to the structure in memory /// Offset to the pointer /// The string to write public static void WriteNativeString(IntPtr ptr, int ofs, string str) { // Apply the offset and call the assign function of native Unity code var real = (IntPtr) (ptr.ToInt64() + ofs); AssignNativeString(real, str, (ulong) str.Length, IntPtr.Zero); } } }