/// <summary> /// Version of TryCast without the generic restriction /// </summary> private static bool TryCast <T>(Il2CppObjectBase obj, out T t) { t = default; var nativeClassPtr = Il2CppClassPointerStore <T> .NativeClassPtr; if (nativeClassPtr == IntPtr.Zero) { MelonLogger.Warning($"{typeof(T)} is not an Il2Cpp reference type"); return(false); } var num = IL2CPP.il2cpp_object_get_class(obj.Pointer); if (!IL2CPP.il2cpp_class_is_assignable_from(nativeClassPtr, num)) { MelonLogger.Warning($"{obj.GetType()} is not a {typeof(T)}"); return(false); } if (RuntimeSpecificsStore.IsInjected(num)) { t = (T)ClassInjectorBase.GetMonoObjectFromIl2CppPointer(obj.Pointer); return(true); } var type = Il2CppClassPointerStore <T> .CreatedTypeRedirect; if ((object)type == null) { type = typeof(T); } t = (T)Activator.CreateInstance(type, obj.Pointer); return(true); }
/// <summary> /// Attempt to cast the object to the provided type. /// </summary> /// <param name="obj">The object you want to cast.</param> /// <param name="castTo">The Type you want to cast to.</param> /// <returns>The object, as the type (or a normal C# object) if successful or the input value if not.</returns> public static object Il2CppCast(this object obj, Type castTo) { if (!(obj is Il2CppSystem.Object ilObj)) { return(obj); } if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr)) { return(obj); } IntPtr castFromPtr = il2cpp_object_get_class(ilObj.Pointer); if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr)) { return(obj); } if (RuntimeSpecificsStore.IsInjected(castToPtr)) { return(UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer)); } return(Activator.CreateInstance(castTo, ilObj.Pointer)); }
public static Type GetActualType(this object obj) { if (obj == null) { return(null); } var type = obj.GetType(); #if CPP if (obj is Il2CppSystem.Object ilObject) { if (ilObject is CppType) { return(typeof(CppType)); } if (!string.IsNullOrEmpty(type.Namespace)) { // Il2CppSystem-namespace objects should just return GetType, // because using GetIl2CppType returns the System namespace type instead. if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem.")) { return(ilObject.GetType()); } } var il2cppType = ilObject.GetIl2CppType(); // check if type is injected IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer); if (RuntimeSpecificsStore.IsInjected(classPtr)) { var typeByName = GetTypeByName(il2cppType.FullName); if (typeByName != null) { return(typeByName); } } // this should be fine for all other il2cpp objects var getType = GetMonoType(il2cppType); if (getType != null) { return(getType); } } #endif return(type); }
public override Type GetActualType(object obj) { if (obj == null) { return(null); } var type = obj.GetType(); if (obj is Il2CppSystem.Object cppObject) { // weird specific case - if the object is an Il2CppSystem.Type, then return so manually. if (cppObject is CppType) { return(typeof(CppType)); } if (!string.IsNullOrEmpty(type.Namespace)) { // Il2CppSystem-namespace objects should just return GetType, // because using GetIl2CppType returns the System namespace type instead. if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem.")) { return(cppObject.GetType()); } } var cppType = cppObject.GetIl2CppType(); // check if type is injected IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer); if (RuntimeSpecificsStore.IsInjected(classPtr)) { var typeByName = ReflectionUtility.GetTypeByName(cppType.FullName); if (typeByName != null) { return(typeByName); } } // this should be fine for all other il2cpp objects var getType = GetMonoType(cppType); if (getType != null) { return(getType); } } return(type); }
internal override Type Internal_GetActualType(object obj) { if (obj == null) { return(null); } var type = obj.GetType(); try { if (IsString(obj)) { return(typeof(string)); } if (IsIl2CppPrimitive(type)) { return(il2cppPrimitivesToMono[type.FullName]); } if (obj is Il2CppSystem.Object cppObject) { var cppType = cppObject.GetIl2CppType(); // check if type is injected IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer); if (RuntimeSpecificsStore.IsInjected(classPtr)) { // Note: This will fail on injected subclasses. // - {Namespace}.{Class}.{Subclass} would be {Namespace}.{Subclass} when injected. // Not sure on solution yet. return(GetTypeByName(cppType.FullName) ?? type); } if (AllTypes.TryGetValue(cppType.FullName, out Type primitive) && primitive.IsPrimitive) { return(primitive); } return(GetUnhollowedType(cppType) ?? type); } } catch (Exception ex) { ExplorerCore.LogWarning("Exception in IL2CPP GetActualType: " + ex); } return(type); }
public static object Il2CppCast(object obj, Type castTo) { if (!(obj is Il2CppSystem.Object ilObj)) { return(obj); } if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) { return(obj as System.Object); } IntPtr castToPtr; if (!ClassPointers.ContainsKey(castTo)) { castToPtr = (IntPtr)typeof(Il2CppClassPointerStore <>) .MakeGenericType(new Type[] { castTo }) .GetField("NativeClassPtr", BF.Public | BF.Static) .GetValue(null); ClassPointers.Add(castTo, castToPtr); } else { castToPtr = ClassPointers[castTo]; } if (castToPtr == IntPtr.Zero) { return(obj); } var classPtr = il2cpp_object_get_class(ilObj.Pointer); if (!il2cpp_class_is_assignable_from(castToPtr, classPtr)) { return(obj); } if (RuntimeSpecificsStore.IsInjected(castToPtr)) { return(UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer)); } return(Activator.CreateInstance(castTo, ilObj.Pointer)); }
/// <summary> /// WARNING: Pubternal API (internal). Do not use. May change during any update. /// </summary> /// <typeparam name="TObject"></typeparam> /// <param name="obj"></param> /// <param name="castedObject"></param> /// <returns></returns> public static bool TryCastTo <TObject>(this object obj, out TObject castedObject) { if (obj is TObject c) { castedObject = c; return(true); } #if IL2CPP if (obj is Il2CppObjectBase il2cppObject) { IntPtr nativeClassPtr = Il2CppClassPointerStore <TObject> .NativeClassPtr; if (nativeClassPtr == IntPtr.Zero) { throw new ArgumentException($"{typeof( TObject )} is not an Il2Cpp reference type"); } var instancePointer = il2cppObject.Pointer; IntPtr intPtr = IL2CPP.il2cpp_object_get_class(instancePointer); if (!IL2CPP.il2cpp_class_is_assignable_from(nativeClassPtr, intPtr)) { castedObject = default; return(false); } if (RuntimeSpecificsStore.IsInjected(intPtr)) { castedObject = (TObject)UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(instancePointer); return(castedObject != null); } castedObject = Il2CppUtilities.Factory <TObject> .CreateProxyComponent(instancePointer); return(castedObject != null); } #endif castedObject = default; return(false); }
public static bool Is <T>(this Il2CppObjectBase obj) { var nestedTypeClassPointer = Il2CppClassPointerStore <T> .NativeClassPtr; if (nestedTypeClassPointer == IntPtr.Zero) { throw new ArgumentException($"{typeof(T)} is not an Il2Cpp reference type"); } var ownClass = IL2CPP.il2cpp_object_get_class(obj.Pointer); if (!IL2CPP.il2cpp_class_is_assignable_from(nestedTypeClassPointer, ownClass)) { return(false); } if (RuntimeSpecificsStore.IsInjected(ownClass)) { return(ClassInjectorBase.GetMonoObjectFromIl2CppPointer(obj.Pointer) is T); } return(true); }
public bool IsInjectedType(Type type) { IntPtr ptr = GetClassPointerForType(type); return(ptr != IntPtr.Zero && RuntimeSpecificsStore.IsInjected(ptr)); }
public static void RegisterTypeInIl2Cpp <T>() where T : class { var type = typeof(T); if (type.IsGenericType || type.IsGenericTypeDefinition) { throw new ArgumentException($"Type {type} is generic and can't be used in il2cpp"); } var currentPointer = Il2CppClassPointerStore <T> .NativeClassPtr; if (currentPointer != IntPtr.Zero) { throw new ArgumentException($"Type {type} is already registered in il2cpp"); } var baseType = type.BaseType; var baseClassPointer = UnityVersionHandler.Wrap((Il2CppClass *)ReadClassPointerForType(baseType)); if (baseClassPointer == null) { throw new ArgumentException($"Base class {baseType} of class {type} is not registered in il2cpp"); } if ((baseClassPointer.Bitfield1 & ClassBitfield1.valuetype) != 0 || (baseClassPointer.Bitfield1 & ClassBitfield1.enumtype) != 0) { throw new ArgumentException($"Base class {baseType} is value type and can't be inherited from"); } if ((baseClassPointer.Bitfield1 & ClassBitfield1.is_generic) != 0) { throw new ArgumentException($"Base class {baseType} is generic and can't be inherited from"); } if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED) != 0) { throw new ArgumentException($"Base class {baseType} is sealed and can't be inherited from"); } if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_INTERFACE) != 0) { throw new ArgumentException($"Base class {baseType} is an interface and can't be inherited from"); } lock (InjectedTypes) if (!InjectedTypes.Add(typeof(T).FullName)) { throw new ArgumentException($"Type with FullName {typeof(T).FullName} is already injected. Don't inject the same type twice, or use a different namespace"); } if (ourOriginalTypeToClassMethod == null) { HookClassFromType(); } var classPointer = UnityVersionHandler.NewClass(baseClassPointer.VtableCount); classPointer.Image = FakeImage; classPointer.Parent = baseClassPointer.ClassPointer; classPointer.ElementClass = classPointer.Class = classPointer.CastClass = classPointer.ClassPointer; classPointer.NativeSize = -1; classPointer.ActualSize = classPointer.InstanceSize = baseClassPointer.InstanceSize + (uint)IntPtr.Size; classPointer.Bitfield1 = ClassBitfield1.initialized | ClassBitfield1.initialized_and_no_error | ClassBitfield1.size_inited; classPointer.Bitfield2 = ClassBitfield2.has_finalize | ClassBitfield2.is_vtable_initialized; classPointer.Name = Marshal.StringToHGlobalAnsi(type.Name); classPointer.Namespace = Marshal.StringToHGlobalAnsi(type.Namespace); classPointer.ThisArg.type = classPointer.ByValArg.type = Il2CppTypeEnum.IL2CPP_TYPE_CLASS; classPointer.ThisArg.mods_byref_pin = 64; classPointer.Flags = baseClassPointer.Flags; // todo: adjust flags? var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray(); var methodCount = 2 + eligibleMethods.Length; // 1 is the finalizer, 1 is empty ctor classPointer.MethodCount = (ushort)methodCount; var methodPointerArray = (Il2CppMethodInfo **)Marshal.AllocHGlobal(methodCount * IntPtr.Size); classPointer.Methods = methodPointerArray; methodPointerArray[0] = ConvertStaticMethod(FinalizeDelegate, "Finalize", classPointer); methodPointerArray[1] = ConvertStaticMethod(CreateEmptyCtor(type), ".ctor", classPointer); for (var i = 0; i < eligibleMethods.Length; i++) { var methodInfo = eligibleMethods[i]; methodPointerArray[i + 2] = ConvertMethodInfo(methodInfo, classPointer); } var vTablePointer = (VirtualInvokeData *)classPointer.VTable; var baseVTablePointer = (VirtualInvokeData *)baseClassPointer.VTable; classPointer.VtableCount = baseClassPointer.VtableCount; for (var i = 0; i < classPointer.VtableCount; i++) { vTablePointer[i] = baseVTablePointer[i]; if (Marshal.PtrToStringAnsi(vTablePointer[i].method->name) == "Finalize") // slot number is not static { vTablePointer[i].method = methodPointerArray[0]; vTablePointer[i].methodPtr = methodPointerArray[0]->methodPointer; } } var newCounter = Interlocked.Decrement(ref ourClassOverrideCounter); FakeTokenClasses[newCounter] = classPointer.Pointer; classPointer.ByValArg.data = classPointer.ThisArg.data = (IntPtr)newCounter; RuntimeSpecificsStore.SetClassInfo(classPointer.Pointer, true, true); Il2CppClassPointerStore <T> .NativeClassPtr = classPointer.Pointer; LogSupport.Info($"Registered mono type {typeof(T)} in il2cpp domain"); }
internal override object Internal_TryCast(object obj, Type castTo) { if (obj == null) { return(null); } var type = obj.GetType(); if (type == castTo) { return(obj); } // from structs if (type.IsValueType) { // from il2cpp primitive to system primitive if (IsIl2CppPrimitive(type) && castTo.IsPrimitive) { return(MakeMonoPrimitive(obj)); } // from system primitive to il2cpp primitive else if (IsIl2CppPrimitive(castTo)) { return(MakeIl2CppPrimitive(castTo, obj)); } // from other structs to il2cpp object else if (typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) { return(BoxIl2CppObject(obj)); } else { return(obj); } } // from string to il2cpp.Object / il2cpp.String if (obj is string && typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) { return(BoxStringToType(obj, castTo)); } // from il2cpp objects... if (!(obj is Il2CppObjectBase cppObj)) { return(obj); } // from Il2CppSystem.Object to a struct if (castTo.IsValueType) { return(UnboxCppObject(cppObj, castTo)); } // or to system string else if (castTo == typeof(string)) { return(UnboxString(obj)); } if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr)) { return(obj); } // Casting from il2cpp object to il2cpp object... IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer); if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr)) { return(null); } if (RuntimeSpecificsStore.IsInjected(castToPtr)) { var injectedObj = UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer); return(injectedObj ?? obj); } try { return(Activator.CreateInstance(castTo, cppObj.Pointer)); } catch { return(obj); } }