diff --git a/UnityUtilities/UnityEngine.Il2CppImageConversionManager/Il2CppImageConversionManager.cs b/UnityUtilities/UnityEngine.Il2CppImageConversionManager/Il2CppImageConversionManager.cs index 01128bc5..53d9f8fc 100644 --- a/UnityUtilities/UnityEngine.Il2CppImageConversionManager/Il2CppImageConversionManager.cs +++ b/UnityUtilities/UnityEngine.Il2CppImageConversionManager/Il2CppImageConversionManager.cs @@ -1,63 +1,183 @@ using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; +using MelonLoader.InternalUtils; using System; +using System.Runtime.InteropServices; namespace UnityEngine; public class Il2CppImageConversionManager { + // Implemented class/struct needed for unmarshalling of data in Unity 6. + // New struct needed for Unity 6 function calls. + [StructLayout(LayoutKind.Sequential)] + public struct ManagedSpanWrapper + { + public unsafe void* begin; + public int length; + } + internal static class BindingsAllocator + { + public struct NativeOwnedMemory + { + public unsafe void* data; + } + + static BindingsAllocator() + { + FreeNativeOwnedMemoryDelegateField = IL2CPP.ResolveICall("UnityEngine.Bindings.BindingsAllocator::FreeNativeOwnedMemory"); + } + + public unsafe static void FreeNativeOwnedMemory(void* ptr) => FreeNativeOwnedMemoryDelegateField(ptr); + + public unsafe static void* GetNativeOwnedDataPointer(void* ptr) + { + return ((NativeOwnedMemory*)ptr)->data; + } + + unsafe delegate void FreeNativeOwnedMemoryDelegate(void* ptr); + static FreeNativeOwnedMemoryDelegate FreeNativeOwnedMemoryDelegateField; + } + public ref struct BlittableArrayWrapper + { + enum UpdateFlags + { + NoUpdateNeeded, + SizeChanged, + DataIsNativePointer, + DataIsNativeOwnedMemory, + DataIsEmpty, + DataIsNull + } + + unsafe void* data; + int size; + UpdateFlags updateFlags; + + public unsafe void Unmarshal(ref T[] array) where T : struct + { + switch (updateFlags) + { + case BlittableArrayWrapper.UpdateFlags.SizeChanged: + case BlittableArrayWrapper.UpdateFlags.DataIsNativePointer: + array = new Span(data, size).ToArray(); + break; + case BlittableArrayWrapper.UpdateFlags.DataIsNativeOwnedMemory: + array = new Span(BindingsAllocator.GetNativeOwnedDataPointer(data), size).ToArray(); + BindingsAllocator.FreeNativeOwnedMemory(data); + break; + case BlittableArrayWrapper.UpdateFlags.DataIsEmpty: + array = Array.Empty(); + break; + case BlittableArrayWrapper.UpdateFlags.DataIsNull: + array = null; + break; + } + } + } + static Il2CppImageConversionManager() { - EncodeToTGADelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToTGA"); - EncodeToEXRDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToEXR"); - EncodeToPNGDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToPNG"); - EncodeToJPGDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToJPG"); - LoadImageDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage"); + if (UnityInformationHandler.EngineVersion.Major >= 6000) + { + EncodeToTGADelegateField_Unity6 = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToTGA_Injected"); + EncodeToEXRDelegateField_Unity6 = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToEXR_Injected"); + EncodeToPNGDelegateField_Unity6 = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToPNG_Injected"); + EncodeToJPGDelegateField_Unity6 = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToJPG_Injected"); + LoadImageDelegateField_Unity6 = IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage_Injected"); + } + else + { + EncodeToTGADelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToTGA"); + EncodeToEXRDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToEXR"); + EncodeToPNGDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToPNG"); + EncodeToJPGDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToJPG"); + LoadImageDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage"); + } } public static Il2CppStructArray EncodeToTGA(Texture2D tex) { if (tex == null) throw new ArgumentException("The texture cannot be null."); - if (EncodeToTGADelegateField == null) - throw new NullReferenceException("The EncodeToTGADelegateField cannot be null."); - Il2CppStructArray il2CppStructArray; - var encodeToTGADelegateField = EncodeToTGADelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex)); - if (encodeToTGADelegateField != IntPtr.Zero) - il2CppStructArray = new Il2CppStructArray(encodeToTGADelegateField); + if (UnityInformationHandler.EngineVersion.Major >= 6000) + { + if (EncodeToTGADelegateField_Unity6 == null) + throw new NullReferenceException("The EncodeToTGADelegateField_Unity6 cannot be null."); + + EncodeToTGADelegateField_Unity6(tex.m_CachedPtr, out BlittableArrayWrapper arrayWrapper); + byte[] array = null; + arrayWrapper.Unmarshal(ref array); + + return array; + } else - il2CppStructArray = null; - return il2CppStructArray; + { + if (EncodeToTGADelegateField == null) + throw new NullReferenceException("The EncodeToTGADelegateField cannot be null."); + + var arrayPtr = EncodeToTGADelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex)); + if (arrayPtr != IntPtr.Zero) + return new Il2CppStructArray(arrayPtr); + else + return null; + } } public static Il2CppStructArray EncodeToPNG(Texture2D tex) { if (tex == null) throw new ArgumentException("The texture cannot be null."); - if (EncodeToPNGDelegateField == null) - throw new NullReferenceException("The EncodeToPNGDelegateField cannot be null."); - Il2CppStructArray il2CppStructArray; - var encodeToPNGDelegateField = EncodeToPNGDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex)); - if (encodeToPNGDelegateField != IntPtr.Zero) - il2CppStructArray = new Il2CppStructArray(encodeToPNGDelegateField); + if (UnityInformationHandler.EngineVersion.Major >= 6000) + { + if (EncodeToPNGDelegateField_Unity6 == null) + throw new NullReferenceException("The EncodeToPNGDelegateField_Unity6 cannot be null."); + + EncodeToPNGDelegateField_Unity6(tex.m_CachedPtr, out BlittableArrayWrapper arrayWrapper); + byte[] array = null; + arrayWrapper.Unmarshal(ref array); + + return array; + } else - il2CppStructArray = null; - return il2CppStructArray; + { + if (EncodeToPNGDelegateField == null) + throw new NullReferenceException("The EncodeToPNGDelegateField cannot be null."); + + var arrayPtr = EncodeToPNGDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex)); + if (arrayPtr != IntPtr.Zero) + return new Il2CppStructArray(arrayPtr); + else + return null; + } } public static Il2CppStructArray EncodeToJPG(Texture2D tex, int quality) { if (tex == null) throw new ArgumentException("The texture cannot be null."); - if (EncodeToJPGDelegateField == null) - throw new NullReferenceException("The EncodeToJPGDelegateField cannot be null."); - Il2CppStructArray il2CppStructArray; - var encodeToJPGDelegateField = EncodeToJPGDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex), quality); - if (encodeToJPGDelegateField != IntPtr.Zero) - il2CppStructArray = new Il2CppStructArray(encodeToJPGDelegateField); + if (UnityInformationHandler.EngineVersion.Major >= 6000) + { + if (EncodeToJPGDelegateField_Unity6 == null) + throw new NullReferenceException("The EncodeToJPGDelegateField_Unity6 cannot be null."); + + EncodeToJPGDelegateField_Unity6(tex.m_CachedPtr, quality, out BlittableArrayWrapper arrayWrapper); + byte[] array = null; + arrayWrapper.Unmarshal(ref array); + + return array; + } else - il2CppStructArray = null; - return il2CppStructArray; + { + if (EncodeToJPGDelegateField == null) + throw new NullReferenceException("The EncodeToJPGDelegateField cannot be null."); + + var arrayPtr = EncodeToJPGDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex), quality); + if (arrayPtr != IntPtr.Zero) + return new Il2CppStructArray(arrayPtr); + else + return null; + } } public static Il2CppStructArray EncodeToJPG(Texture2D tex) => EncodeToJPG(tex, 75); @@ -65,15 +185,28 @@ public class Il2CppImageConversionManager { if (tex == null) throw new ArgumentException("The texture cannot be null."); - if (EncodeToEXRDelegateField == null) - throw new NullReferenceException("The EncodeToEXRDelegateField cannot be null."); - Il2CppStructArray il2CppStructArray; - var encodeToEXRDelegateField = EncodeToEXRDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex), flags); - if (encodeToEXRDelegateField != IntPtr.Zero) - il2CppStructArray = new Il2CppStructArray(encodeToEXRDelegateField); + if (UnityInformationHandler.EngineVersion.Major >= 6000) + { + if (EncodeToEXRDelegateField_Unity6 == null) + throw new NullReferenceException("The EncodeToEXRDelegateField_Unity6 cannot be null."); + + EncodeToEXRDelegateField_Unity6(tex.m_CachedPtr, flags, out BlittableArrayWrapper arrayWrapper); + byte[] array = null; + arrayWrapper.Unmarshal(ref array); + + return array; + } else - il2CppStructArray = null; - return il2CppStructArray; + { + if (EncodeToEXRDelegateField == null) + throw new NullReferenceException("The EncodeToEXRDelegateField cannot be null."); + + var arrayPtr = EncodeToEXRDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex), flags); + if (arrayPtr != IntPtr.Zero) + return new Il2CppStructArray(arrayPtr); + else + return null; + } } public static Il2CppStructArray EncodeToEXR(Texture2D tex) => EncodeToEXR(tex, 0); @@ -83,9 +216,29 @@ public class Il2CppImageConversionManager throw new ArgumentException("The texture cannot be null."); if (data == null) throw new ArgumentException("The data cannot be null."); - if (LoadImageDelegateField == null) - throw new NullReferenceException("The LoadImageDelegateField cannot be null."); - return LoadImageDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex), IL2CPP.Il2CppObjectBaseToPtr(data), markNonReadable); + + if (UnityInformationHandler.EngineVersion.Major >= 6000) + { + if (LoadImageDelegateField_Unity6 == null) + throw new NullReferenceException("The LoadImageDelegateField_Unity6 cannot be null."); + + unsafe + { + var arrayPtr = IL2CPP.Il2CppObjectBaseToPtr(data); + var span = new ManagedSpanWrapper + { + begin = (void*)(arrayPtr + 0x20), // Skip the IL2CPP object header. + length = data.Length + }; + return LoadImageDelegateField_Unity6(tex.m_CachedPtr, ref span, markNonReadable); + } + } + else + { + if (LoadImageDelegateField == null) + throw new NullReferenceException("The LoadImageDelegateField cannot be null."); + return LoadImageDelegateField(IL2CPP.Il2CppObjectBaseToPtr(tex), IL2CPP.Il2CppObjectBaseToPtr(data), markNonReadable); + } } public static bool LoadImage(Texture2D tex, Il2CppStructArray data) => LoadImage(tex, data, false); @@ -98,4 +251,14 @@ public class Il2CppImageConversionManager private readonly static TextureOnlyDelegate EncodeToPNGDelegateField; private readonly static TextureAndQualityDelegate EncodeToJPGDelegateField; private readonly static LoadImageDelegate LoadImageDelegateField; + // ---------- + private delegate void TextureOnlyDelegate_Unity6(IntPtr tex, out BlittableArrayWrapper ret); + private delegate void TextureAndQualityDelegate_Unity6(IntPtr tex, int quality, out BlittableArrayWrapper ret); + private delegate void TextureAndFlagDelegate_Unity6(IntPtr tex, Texture2D.EXRFlags flags, out BlittableArrayWrapper ret); + private delegate bool LoadImageDelegate_Unity6(IntPtr tex, ref ManagedSpanWrapper data, bool markNonReadable); + private readonly static TextureAndFlagDelegate_Unity6 EncodeToEXRDelegateField_Unity6; + private readonly static TextureOnlyDelegate_Unity6 EncodeToTGADelegateField_Unity6; + private readonly static TextureOnlyDelegate_Unity6 EncodeToPNGDelegateField_Unity6; + private readonly static TextureAndQualityDelegate_Unity6 EncodeToJPGDelegateField_Unity6; + private readonly static LoadImageDelegate_Unity6 LoadImageDelegateField_Unity6; } \ No newline at end of file diff --git a/UnityUtilities/UnityEngine.Il2CppImageConversionManager/UnityEngine.Il2CppImageConversionManager.csproj b/UnityUtilities/UnityEngine.Il2CppImageConversionManager/UnityEngine.Il2CppImageConversionManager.csproj index 0dfbb9b7..cef410f5 100644 --- a/UnityUtilities/UnityEngine.Il2CppImageConversionManager/UnityEngine.Il2CppImageConversionManager.csproj +++ b/UnityUtilities/UnityEngine.Il2CppImageConversionManager/UnityEngine.Il2CppImageConversionManager.csproj @@ -8,6 +8,7 @@ +