Beispiel #1
0
        /// <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);
        }
Beispiel #4
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #8
0
        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);
            }
        }