private static unsafe IntPtr FindOriginalVTableOwner(IntPtr baseMostClass, IntPtr ownerClass, IntPtr functionAddress, int vtableIndex) { // Don't search lower than the target base if (ownerClass == baseMostClass) { return(ownerClass); } IntPtr originalOwner = ownerClass; while ((ownerClass = Native_UClass.GetSuperClass(ownerClass)) != IntPtr.Zero) { IntPtr obj = Native_UClass.GetDefaultObject(ownerClass, true); IntPtr *vtable = *(IntPtr **)obj; if (vtable[vtableIndex] == functionAddress) { originalOwner = ownerClass; } // Don't search lower than the target base if (ownerClass == baseMostClass) { return(ownerClass); } } return(originalOwner); }
private void MoveToTransientPackage(IntPtr obj) { // Copy of UObjectBase.cpp UClassCompiledInDefer // Check if rooted? Native_UObjectBaseUtility.RemoveFromRoot(obj); Native_UObjectBaseUtility.ClearFlags(obj, EObjectFlags.Standalone | EObjectFlags.Public); IntPtr defaultObject = Native_UClass.GetDefaultObject(obj, false); if (defaultObject != IntPtr.Zero) { // Check if rooted? Native_UObjectBaseUtility.RemoveFromRoot(defaultObject); Native_UObjectBaseUtility.ClearFlags(defaultObject, EObjectFlags.Standalone | EObjectFlags.Public); } FName oldClassRename = NativeReflection.MakeUniqueObjectName(NativeReflection.GetTransientPackage(), Native_UObjectBase.GetClass(obj), new FName("USharpHotReload_" + Native_UObjectBase.GetFName(obj))); using (FStringUnsafe oldClassRenameUnsafe = new FStringUnsafe(oldClassRename.ToString())) { Native_UObject.Rename(obj, ref oldClassRenameUnsafe.Array, NativeReflection.GetTransientPackage(), ERenameFlags.None); } Native_UObjectBaseUtility.SetFlags(obj, EObjectFlags.Transient); Native_UObjectBaseUtility.AddToRoot(obj); }
public static unsafe void Load() { vtableRedirects = new List <FunctionRedirect>(); AddVTableRedirects(); // We have three classes UDummyObject3 : UDummyObject2 : UDummyObject1 : UObject // // UDummyObject1 overrides function "X" // UDummyObject2 doesn't function "X" // UDummyObject3 overrides function "X" // // Scan the vtable for each dummy object, search for an entry where vtable entry 1==2 && 1!=3 // - We can assume this entry is our desired vtable index // - This may break down in situations where there is multiple inheritance // - If this fails to complete properly this will result in a crash (or worse) foreach (FunctionRedirect redirect in vtableRedirects) { IntPtr dummyClass1 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "1"); IntPtr dummyClass2 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "2"); IntPtr dummyClass3 = NativeReflection.GetClass("/Script/USharp." + redirect.DummyName + "3"); IntPtr dummyObject1 = Native_UClass.GetDefaultObject(dummyClass1, true); IntPtr dummyObject2 = Native_UClass.GetDefaultObject(dummyClass2, true); IntPtr dummyObject3 = Native_UClass.GetDefaultObject(dummyClass3, true); IntPtr *dummyVTable1 = *(IntPtr **)dummyObject1; IntPtr *dummyVTable2 = *(IntPtr **)dummyObject2; IntPtr *dummyVTable3 = *(IntPtr **)dummyObject3; for (int i = 0; i < int.MaxValue; i++) { IntPtr dummyFunc1 = dummyVTable1[i]; IntPtr dummyFunc2 = dummyVTable2[i]; IntPtr dummyFunc3 = dummyVTable3[i]; if (dummyFunc1 == dummyFunc2 && dummyFunc1 != dummyFunc3) { redirect.NativeCallback = dummyFunc1; redirect.VTableIndex = i; break; } } } }
/// <summary> /// Get the default object from the class /// </summary> /// <param name="createIfNeeded">if true (default) then the CDO is created if it is NULL.</param> /// <returns>the CDO for this class</returns> public UObject GetDefaultObject(bool createIfNeeded = true) { return(GCHelper.Find(Native_UClass.GetDefaultObject(Address, createIfNeeded))); }