mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-26 10:03:10 +08:00
Cleanup project structure
Restructured the project somewhat and cleaned up classes so that things are where they belong. Created "Helpers" folder and put appropriate helper classes in there. Important things: - The "GameObject path" methods are now extension methods on UnityEngine.Transform - Removed AccessTools (Reflection helpers) as there was no use of it. Replaced with ReflectionHelpers class. - Some improvements to the "Object Reflection" window, should be a bit faster now. Code cleaned up significantly.
This commit is contained in:
@ -2,13 +2,8 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using UnhollowerBaseLib;
|
|
||||||
using UnhollowerRuntimeLib;
|
|
||||||
using Harmony;
|
|
||||||
using Il2CppSystem.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -17,53 +12,23 @@ namespace Explorer
|
|||||||
// consts
|
// consts
|
||||||
|
|
||||||
public const string ID = "com.sinai.cppexplorer";
|
public const string ID = "com.sinai.cppexplorer";
|
||||||
public const string VERSION = "1.3.3";
|
public const string VERSION = "1.3.5";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
|
|
||||||
|
public const string NAME = "IL2CPP Runtime Explorer"
|
||||||
#if Release_Unity2018
|
#if Release_Unity2018
|
||||||
public const string NAME = "IL2CPP Runtime Explorer (Unity 2018)";
|
+ " (Unity 2018)"
|
||||||
#else
|
|
||||||
public const string NAME = "IL2CPP Runtime Explorer";
|
|
||||||
#endif
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
// fields
|
// fields
|
||||||
|
|
||||||
public static CppExplorer Instance;
|
public static CppExplorer Instance;
|
||||||
private string m_objUnderMouseName = "";
|
|
||||||
private Camera m_main;
|
|
||||||
|
|
||||||
// props
|
// props
|
||||||
|
|
||||||
public static bool ShowMenu { get; set; } = false;
|
public static bool ShowMenu { get; set; } = false;
|
||||||
public static int ArrayLimit { get; set; } = 20;
|
public static int ArrayLimit { get; set; } = 20;
|
||||||
public bool MouseInspect { get; set; } = false;
|
|
||||||
|
|
||||||
// prop helpers
|
|
||||||
|
|
||||||
public static Il2CppSystem.Type GameObjectType => Il2CppType.Of<GameObject>();
|
|
||||||
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>();
|
|
||||||
public static Il2CppSystem.Type ObjectType => Il2CppType.Of<UnityEngine.Object>();
|
|
||||||
public static Il2CppSystem.Type ComponentType => Il2CppType.Of<Component>();
|
|
||||||
|
|
||||||
public static string ActiveSceneName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Camera MainCamera
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (m_main == null)
|
|
||||||
{
|
|
||||||
m_main = Camera.main;
|
|
||||||
}
|
|
||||||
return m_main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
|
|
||||||
@ -76,21 +41,15 @@ namespace Explorer
|
|||||||
new MainMenu();
|
new MainMenu();
|
||||||
new WindowManager();
|
new WindowManager();
|
||||||
|
|
||||||
// done init
|
|
||||||
ShowMenu = true;
|
ShowMenu = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On scene change
|
|
||||||
public override void OnLevelWasLoaded(int level)
|
public override void OnLevelWasLoaded(int level)
|
||||||
{
|
{
|
||||||
if (ScenePage.Instance != null)
|
ScenePage.Instance?.OnSceneChange();
|
||||||
{
|
SearchPage.Instance?.OnSceneChange();
|
||||||
ScenePage.Instance.OnSceneChange();
|
|
||||||
SearchPage.Instance.OnSceneChange();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
|
||||||
public override void OnUpdate()
|
public override void OnUpdate()
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.F7))
|
if (Input.GetKeyDown(KeyCode.F7))
|
||||||
@ -109,43 +68,7 @@ namespace Explorer
|
|||||||
MainMenu.Instance.Update();
|
MainMenu.Instance.Update();
|
||||||
WindowManager.Instance.Update();
|
WindowManager.Instance.Update();
|
||||||
|
|
||||||
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1))
|
InspectUnderMouse.Update();
|
||||||
{
|
|
||||||
MouseInspect = !MouseInspect;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MouseInspect)
|
|
||||||
{
|
|
||||||
InspectUnderMouse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (MouseInspect)
|
|
||||||
{
|
|
||||||
MouseInspect = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InspectUnderMouse()
|
|
||||||
{
|
|
||||||
Ray ray = MainCamera.ScreenPointToRay(Input.mousePosition);
|
|
||||||
|
|
||||||
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
|
|
||||||
{
|
|
||||||
var obj = hit.transform.gameObject;
|
|
||||||
|
|
||||||
m_objUnderMouseName = GetGameObjectPath(obj.transform);
|
|
||||||
|
|
||||||
if (Input.GetMouseButtonDown(0))
|
|
||||||
{
|
|
||||||
MouseInspect = false;
|
|
||||||
m_objUnderMouseName = "";
|
|
||||||
|
|
||||||
WindowManager.InspectObject(obj, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_objUnderMouseName = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,79 +79,7 @@ namespace Explorer
|
|||||||
MainMenu.Instance.OnGUI();
|
MainMenu.Instance.OnGUI();
|
||||||
WindowManager.Instance.OnGUI();
|
WindowManager.Instance.OnGUI();
|
||||||
|
|
||||||
if (MouseInspect)
|
InspectUnderMouse.OnGUI();
|
||||||
{
|
|
||||||
if (m_objUnderMouseName != "")
|
|
||||||
{
|
|
||||||
var pos = Input.mousePosition;
|
|
||||||
var rect = new Rect(
|
|
||||||
pos.x - (Screen.width / 2), // x
|
|
||||||
Screen.height - pos.y - 50, // y
|
|
||||||
Screen.width, // w
|
|
||||||
50 // h
|
|
||||||
);
|
|
||||||
|
|
||||||
var origAlign = GUI.skin.label.alignment;
|
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
|
||||||
|
|
||||||
//shadow text
|
|
||||||
GUI.Label(rect, $"<color=black>{m_objUnderMouseName}</color>");
|
|
||||||
//white text
|
|
||||||
GUI.Label(new Rect(rect.x - 1, rect.y + 1, rect.width, rect.height), m_objUnderMouseName);
|
|
||||||
|
|
||||||
GUI.skin.label.alignment = origAlign;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ************** public helpers **************
|
|
||||||
|
|
||||||
public static object Il2CppCast(object obj, Type castTo)
|
|
||||||
{
|
|
||||||
var method = typeof(Il2CppObjectBase).GetMethod("TryCast");
|
|
||||||
var generic = method.MakeGenericMethod(castTo);
|
|
||||||
return generic.Invoke(obj, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetGameObjectPath(Transform _transform)
|
|
||||||
{
|
|
||||||
return GetGameObjectPath(_transform, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetGameObjectPath(Transform _transform, bool _includeItemName)
|
|
||||||
{
|
|
||||||
string text = _includeItemName ? ("/" + _transform.name) : "";
|
|
||||||
GameObject gameObject = _transform.gameObject;
|
|
||||||
while (gameObject.transform.parent != null)
|
|
||||||
{
|
|
||||||
gameObject = gameObject.transform.parent.gameObject;
|
|
||||||
text = "/" + gameObject.name + text;
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type GetType(string _type)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (asm.GetType(_type) is Type type)
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,23 +137,26 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CppExplorer.cs" />
|
<Compile Include="CppExplorer.cs" />
|
||||||
<Compile Include="Inspectors\Reflection\FieldInfoHolder.cs" />
|
<Compile Include="Helpers\ReflectionHelpers.cs" />
|
||||||
<Compile Include="Inspectors\Reflection\MemberInfoHolder.cs" />
|
<Compile Include="Helpers\UIHelpers.cs" />
|
||||||
<Compile Include="Inspectors\Reflection\PropertyInfoHolder.cs" />
|
<Compile Include="Helpers\UnityHelpers.cs" />
|
||||||
<Compile Include="Inspectors\UIWindow.cs" />
|
<Compile Include="MainMenu\InspectUnderMouse.cs" />
|
||||||
|
<Compile Include="Windows\Reflection\FieldInfoHolder.cs" />
|
||||||
|
<Compile Include="Windows\Reflection\MemberInfoHolder.cs" />
|
||||||
|
<Compile Include="Windows\Reflection\PropertyInfoHolder.cs" />
|
||||||
|
<Compile Include="Windows\UIWindow.cs" />
|
||||||
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
|
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
|
||||||
<Compile Include="MainMenu\Pages\Console\REPL.cs" />
|
<Compile Include="MainMenu\Pages\Console\REPL.cs" />
|
||||||
<Compile Include="MainMenu\Pages\Console\REPLHelper.cs" />
|
<Compile Include="MainMenu\Pages\Console\REPLHelper.cs" />
|
||||||
<Compile Include="MainMenu\Pages\WindowPage.cs" />
|
<Compile Include="MainMenu\Pages\WindowPage.cs" />
|
||||||
<Compile Include="WindowManager.cs" />
|
<Compile Include="Windows\WindowManager.cs" />
|
||||||
<Compile Include="MainMenu\MainMenu.cs" />
|
<Compile Include="MainMenu\MainMenu.cs" />
|
||||||
<Compile Include="Inspectors\GameObjectWindow.cs" />
|
<Compile Include="Windows\GameObjectWindow.cs" />
|
||||||
<Compile Include="Inspectors\ReflectionWindow.cs" />
|
<Compile Include="Windows\ReflectionWindow.cs" />
|
||||||
<Compile Include="MainMenu\Pages\ScenePage.cs" />
|
<Compile Include="MainMenu\Pages\ScenePage.cs" />
|
||||||
<Compile Include="MainMenu\Pages\SearchPage.cs" />
|
<Compile Include="MainMenu\Pages\SearchPage.cs" />
|
||||||
<Compile Include="UIStyles.cs" />
|
<Compile Include="Helpers\UIStyles.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="utils\AccessTools.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
105
src/Helpers/ReflectionHelpers.cs
Normal file
105
src/Helpers/ReflectionHelpers.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnhollowerBaseLib;
|
||||||
|
using UnhollowerRuntimeLib;
|
||||||
|
using UnityEngine;
|
||||||
|
using BF = System.Reflection.BindingFlags;
|
||||||
|
using ILBF = Il2CppSystem.Reflection.BindingFlags;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
public class ReflectionHelpers
|
||||||
|
{
|
||||||
|
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||||
|
public static ILBF CommonFlags_IL = ILBF.Public | ILBF.NonPublic | ILBF.Instance | ILBF.Static;
|
||||||
|
|
||||||
|
public static Il2CppSystem.Type GameObjectType => Il2CppType.Of<GameObject>();
|
||||||
|
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>();
|
||||||
|
public static Il2CppSystem.Type ObjectType => Il2CppType.Of<UnityEngine.Object>();
|
||||||
|
public static Il2CppSystem.Type ComponentType => Il2CppType.Of<Component>();
|
||||||
|
|
||||||
|
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
|
||||||
|
|
||||||
|
public static object Il2CppCast(object obj, Type castTo)
|
||||||
|
{
|
||||||
|
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo);
|
||||||
|
return generic.Invoke(obj, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsList(Type t)
|
||||||
|
{
|
||||||
|
return t.IsGenericType
|
||||||
|
&& t.GetGenericTypeDefinition() is Type typeDef
|
||||||
|
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type GetTypeByName(string typeName)
|
||||||
|
{
|
||||||
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (asm.GetType(typeName) is Type type)
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type GetActualType(object m_object)
|
||||||
|
{
|
||||||
|
if (m_object is Il2CppSystem.Object ilObject)
|
||||||
|
{
|
||||||
|
var iltype = ilObject.GetIl2CppType();
|
||||||
|
return Type.GetType(iltype.AssemblyQualifiedName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return m_object.GetType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type[] GetAllBaseTypes(object m_object)
|
||||||
|
{
|
||||||
|
var list = new List<Type>();
|
||||||
|
|
||||||
|
if (m_object is Il2CppSystem.Object ilObject)
|
||||||
|
{
|
||||||
|
var ilType = ilObject.GetIl2CppType();
|
||||||
|
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
|
||||||
|
{
|
||||||
|
list.Add(ilTypeToManaged);
|
||||||
|
|
||||||
|
while (ilType.BaseType != null)
|
||||||
|
{
|
||||||
|
ilType = ilType.BaseType;
|
||||||
|
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
|
||||||
|
{
|
||||||
|
list.Add(ilBaseTypeToManaged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var type = m_object.GetType();
|
||||||
|
list.Add(type);
|
||||||
|
while (type.BaseType != null)
|
||||||
|
{
|
||||||
|
type = type.BaseType;
|
||||||
|
list.Add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,108 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Il2CppSystem.Collections;
|
using System.Threading.Tasks;
|
||||||
//using Il2CppSystem.Reflection;
|
|
||||||
using MelonLoader;
|
|
||||||
using UnhollowerBaseLib;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class UIStyles
|
public class UIHelpers
|
||||||
{
|
{
|
||||||
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
|
|
||||||
|
|
||||||
public static GUISkin WindowSkin
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_customSkin == null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_customSkin = CreateWindowSkin();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_customSkin = GUI.skin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _customSkin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void HorizontalLine(Color color)
|
|
||||||
{
|
|
||||||
var c = GUI.color;
|
|
||||||
GUI.color = color;
|
|
||||||
GUILayout.Box(GUIContent.none, HorizontalBar, null);
|
|
||||||
GUI.color = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GUISkin _customSkin;
|
|
||||||
|
|
||||||
public static Texture2D m_nofocusTex;
|
|
||||||
public static Texture2D m_focusTex;
|
|
||||||
|
|
||||||
private static GUIStyle _horizBarStyle;
|
|
||||||
|
|
||||||
private static GUIStyle HorizontalBar
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_horizBarStyle == null)
|
|
||||||
{
|
|
||||||
_horizBarStyle = new GUIStyle();
|
|
||||||
_horizBarStyle.normal.background = Texture2D.whiteTexture;
|
|
||||||
_horizBarStyle.margin = new RectOffset(0, 0, 4, 4);
|
|
||||||
_horizBarStyle.fixedHeight = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _horizBarStyle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GUISkin CreateWindowSkin()
|
|
||||||
{
|
|
||||||
var newSkin = Object.Instantiate(GUI.skin);
|
|
||||||
Object.DontDestroyOnLoad(newSkin);
|
|
||||||
|
|
||||||
m_nofocusTex = MakeTex(550, 700, new Color(0.1f, 0.1f, 0.1f, 0.7f));
|
|
||||||
m_focusTex = MakeTex(550, 700, new Color(0.3f, 0.3f, 0.3f, 1f));
|
|
||||||
|
|
||||||
newSkin.window.normal.background = m_nofocusTex;
|
|
||||||
newSkin.window.onNormal.background = m_focusTex;
|
|
||||||
|
|
||||||
newSkin.box.normal.textColor = Color.white;
|
|
||||||
newSkin.window.normal.textColor = Color.white;
|
|
||||||
newSkin.button.normal.textColor = Color.white;
|
|
||||||
newSkin.textField.normal.textColor = Color.white;
|
|
||||||
newSkin.label.normal.textColor = Color.white;
|
|
||||||
|
|
||||||
return newSkin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Texture2D MakeTex(int width, int height, Color col)
|
|
||||||
{
|
|
||||||
Color[] pix = new Color[width * height];
|
|
||||||
for (int i = 0; i < pix.Length; ++i)
|
|
||||||
{
|
|
||||||
pix[i] = col;
|
|
||||||
}
|
|
||||||
Texture2D result = new Texture2D(width, height);
|
|
||||||
result.SetPixels(pix);
|
|
||||||
result.Apply();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// *********************************** METHODS FOR DRAWING VALUES IN GUI ************************************
|
|
||||||
|
|
||||||
// helper for "Instantiate" button on UnityEngine.Objects
|
// helper for "Instantiate" button on UnityEngine.Objects
|
||||||
public static void InstantiateButton(Object obj, float width = 100)
|
public static void InstantiateButton(Object obj, float width = 100)
|
||||||
{
|
{
|
||||||
@ -134,7 +42,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
color = LightGreen;
|
color = UIStyles.LightGreen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -144,7 +52,7 @@ namespace Explorer
|
|||||||
|
|
||||||
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
|
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
|
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
|
||||||
{
|
{
|
||||||
if (!obj)
|
if (!obj)
|
||||||
@ -236,11 +144,11 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
DrawPrimitive(ref value, rect, setTarget, setAction);
|
DrawPrimitive(ref value, rect, setTarget, setAction);
|
||||||
}
|
}
|
||||||
else if (ilType != null && ilType == CppExplorer.GameObjectType || CppExplorer.TransformType.IsAssignableFrom(ilType))
|
else if (ilType != null && ilType == ReflectionHelpers.GameObjectType || ReflectionHelpers.TransformType.IsAssignableFrom(ilType))
|
||||||
{
|
{
|
||||||
GameObject go;
|
GameObject go;
|
||||||
var ilObj = value as Il2CppSystem.Object;
|
var ilObj = value as Il2CppSystem.Object;
|
||||||
if (ilType == CppExplorer.GameObjectType)
|
if (ilType == ReflectionHelpers.GameObjectType)
|
||||||
{
|
{
|
||||||
go = ilObj.TryCast<GameObject>();
|
go = ilObj.TryCast<GameObject>();
|
||||||
}
|
}
|
||||||
@ -269,7 +177,7 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.Label(value.ToString(), null);
|
GUILayout.Label(value.ToString(), null);
|
||||||
}
|
}
|
||||||
else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
|
else if (value is System.Collections.IEnumerable || ReflectionHelpers.IsList(valueType))
|
||||||
{
|
{
|
||||||
System.Collections.IEnumerable enumerable;
|
System.Collections.IEnumerable enumerable;
|
||||||
|
|
100
src/Helpers/UIStyles.cs
Normal file
100
src/Helpers/UIStyles.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
public class UIStyles
|
||||||
|
{
|
||||||
|
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
|
||||||
|
|
||||||
|
public static GUISkin WindowSkin
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_customSkin == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_customSkin = CreateWindowSkin();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_customSkin = GUI.skin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _customSkin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HorizontalLine(Color color)
|
||||||
|
{
|
||||||
|
var c = GUI.color;
|
||||||
|
GUI.color = color;
|
||||||
|
GUILayout.Box(GUIContent.none, HorizontalBar, null);
|
||||||
|
GUI.color = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GUISkin _customSkin;
|
||||||
|
|
||||||
|
public static Texture2D m_nofocusTex;
|
||||||
|
public static Texture2D m_focusTex;
|
||||||
|
|
||||||
|
private static GUIStyle _horizBarStyle;
|
||||||
|
|
||||||
|
private static GUIStyle HorizontalBar
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_horizBarStyle == null)
|
||||||
|
{
|
||||||
|
_horizBarStyle = new GUIStyle();
|
||||||
|
_horizBarStyle.normal.background = Texture2D.whiteTexture;
|
||||||
|
_horizBarStyle.margin = new RectOffset(0, 0, 4, 4);
|
||||||
|
_horizBarStyle.fixedHeight = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _horizBarStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GUISkin CreateWindowSkin()
|
||||||
|
{
|
||||||
|
var newSkin = Object.Instantiate(GUI.skin);
|
||||||
|
Object.DontDestroyOnLoad(newSkin);
|
||||||
|
|
||||||
|
m_nofocusTex = MakeTex(550, 700, new Color(0.1f, 0.1f, 0.1f, 0.7f));
|
||||||
|
m_focusTex = MakeTex(550, 700, new Color(0.3f, 0.3f, 0.3f, 1f));
|
||||||
|
|
||||||
|
newSkin.window.normal.background = m_nofocusTex;
|
||||||
|
newSkin.window.onNormal.background = m_focusTex;
|
||||||
|
|
||||||
|
newSkin.box.normal.textColor = Color.white;
|
||||||
|
newSkin.window.normal.textColor = Color.white;
|
||||||
|
newSkin.button.normal.textColor = Color.white;
|
||||||
|
newSkin.textField.normal.textColor = Color.white;
|
||||||
|
newSkin.label.normal.textColor = Color.white;
|
||||||
|
|
||||||
|
return newSkin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Texture2D MakeTex(int width, int height, Color col)
|
||||||
|
{
|
||||||
|
Color[] pix = new Color[width * height];
|
||||||
|
for (int i = 0; i < pix.Length; ++i)
|
||||||
|
{
|
||||||
|
pix[i] = col;
|
||||||
|
}
|
||||||
|
Texture2D result = new Texture2D(width, height);
|
||||||
|
result.SetPixels(pix);
|
||||||
|
result.Apply();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/Helpers/UnityHelpers.cs
Normal file
51
src/Helpers/UnityHelpers.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
public static class UnityHelpers
|
||||||
|
{
|
||||||
|
private static Camera m_mainCamera;
|
||||||
|
|
||||||
|
public static Camera MainCamera
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_mainCamera == null)
|
||||||
|
{
|
||||||
|
m_mainCamera = Camera.main;
|
||||||
|
}
|
||||||
|
return m_mainCamera;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ActiveSceneName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetGameObjectPath(this Transform _transform)
|
||||||
|
{
|
||||||
|
return GetGameObjectPath(_transform, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetGameObjectPath(this Transform _transform, bool _includeThisName)
|
||||||
|
{
|
||||||
|
string path = _includeThisName ? ("/" + _transform.name) : "";
|
||||||
|
GameObject gameObject = _transform.gameObject;
|
||||||
|
while (gameObject.transform.parent != null)
|
||||||
|
{
|
||||||
|
gameObject = gameObject.transform.parent.gameObject;
|
||||||
|
path = "/" + gameObject.name + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
src/MainMenu/InspectUnderMouse.cs
Normal file
87
src/MainMenu/InspectUnderMouse.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
public class InspectUnderMouse
|
||||||
|
{
|
||||||
|
public static bool EnableInspect { get; set; } = false;
|
||||||
|
|
||||||
|
private static string m_objUnderMouseName = "";
|
||||||
|
|
||||||
|
public static void Update()
|
||||||
|
{
|
||||||
|
if (CppExplorer.ShowMenu)
|
||||||
|
{
|
||||||
|
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1))
|
||||||
|
{
|
||||||
|
EnableInspect = !EnableInspect;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnableInspect)
|
||||||
|
{
|
||||||
|
InspectRaycast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (EnableInspect)
|
||||||
|
{
|
||||||
|
EnableInspect = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InspectRaycast()
|
||||||
|
{
|
||||||
|
Ray ray = UnityHelpers.MainCamera.ScreenPointToRay(Input.mousePosition);
|
||||||
|
|
||||||
|
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
|
||||||
|
{
|
||||||
|
var obj = hit.transform.gameObject;
|
||||||
|
|
||||||
|
m_objUnderMouseName = obj.transform.GetGameObjectPath();
|
||||||
|
|
||||||
|
if (Input.GetMouseButtonDown(0))
|
||||||
|
{
|
||||||
|
EnableInspect = false;
|
||||||
|
m_objUnderMouseName = "";
|
||||||
|
|
||||||
|
WindowManager.InspectObject(obj, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_objUnderMouseName = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnGUI()
|
||||||
|
{
|
||||||
|
if (EnableInspect)
|
||||||
|
{
|
||||||
|
if (m_objUnderMouseName != "")
|
||||||
|
{
|
||||||
|
var pos = Input.mousePosition;
|
||||||
|
var rect = new Rect(
|
||||||
|
pos.x - (Screen.width / 2), // x
|
||||||
|
Screen.height - pos.y - 50, // y
|
||||||
|
Screen.width, // w
|
||||||
|
50 // h
|
||||||
|
);
|
||||||
|
|
||||||
|
var origAlign = GUI.skin.label.alignment;
|
||||||
|
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||||
|
|
||||||
|
//shadow text
|
||||||
|
GUI.Label(rect, $"<color=black>{m_objUnderMouseName}</color>");
|
||||||
|
//white text
|
||||||
|
GUI.Label(new Rect(rect.x - 1, rect.y + 1, rect.width, rect.height), m_objUnderMouseName);
|
||||||
|
|
||||||
|
GUI.skin.label.alignment = origAlign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -98,7 +98,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
CppExplorer.ArrayLimit = _lim;
|
CppExplorer.ArrayLimit = _lim;
|
||||||
}
|
}
|
||||||
CppExplorer.Instance.MouseInspect = GUILayout.Toggle(CppExplorer.Instance.MouseInspect, "Inspect Under Mouse (Shift + RMB)", null);
|
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
|
@ -36,7 +36,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public void OnSceneChange()
|
public void OnSceneChange()
|
||||||
{
|
{
|
||||||
m_currentScene = CppExplorer.ActiveSceneName;
|
m_currentScene = UnityHelpers.ActiveSceneName;
|
||||||
|
|
||||||
m_currentTransform = null;
|
m_currentTransform = null;
|
||||||
CancelSearch();
|
CancelSearch();
|
||||||
@ -155,7 +155,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Label(CppExplorer.GetGameObjectPath(m_currentTransform), null);
|
GUILayout.Label(m_currentTransform.GetGameObjectPath(), null);
|
||||||
}
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
@ -169,7 +169,7 @@ namespace Explorer
|
|||||||
foreach (var obj in m_objectList)
|
foreach (var obj in m_objectList)
|
||||||
{
|
{
|
||||||
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
||||||
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ namespace Explorer
|
|||||||
foreach (var obj in m_searchResults)
|
foreach (var obj in m_searchResults)
|
||||||
{
|
{
|
||||||
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
||||||
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -132,7 +132,7 @@ namespace Explorer
|
|||||||
|
|
||||||
bool _ = false;
|
bool _ = false;
|
||||||
int __ = 0;
|
int __ = 0;
|
||||||
UIStyles.DrawValue(ref obj, ref _, ref __, _temprect);
|
UIHelpers.DrawValue(ref obj, ref _, ref __, _temprect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -263,7 +263,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var findType = CppExplorer.GetType(_type);
|
var findType = ReflectionHelpers.GetTypeByName(_type);
|
||||||
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
|
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -273,18 +273,18 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else if (TypeMode == TypeFilter.Object)
|
else if (TypeMode == TypeFilter.Object)
|
||||||
{
|
{
|
||||||
searchType = CppExplorer.ObjectType;
|
searchType = ReflectionHelpers.ObjectType;
|
||||||
}
|
}
|
||||||
else if (TypeMode == TypeFilter.GameObject)
|
else if (TypeMode == TypeFilter.GameObject)
|
||||||
{
|
{
|
||||||
searchType = CppExplorer.GameObjectType;
|
searchType = ReflectionHelpers.GameObjectType;
|
||||||
}
|
}
|
||||||
else if (TypeMode == TypeFilter.Component)
|
else if (TypeMode == TypeFilter.Component)
|
||||||
{
|
{
|
||||||
searchType = CppExplorer.ComponentType;
|
searchType = ReflectionHelpers.ComponentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CppExplorer.ObjectType.IsAssignableFrom(searchType))
|
if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType))
|
||||||
{
|
{
|
||||||
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!");
|
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!");
|
||||||
return new List<object>();
|
return new List<object>();
|
||||||
@ -301,7 +301,7 @@ namespace Explorer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchType == CppExplorer.ComponentType && CppExplorer.TransformType.IsAssignableFrom(obj.GetIl2CppType()))
|
if (searchType == ReflectionHelpers.ComponentType && ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetIl2CppType()))
|
||||||
{
|
{
|
||||||
// Transforms shouldn't really be counted as Components, skip them.
|
// Transforms shouldn't really be counted as Components, skip them.
|
||||||
// They're more akin to GameObjects.
|
// They're more akin to GameObjects.
|
||||||
@ -346,7 +346,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else if (filter == SceneFilter.This)
|
else if (filter == SceneFilter.This)
|
||||||
{
|
{
|
||||||
return go.scene.name == CppExplorer.ActiveSceneName;
|
return go.scene.name == UnityHelpers.ActiveSceneName;
|
||||||
}
|
}
|
||||||
else if (filter == SceneFilter.DontDestroy)
|
else if (filter == SceneFilter.DontDestroy)
|
||||||
{
|
{
|
||||||
|
@ -133,7 +133,7 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
|
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
|
||||||
if (m_scene == CppExplorer.ActiveSceneName)
|
if (m_scene == UnityHelpers.ActiveSceneName)
|
||||||
{
|
{
|
||||||
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
|
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
|
||||||
{
|
{
|
||||||
@ -145,7 +145,7 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
|
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||||
string pathlabel = CppExplorer.GetGameObjectPath(m_object.transform);
|
string pathlabel = m_object.transform.GetGameObjectPath();
|
||||||
if (m_object.transform.parent != null)
|
if (m_object.transform.parent != null)
|
||||||
{
|
{
|
||||||
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
|
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
|
||||||
@ -198,7 +198,7 @@ namespace Explorer
|
|||||||
GUILayout.Label("null", null);
|
GUILayout.Label("null", null);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
UIStyles.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
|
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
|
||||||
}
|
}
|
||||||
foreach (var obj in m_children.Where(x => x.childCount == 0))
|
foreach (var obj in m_children.Where(x => x.childCount == 0))
|
||||||
{
|
{
|
||||||
@ -207,7 +207,7 @@ namespace Explorer
|
|||||||
GUILayout.Label("null", null);
|
GUILayout.Label("null", null);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
UIStyles.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
|
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -329,7 +329,7 @@ namespace Explorer
|
|||||||
new GUILayoutOption[] { GUILayout.Width(80) });
|
new GUILayoutOption[] { GUILayout.Width(80) });
|
||||||
if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); }
|
if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); }
|
||||||
|
|
||||||
UIStyles.InstantiateButton(m_object, 100);
|
UIHelpers.InstantiateButton(m_object, 100);
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var declaringType = this.fieldInfo.DeclaringType;
|
var declaringType = this.fieldInfo.DeclaringType;
|
||||||
|
|
||||||
var cast = CppExplorer.Il2CppCast(obj, declaringType);
|
var cast = ReflectionHelpers.Il2CppCast(obj, declaringType);
|
||||||
m_value = this.fieldInfo.GetValue(fieldInfo.IsStatic ? null : cast);
|
m_value = this.fieldInfo.GetValue(fieldInfo.IsStatic ? null : cast);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -44,7 +44,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void Draw(ReflectionWindow window)
|
public override void Draw(ReflectionWindow window)
|
||||||
{
|
{
|
||||||
UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.fieldInfo, window.m_rect, window.m_object, SetValue);
|
UIHelpers.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.fieldInfo, window.m_rect, window.Target, SetValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetValue(object obj)
|
public override void SetValue(object obj)
|
||||||
@ -103,7 +103,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var declaringType = this.fieldInfo.DeclaringType;
|
var declaringType = this.fieldInfo.DeclaringType;
|
||||||
|
|
||||||
var cast = CppExplorer.Il2CppCast(obj, declaringType);
|
var cast = ReflectionHelpers.Il2CppCast(obj, declaringType);
|
||||||
fieldInfo.SetValue(fieldInfo.IsStatic ? null : cast, m_value);
|
fieldInfo.SetValue(fieldInfo.IsStatic ? null : cast, m_value);
|
||||||
}
|
}
|
||||||
else
|
else
|
@ -23,7 +23,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void Draw(ReflectionWindow window)
|
public override void Draw(ReflectionWindow window)
|
||||||
{
|
{
|
||||||
UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.propInfo, window.m_rect, window.m_object, SetValue);
|
UIHelpers.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.propInfo, window.m_rect, window.Target, SetValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue(object obj)
|
public override void UpdateValue(object obj)
|
||||||
@ -40,7 +40,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var cast = CppExplorer.Il2CppCast(obj, declaringType);
|
var cast = ReflectionHelpers.Il2CppCast(obj, declaringType);
|
||||||
m_value = this.propInfo.GetValue(this.propInfo.GetAccessors()[0].IsStatic ? null : cast, null);
|
m_value = this.propInfo.GetValue(this.propInfo.GetAccessors()[0].IsStatic ? null : cast, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
var declaring = propInfo.DeclaringType;
|
var declaring = propInfo.DeclaringType;
|
||||||
var cast = CppExplorer.Il2CppCast(obj, declaring);
|
var cast = ReflectionHelpers.Il2CppCast(obj, declaring);
|
||||||
|
|
||||||
propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null);
|
propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null);
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -14,11 +15,11 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public override string Name { get => "Object Reflection"; set => Name = value; }
|
public override string Name { get => "Object Reflection"; set => Name = value; }
|
||||||
|
|
||||||
public Type m_objectType;
|
public Type ObjectType;
|
||||||
public object m_object;
|
//public object Target;
|
||||||
|
|
||||||
private List<FieldInfoHolder> m_FieldInfos;
|
private FieldInfoHolder[] m_FieldInfos;
|
||||||
private List<PropertyInfoHolder> m_PropertyInfos;
|
private PropertyInfoHolder[] m_PropertyInfos;
|
||||||
|
|
||||||
private bool m_autoUpdate = false;
|
private bool m_autoUpdate = false;
|
||||||
private string m_search = "";
|
private string m_search = "";
|
||||||
@ -33,25 +34,21 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
m_object = Target;
|
var type = ReflectionHelpers.GetActualType(Target);
|
||||||
|
|
||||||
m_FieldInfos = new List<FieldInfoHolder>();
|
|
||||||
m_PropertyInfos = new List<PropertyInfoHolder>();
|
|
||||||
|
|
||||||
var type = GetActualType(m_object);
|
|
||||||
if (type == null)
|
if (type == null)
|
||||||
{
|
{
|
||||||
MelonLogger.Log("could not get underlying type for object. ToString(): " + m_object.ToString());
|
MelonLogger.Log("Could not get underlying type for object. ToString(): " + Target.ToString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
ObjectType = type;
|
||||||
{
|
|
||||||
m_objectType = type;
|
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
||||||
GetFields(m_object);
|
|
||||||
GetProperties(m_object);
|
CacheFields(types);
|
||||||
}
|
CacheProperties(types);
|
||||||
catch { }
|
|
||||||
|
MelonLogger.Log("Cached properties: " + m_PropertyInfos.Length);
|
||||||
|
|
||||||
UpdateValues(true);
|
UpdateValues(true);
|
||||||
}
|
}
|
||||||
@ -66,29 +63,106 @@ namespace Explorer
|
|||||||
|
|
||||||
private void UpdateValues(bool forceAll = false)
|
private void UpdateValues(bool forceAll = false)
|
||||||
{
|
{
|
||||||
if (forceAll || m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
|
UpdateMemberList(forceAll, this.m_FieldInfos, MemberFilter.Field);
|
||||||
{
|
UpdateMemberList(forceAll, this.m_PropertyInfos, MemberFilter.Property);
|
||||||
foreach (var holder in this.m_FieldInfos)
|
}
|
||||||
{
|
|
||||||
if (forceAll || m_search == "" || holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
|
|
||||||
{
|
|
||||||
holder.UpdateValue(m_object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceAll || m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
|
private void UpdateMemberList(bool forceAll, MemberInfoHolder[] list, MemberFilter filter)
|
||||||
|
{
|
||||||
|
if (forceAll || m_filter == MemberFilter.Both || m_filter == filter)
|
||||||
{
|
{
|
||||||
foreach (var holder in this.m_PropertyInfos)
|
foreach (var holder in list)
|
||||||
{
|
{
|
||||||
if (forceAll || m_search == "" || holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
|
if (forceAll || ShouldUpdateMemberInfo(holder))
|
||||||
{
|
{
|
||||||
holder.UpdateValue(m_object);
|
holder.UpdateValue(Target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShouldUpdateMemberInfo(MemberInfoHolder holder)
|
||||||
|
{
|
||||||
|
var memberName = holder is FieldInfoHolder ?
|
||||||
|
(holder as FieldInfoHolder).fieldInfo.Name :
|
||||||
|
(holder as PropertyInfoHolder).propInfo.Name;
|
||||||
|
|
||||||
|
return m_search == "" || memberName.ToLower().Contains(m_search.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheProperties(Type[] types, List<string> names = null)
|
||||||
|
{
|
||||||
|
if (names == null)
|
||||||
|
{
|
||||||
|
names = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = new List<PropertyInfoHolder>();
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
PropertyInfo[] propInfos = new PropertyInfo[0];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
propInfos = type.GetProperties(ReflectionHelpers.CommonFlags);
|
||||||
|
}
|
||||||
|
catch (TypeLoadException)
|
||||||
|
{
|
||||||
|
MelonLogger.Log($"Couldn't get Properties for Type '{type.Name}', it may not support Il2Cpp Reflection at the moment.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var pi in propInfos)
|
||||||
|
{
|
||||||
|
// this member causes a crash when inspected, so just skipping it for now.
|
||||||
|
if (pi.Name == "Il2CppType")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (names.Contains(pi.Name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
names.Add(pi.Name);
|
||||||
|
|
||||||
|
var piHolder = new PropertyInfoHolder(type, pi);
|
||||||
|
list.Add(piHolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PropertyInfos = list.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheFields(Type[] types, List<string> names = null)
|
||||||
|
{
|
||||||
|
if (names == null)
|
||||||
|
{
|
||||||
|
names = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = new List<FieldInfoHolder>();
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
foreach (var fi in type.GetFields(ReflectionHelpers.CommonFlags))
|
||||||
|
{
|
||||||
|
if (names.Contains(fi.Name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
names.Add(fi.Name);
|
||||||
|
|
||||||
|
var fiHolder = new FieldInfoHolder(type, fi);
|
||||||
|
list.Add(fiHolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_FieldInfos = list.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== GUI DRAW =========== //
|
||||||
|
|
||||||
public override void WindowFunction(int windowID)
|
public override void WindowFunction(int windowID)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -98,13 +172,13 @@ namespace Explorer
|
|||||||
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
|
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label("<b>Type:</b> <color=cyan>" + m_objectType.Name + "</color>", null);
|
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.Name + "</color>", null);
|
||||||
|
|
||||||
bool unityObj = m_object is UnityEngine.Object;
|
bool unityObj = Target is UnityEngine.Object;
|
||||||
|
|
||||||
if (unityObj)
|
if (unityObj)
|
||||||
{
|
{
|
||||||
GUILayout.Label("Name: " + (m_object as UnityEngine.Object).name, null);
|
GUILayout.Label("Name: " + (Target as UnityEngine.Object).name, null);
|
||||||
}
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
@ -114,9 +188,9 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
|
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||||
|
|
||||||
UIStyles.InstantiateButton((UnityEngine.Object)m_object);
|
UIHelpers.InstantiateButton((UnityEngine.Object)Target);
|
||||||
|
|
||||||
if (m_object is Component comp && comp.gameObject is GameObject obj)
|
if (Target is Component comp && comp.gameObject is GameObject obj)
|
||||||
{
|
{
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
||||||
GUILayout.Label("GameObject:", null);
|
GUILayout.Label("GameObject:", null);
|
||||||
@ -163,40 +237,12 @@ namespace Explorer
|
|||||||
|
|
||||||
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
|
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
|
||||||
{
|
{
|
||||||
UIStyles.HorizontalLine(Color.grey);
|
DrawMembers(this.m_FieldInfos, "Fields");
|
||||||
|
|
||||||
GUILayout.Label("<size=18><b><color=gold>Fields</color></b></size>", null);
|
|
||||||
|
|
||||||
foreach (var holder in this.m_FieldInfos)
|
|
||||||
{
|
|
||||||
if (m_search != "" && !holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
|
||||||
holder.Draw(this);
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
|
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
|
||||||
{
|
{
|
||||||
UIStyles.HorizontalLine(Color.grey);
|
DrawMembers(this.m_PropertyInfos, "Properties");
|
||||||
|
|
||||||
GUILayout.Label("<size=18><b><color=gold>Properties</color></b></size>", null);
|
|
||||||
|
|
||||||
foreach (var holder in this.m_PropertyInfos)
|
|
||||||
{
|
|
||||||
if (m_search != "" && !holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
|
||||||
holder.Draw(this);
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.EndScrollView();
|
GUILayout.EndScrollView();
|
||||||
@ -213,6 +259,27 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawMembers(MemberInfoHolder[] members, string title)
|
||||||
|
{
|
||||||
|
UIStyles.HorizontalLine(Color.grey);
|
||||||
|
|
||||||
|
GUILayout.Label($"<size=18><b><color=gold>{title}</color></b></size>", null);
|
||||||
|
|
||||||
|
foreach (var holder in members)
|
||||||
|
{
|
||||||
|
var memberName = (holder as FieldInfoHolder)?.fieldInfo.Name ?? (holder as PropertyInfoHolder)?.propInfo.Name;
|
||||||
|
|
||||||
|
if (m_search != "" && !memberName.ToLower().Contains(m_search.ToLower()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
||||||
|
holder.Draw(this);
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void FilterToggle(MemberFilter mode, string label)
|
private void FilterToggle(MemberFilter mode, string label)
|
||||||
{
|
{
|
||||||
if (m_filter == mode)
|
if (m_filter == mode)
|
||||||
@ -229,129 +296,5 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ HELPERS ===============
|
|
||||||
|
|
||||||
public Type GetActualType(object m_object)
|
|
||||||
{
|
|
||||||
if (m_object is Il2CppSystem.Object ilObject)
|
|
||||||
{
|
|
||||||
var iltype = ilObject.GetIl2CppType();
|
|
||||||
return Type.GetType(iltype.AssemblyQualifiedName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return m_object.GetType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type[] GetAllBaseTypes(object m_object)
|
|
||||||
{
|
|
||||||
var list = new List<Type>();
|
|
||||||
|
|
||||||
if (m_object is Il2CppSystem.Object ilObject)
|
|
||||||
{
|
|
||||||
var ilType = ilObject.GetIl2CppType();
|
|
||||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
|
|
||||||
{
|
|
||||||
list.Add(ilTypeToManaged);
|
|
||||||
|
|
||||||
while (ilType.BaseType != null)
|
|
||||||
{
|
|
||||||
ilType = ilType.BaseType;
|
|
||||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
|
|
||||||
{
|
|
||||||
list.Add(ilBaseTypeToManaged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var type = m_object.GetType();
|
|
||||||
list.Add(type);
|
|
||||||
while (type.BaseType != null)
|
|
||||||
{
|
|
||||||
type = type.BaseType;
|
|
||||||
list.Add(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsList(Type t)
|
|
||||||
{
|
|
||||||
return t.IsGenericType
|
|
||||||
&& t.GetGenericTypeDefinition() is Type typeDef
|
|
||||||
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetProperties(object m_object, List<string> names = null)
|
|
||||||
{
|
|
||||||
if (names == null)
|
|
||||||
{
|
|
||||||
names = new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var types = GetAllBaseTypes(m_object);
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
PropertyInfo[] propInfos = new PropertyInfo[0];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
propInfos = type.GetProperties(At.flags);
|
|
||||||
}
|
|
||||||
catch (TypeLoadException)
|
|
||||||
{
|
|
||||||
MelonLogger.Log($"Couldn't get Properties for Type '{type.Name}', it may not support Il2Cpp Reflection at the moment.");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pi in propInfos)
|
|
||||||
{
|
|
||||||
// this member causes a crash when inspected, so just skipping it for now.
|
|
||||||
if (pi.Name == "Il2CppType")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (names.Contains(pi.Name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
names.Add(pi.Name);
|
|
||||||
|
|
||||||
var piHolder = new PropertyInfoHolder(type, pi);
|
|
||||||
m_PropertyInfos.Add(piHolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetFields(object m_object, List<string> names = null)
|
|
||||||
{
|
|
||||||
if (names == null)
|
|
||||||
{
|
|
||||||
names = new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var types = GetAllBaseTypes(m_object);
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
foreach (var fi in type.GetFields(At.flags))
|
|
||||||
{
|
|
||||||
if (names.Contains(fi.Name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
names.Add(fi.Name);
|
|
||||||
|
|
||||||
var fiHolder = new FieldInfoHolder(type, fi);
|
|
||||||
m_FieldInfos.Add(fiHolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Explorer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// AccessTools
|
|
||||||
/// Some helpers for Reflection (GetValue, SetValue, Call, InheritBaseValues)
|
|
||||||
/// </summary>
|
|
||||||
public static class At
|
|
||||||
{
|
|
||||||
public static Il2CppSystem.Reflection.BindingFlags ilFlags = Il2CppSystem.Reflection.BindingFlags.Public
|
|
||||||
| Il2CppSystem.Reflection.BindingFlags.NonPublic
|
|
||||||
| Il2CppSystem.Reflection.BindingFlags.Instance
|
|
||||||
| Il2CppSystem.Reflection.BindingFlags.Static;
|
|
||||||
|
|
||||||
public static BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static;
|
|
||||||
|
|
||||||
//reflection call
|
|
||||||
public static object Call(object obj, string method, params object[] args)
|
|
||||||
{
|
|
||||||
var methodInfo = obj.GetType().GetMethod(method, flags);
|
|
||||||
if (methodInfo != null)
|
|
||||||
{
|
|
||||||
return methodInfo.Invoke(obj, args);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set value
|
|
||||||
public static void SetValue<T>(T value, Type type, object obj, string field)
|
|
||||||
{
|
|
||||||
FieldInfo fieldInfo = type.GetField(field, flags);
|
|
||||||
if (fieldInfo != null)
|
|
||||||
{
|
|
||||||
fieldInfo.SetValue(obj, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get value
|
|
||||||
public static object GetValue(Type type, object obj, string value)
|
|
||||||
{
|
|
||||||
FieldInfo fieldInfo = type.GetField(value, flags);
|
|
||||||
if (fieldInfo != null)
|
|
||||||
{
|
|
||||||
return fieldInfo.GetValue(obj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// inherit base values
|
|
||||||
public static void InheritBaseValues(object _derived, object _base)
|
|
||||||
{
|
|
||||||
foreach (FieldInfo fi in _base.GetType().GetFields(flags))
|
|
||||||
{
|
|
||||||
try { _derived.GetType().GetField(fi.Name).SetValue(_derived, fi.GetValue(_base)); } catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user