From 2ba6f27a27257809caa83c03db14cd585985136d Mon Sep 17 00:00:00 2001
From: sinaioutlander <49360850+sinaioutlander@users.noreply.github.com>
Date: Wed, 12 Aug 2020 03:51:47 +1000
Subject: [PATCH] 2018 support test
---
src/CppExplorer.cs | 12 +-
src/CppExplorer.csproj | 8 +-
src_2018/CppExplorer.cs | 220 +++++++
src_2018/CppExplorer.csproj | 123 ++++
src_2018/CppExplorer.sln | 25 +
src_2018/ILBehaviour.cs | 33 ++
src_2018/Inspectors/GameObjectWindow.cs | 448 ++++++++++++++
src_2018/Inspectors/ReflectionWindow.cs | 552 ++++++++++++++++++
src_2018/MainMenu/MainMenu.cs | 136 +++++
src_2018/MainMenu/Pages/Console/REPL.cs | 131 +++++
src_2018/MainMenu/Pages/Console/REPLHelper.cs | 34 ++
src_2018/MainMenu/Pages/ConsolePage.cs | 227 +++++++
src_2018/MainMenu/Pages/ScenePage.cs | 219 +++++++
src_2018/MainMenu/Pages/SearchPage.cs | 434 ++++++++++++++
src_2018/Properties/AssemblyInfo.cs | 41 ++
src_2018/UIStyles.cs | 415 +++++++++++++
src_2018/WindowManager.cs | 258 ++++++++
src_2018/utils/AccessTools.cs | 65 +++
18 files changed, 3372 insertions(+), 9 deletions(-)
create mode 100644 src_2018/CppExplorer.cs
create mode 100644 src_2018/CppExplorer.csproj
create mode 100644 src_2018/CppExplorer.sln
create mode 100644 src_2018/ILBehaviour.cs
create mode 100644 src_2018/Inspectors/GameObjectWindow.cs
create mode 100644 src_2018/Inspectors/ReflectionWindow.cs
create mode 100644 src_2018/MainMenu/MainMenu.cs
create mode 100644 src_2018/MainMenu/Pages/Console/REPL.cs
create mode 100644 src_2018/MainMenu/Pages/Console/REPLHelper.cs
create mode 100644 src_2018/MainMenu/Pages/ConsolePage.cs
create mode 100644 src_2018/MainMenu/Pages/ScenePage.cs
create mode 100644 src_2018/MainMenu/Pages/SearchPage.cs
create mode 100644 src_2018/Properties/AssemblyInfo.cs
create mode 100644 src_2018/UIStyles.cs
create mode 100644 src_2018/WindowManager.cs
create mode 100644 src_2018/utils/AccessTools.cs
diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs
index 7df55f4..5881dc7 100644
--- a/src/CppExplorer.cs
+++ b/src/CppExplorer.cs
@@ -6,6 +6,7 @@ using System.Text;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
+using Harmony;
namespace Explorer
{
@@ -61,13 +62,11 @@ namespace Explorer
new MainMenu();
new WindowManager();
- //var harmony = HarmonyInstance.Create(ID);
- //harmony.PatchAll();
-
// done init
ShowMenu = true;
}
+ // On scene change
public override void OnLevelWasLoaded(int level)
{
if (ScenePage.Instance != null)
@@ -77,6 +76,7 @@ namespace Explorer
}
}
+ // Update
public override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.F7))
@@ -86,6 +86,12 @@ namespace Explorer
if (ShowMenu)
{
+ if (!Cursor.visible)
+ {
+ Cursor.visible = true;
+ Cursor.lockState = CursorLockMode.None;
+ }
+
MainMenu.Instance.Update();
WindowManager.Instance.Update();
diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj
index 7506e56..a2274dd 100644
--- a/src/CppExplorer.csproj
+++ b/src/CppExplorer.csproj
@@ -46,7 +46,7 @@
..\lib\mcs.dll
- True
+ False
..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\MelonLoader.ModHandler.dll
@@ -90,12 +90,8 @@
..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.ParticleSystemModule.dll
False
-
- ..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.Physics2DModule.dll
- False
-
- ..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll
+ ..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll
False
diff --git a/src_2018/CppExplorer.cs b/src_2018/CppExplorer.cs
new file mode 100644
index 0000000..5881dc7
--- /dev/null
+++ b/src_2018/CppExplorer.cs
@@ -0,0 +1,220 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using MelonLoader;
+using UnhollowerBaseLib;
+using Harmony;
+
+namespace Explorer
+{
+ public class CppExplorer : MelonMod
+ {
+ // consts
+
+ public const string ID = "com.sinai.cppexplorer";
+ public const string NAME = "IL2CPP Runtime Explorer";
+ public const string VERSION = "1.1.0";
+ public const string AUTHOR = "Sinai";
+
+ // fields
+
+ public static CppExplorer Instance;
+ private string m_objUnderMouseName = "";
+ private Camera m_main;
+
+ // props
+
+ public static bool ShowMenu { get; set; } = false;
+ public static int ArrayLimit { get; set; } = 100;
+ public bool MouseInspect { get; set; } = false;
+
+ 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
+
+ public override void OnApplicationStart()
+ {
+ base.OnApplicationStart();
+
+ Instance = this;
+
+ new MainMenu();
+ new WindowManager();
+
+ // done init
+ ShowMenu = true;
+ }
+
+ // On scene change
+ public override void OnLevelWasLoaded(int level)
+ {
+ if (ScenePage.Instance != null)
+ {
+ ScenePage.Instance.OnSceneChange();
+ SearchPage.Instance.OnSceneChange();
+ }
+ }
+
+ // Update
+ public override void OnUpdate()
+ {
+ if (Input.GetKeyDown(KeyCode.F7))
+ {
+ ShowMenu = !ShowMenu;
+ }
+
+ if (ShowMenu)
+ {
+ if (!Cursor.visible)
+ {
+ Cursor.visible = true;
+ Cursor.lockState = CursorLockMode.None;
+ }
+
+ MainMenu.Instance.Update();
+ WindowManager.Instance.Update();
+
+ if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1))
+ {
+ 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 = "";
+ }
+ }
+
+ public override void OnGUI()
+ {
+ base.OnGUI();
+
+ MainMenu.Instance.OnGUI();
+ WindowManager.Instance.OnGUI();
+
+ if (MouseInspect)
+ {
+ 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, $"{m_objUnderMouseName}");
+ //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;
+ }
+ }
+ }
+}
diff --git a/src_2018/CppExplorer.csproj b/src_2018/CppExplorer.csproj
new file mode 100644
index 0000000..b195b77
--- /dev/null
+++ b/src_2018/CppExplorer.csproj
@@ -0,0 +1,123 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}
+ Library
+ Properties
+ CppExplorer
+ CppExplorer_2018
+ v4.7.2
+ 512
+ true
+
+
+
+ false
+ none
+ false
+ ..\Release\
+
+
+ prompt
+ 4
+ x64
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\Il2Cppmscorlib.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\Il2CppSystem.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\Il2CppSystem.Core.dll
+ False
+
+
+ ..\lib\mcs.dll
+ False
+
+
+ ..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\MelonLoader.ModHandler.dll
+ False
+
+
+
+
+
+
+
+
+ ..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll
+ False
+
+
+ ..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerRuntimeLib.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.CoreModule.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.IMGUIModule.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.InputModule.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.PhysicsModule.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll
+ False
+
+
+ ..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UIElementsModule.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src_2018/CppExplorer.sln b/src_2018/CppExplorer.sln
new file mode 100644
index 0000000..5845c60
--- /dev/null
+++ b/src_2018/CppExplorer.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30128.74
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppExplorer", "CppExplorer.csproj", "{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DD5C0A5D-03F1-4CC3-8B4D-E10834908C5A}
+ EndGlobalSection
+EndGlobal
diff --git a/src_2018/ILBehaviour.cs b/src_2018/ILBehaviour.cs
new file mode 100644
index 0000000..a00d88d
--- /dev/null
+++ b/src_2018/ILBehaviour.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using MelonLoader;
+using UnhollowerRuntimeLib;
+
+namespace Explorer
+{
+ //public class ILBehaviour : MonoBehaviour
+ //{
+ // public ILBehaviour(IntPtr intPtr) : base(intPtr) { }
+
+ // public static T AddToGameObject(GameObject _go) where T : ILBehaviour
+ // {
+ // Il2CppSystem.Type ilType = UnhollowerRuntimeLib.Il2CppType.Of();
+
+ // if (ilType == null)
+ // {
+ // MelonLogger.Log("Error - could not get MB as ilType");
+ // return null;
+ // }
+
+ // var obj = typeof(T)
+ // .GetConstructor(new Type[] { typeof(IntPtr) })
+ // .Invoke(new object[] { _go.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of()).Pointer });
+
+ // return (T)obj;
+ // }
+ //}
+}
diff --git a/src_2018/Inspectors/GameObjectWindow.cs b/src_2018/Inspectors/GameObjectWindow.cs
new file mode 100644
index 0000000..16d265e
--- /dev/null
+++ b/src_2018/Inspectors/GameObjectWindow.cs
@@ -0,0 +1,448 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MelonLoader;
+using UnhollowerRuntimeLib;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+
+namespace Explorer
+{
+ public class GameObjectWindow : WindowManager.UIWindow
+ {
+ public override Il2CppSystem.String Name { get => "GameObject Inspector"; set => Name = value; }
+
+ public GameObject m_object;
+
+ // gui element holders
+ private string m_name;
+ private string m_scene;
+
+ private Vector2 m_transformScroll = Vector2.zero;
+ private Transform[] m_children;
+
+ private Vector2 m_compScroll = Vector2.zero;
+ //private Component[] m_components;
+
+ private float m_translateAmount = 0.3f;
+ private float m_rotateAmount = 50f;
+ private float m_scaleAmount = 0.1f;
+
+ List m_cachedDestroyList = new List();
+ //private string m_addComponentInput = "";
+
+ private string m_setParentInput = "";
+
+ public bool GetObjectAsGameObject()
+ {
+ if (Target == null)
+ {
+ MelonLogger.Log("Target is null!");
+ return false;
+ }
+
+ var targetType = Target.GetType();
+
+ if (targetType == typeof(GameObject))
+ {
+ m_object = Target as GameObject;
+ return true;
+ }
+ else if (targetType == typeof(Transform))
+ {
+ m_object = (Target as Transform).gameObject;
+ return true;
+ }
+
+ MelonLogger.Log("Error: Target is null or not a GameObject/Transform!");
+ DestroyWindow();
+ return false;
+ }
+
+ public override void Init()
+ {
+ if (!GetObjectAsGameObject())
+ {
+ return;
+ }
+
+ m_name = m_object.name;
+ m_scene = m_object.scene == null ? "null" : m_object.scene.name;
+
+ //var listComps = new Il2CppSystem.Collections.Generic.List();
+ //m_object.GetComponents(listComps);
+ //m_components = listComps.ToArray();
+
+ var list = new List();
+ for (int i = 0; i < m_object.transform.childCount; i++)
+ {
+ list.Add(m_object.transform.GetChild(i));
+ }
+ m_children = list.ToArray();
+ }
+
+ public override void Update()
+ {
+ if (!m_object && !GetObjectAsGameObject())
+ {
+ MelonLogger.Log("Object is null! Destroying window...");
+ DestroyWindow();
+ }
+ }
+
+ private void InspectGameObject(GameObject obj)
+ {
+ var window = WindowManager.InspectObject(obj, out bool created);
+
+ if (created)
+ {
+ window.m_rect = new Rect(this.m_rect.x, this.m_rect.y, this.m_rect.width, this.m_rect.height);
+ DestroyWindow();
+ }
+ }
+
+ private void ReflectObject(Il2CppSystem.Object obj)
+ {
+ var window = WindowManager.InspectObject(obj, out bool created);
+
+ if (created)
+ {
+ if (this.m_rect.x <= (Screen.width - this.m_rect.width - 100))
+ {
+ window.m_rect = new Rect(
+ this.m_rect.x + this.m_rect.width + 20,
+ this.m_rect.y,
+ 550,
+ 700);
+ }
+ else
+ {
+ window.m_rect = new Rect(this.m_rect.x + 50, this.m_rect.y + 50, 550, 700);
+ }
+ }
+ }
+
+ public override void WindowFunction(int windowID)
+ {
+ Header();
+
+ GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
+
+ scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Scene: " + (m_scene == "" ? "n/a" : m_scene) + "", null);
+ if (m_scene == CppExplorer.ActiveSceneName)
+ {
+ if (GUILayout.Button("< View in Scene Explorer", new GUILayoutOption[] { GUILayout.Width(230) }))
+ {
+ ScenePage.Instance.SetTransformTarget(m_object);
+ MainMenu.SetCurrentPage(0);
+ }
+ }
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
+ string pathlabel = CppExplorer.GetGameObjectPath(m_object.transform);
+ if (m_object.transform.parent != null)
+ {
+ if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
+ {
+ InspectGameObject(m_object.transform.parent.gameObject);
+ }
+ }
+ GUILayout.TextArea(pathlabel, null);
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
+ GUILayout.TextArea(m_name, null);
+ GUILayout.EndHorizontal();
+
+ // --- Horizontal Columns section ---
+ GUILayout.BeginHorizontal(null);
+
+ GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
+ TransformList();
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
+ ComponentList();
+ GUILayout.EndVertical();
+
+ GUILayout.EndHorizontal(); // end horiz columns
+
+ GameObjectControls();
+
+ GUILayout.EndScrollView();
+
+ m_rect = WindowManager.ResizeWindow(m_rect, windowID);
+
+ GUILayout.EndArea();
+ }
+
+ private void TransformList()
+ {
+ GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Height(250) });
+ m_transformScroll = GUILayout.BeginScrollView(m_transformScroll, GUI.skin.scrollView);
+
+ GUILayout.Label("Children:", null);
+ if (m_children != null && m_children.Length > 0)
+ {
+ foreach (var obj in m_children.Where(x => x.childCount > 0))
+ {
+ if (!obj)
+ {
+ GUILayout.Label("null", null);
+ continue;
+ }
+ UIStyles.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
+ }
+ foreach (var obj in m_children.Where(x => x.childCount == 0))
+ {
+ if (!obj)
+ {
+ GUILayout.Label("null", null);
+ continue;
+ }
+ UIStyles.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
+ }
+ }
+ else
+ {
+ GUILayout.Label("None", null);
+ }
+
+ GUILayout.EndScrollView();
+ GUILayout.EndVertical();
+ }
+
+
+ private void ComponentList()
+ {
+ GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Height(250) });
+ m_compScroll = GUILayout.BeginScrollView(m_compScroll, GUI.skin.scrollView);
+ GUILayout.Label("Components", null);
+
+ GUI.skin.button.alignment = TextAnchor.MiddleLeft;
+ if (m_cachedDestroyList.Count > 0)
+ {
+ m_cachedDestroyList.Clear();
+ }
+
+ var m_components = new Il2CppSystem.Collections.Generic.List();
+ m_object.GetComponentsInternal(Il2CppType.Of(), false, false, true, false, m_components);
+
+ var ilTypeOfTransform = Il2CppType.Of();
+ var ilTypeOfBehaviour = Il2CppType.Of();
+ foreach (var component in m_components)
+ {
+ var ilType = component.GetIl2CppType();
+ if (ilType == ilTypeOfTransform)
+ {
+ continue;
+ }
+
+ GUILayout.BeginHorizontal(null);
+ if (ilTypeOfBehaviour.IsAssignableFrom(ilType))
+ {
+ BehaviourEnabledBtn(component.TryCast());
+ }
+ if (GUILayout.Button("" + ilType.Name + "", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) }))
+ {
+ ReflectObject(component);
+ }
+ if (GUILayout.Button("-", new GUILayoutOption[] { GUILayout.Width(20) }))
+ {
+ m_cachedDestroyList.Add(component);
+ }
+ GUILayout.EndHorizontal();
+ }
+
+
+ GUI.skin.button.alignment = TextAnchor.MiddleCenter;
+ if (m_cachedDestroyList.Count > 0)
+ {
+ for (int i = m_cachedDestroyList.Count - 1; i >= 0; i--)
+ {
+ var comp = m_cachedDestroyList[i];
+ GameObject.Destroy(comp);
+ }
+ }
+
+ GUILayout.EndScrollView();
+
+ //GUILayout.BeginHorizontal(null);
+ //m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 150) });
+ //if (GUILayout.Button("Add Component", new GUILayoutOption[] { GUILayout.Width(120) }))
+ //{
+ // if (HPExplorer.GetType(m_addComponentInput) is Type type && typeof(Component).IsAssignableFrom(type))
+ // {
+ // var comp = m_object.AddComponent(type);
+ // var list = m_components.ToList();
+ // list.Add(comp);
+ // m_components = list.ToArray();
+ // }
+ // else
+ // {
+ // MelonLogger.LogWarning($"Could not get type '{m_addComponentInput}'. If it's not a typo, try the fully qualified name.");
+ // }
+ //}
+ //GUILayout.EndHorizontal();
+
+ GUILayout.EndVertical();
+ }
+
+ private void BehaviourEnabledBtn(Behaviour obj)
+ {
+ var _col = GUI.color;
+ bool _enabled = obj.enabled;
+ if (_enabled)
+ {
+ GUI.color = Color.green;
+ }
+ else
+ {
+ GUI.color = Color.red;
+ }
+
+ // ------ toggle active button ------
+
+ _enabled = GUILayout.Toggle(_enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
+ if (obj.enabled != _enabled)
+ {
+ obj.enabled = _enabled;
+ }
+ GUI.color = _col;
+ }
+
+ private void GameObjectControls()
+ {
+ GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(530) });
+ GUILayout.Label("GameObject Controls", null);
+
+ GUILayout.BeginHorizontal(null);
+ bool m_active = m_object.activeSelf;
+ m_active = GUILayout.Toggle(m_active, (m_active ? "Enabled " : "Disabled") + "",
+ new GUILayoutOption[] { GUILayout.Width(80) });
+ if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); }
+
+ UIStyles.InstantiateButton(m_object, 100);
+
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+
+ if (GUILayout.Button("Remove from parent", new GUILayoutOption[] { GUILayout.Width(160) }))
+ {
+ m_object.transform.parent = null;
+ }
+ m_setParentInput = GUILayout.TextField(m_setParentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width - 280) });
+ if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
+ {
+ if (GameObject.Find(m_setParentInput) is GameObject newparent)
+ {
+ m_object.transform.parent = newparent.transform;
+ }
+ else
+ {
+ MelonLogger.LogWarning($"Could not find gameobject '{m_setParentInput}'");
+ }
+ }
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginVertical(GUI.skin.box, null);
+
+ var t = m_object.transform;
+ TranslateControl(t, TranslateType.Position, ref m_translateAmount, false);
+ TranslateControl(t, TranslateType.Rotation, ref m_rotateAmount, true);
+ TranslateControl(t, TranslateType.Scale, ref m_scaleAmount, false);
+
+ GUILayout.EndVertical();
+
+ if (GUILayout.Button("Destroy", null))
+ {
+ GameObject.Destroy(m_object);
+ DestroyWindow();
+ return;
+ }
+
+ GUILayout.EndVertical();
+ }
+
+ public enum TranslateType
+ {
+ Position,
+ Rotation,
+ Scale
+ }
+
+ private void TranslateControl(Transform transform, TranslateType mode, ref float amount, bool multByTime)
+ {
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("" + mode + ":", new GUILayoutOption[] { GUILayout.Width(65) });
+
+ Vector3 vector = Vector3.zero;
+ switch (mode)
+ {
+ case TranslateType.Position: vector = transform.localPosition; break;
+ case TranslateType.Rotation: vector = transform.localRotation.eulerAngles; break;
+ case TranslateType.Scale: vector = transform.localScale; break;
+ }
+ GUILayout.Label(vector.ToString(), new GUILayoutOption[] { GUILayout.Width(250) });
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+ GUI.skin.label.alignment = TextAnchor.MiddleRight;
+
+ GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(20) });
+ PlusMinusFloat(ref vector.x, amount, multByTime);
+
+ GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(20) });
+ PlusMinusFloat(ref vector.y, amount, multByTime);
+
+ GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(20) });
+ PlusMinusFloat(ref vector.z, amount, multByTime);
+
+ switch (mode)
+ {
+ case TranslateType.Position: transform.localPosition = vector; break;
+ case TranslateType.Rotation: transform.localRotation = Quaternion.Euler(vector); break;
+ case TranslateType.Scale: transform.localScale = vector; break;
+ }
+
+ GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) });
+ var input = amount.ToString("F3");
+ input = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(40) });
+ if (float.TryParse(input, out float f))
+ {
+ amount = f;
+ }
+
+ GUI.skin.label.alignment = TextAnchor.UpperLeft;
+ GUILayout.EndHorizontal();
+ }
+
+ private void PlusMinusFloat(ref float f, float amount, bool multByTime)
+ {
+ string s = f.ToString("F3");
+ s = GUILayout.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) });
+ if (float.TryParse(s, out float f2))
+ {
+ f = f2;
+ }
+ if (GUILayout.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
+ {
+ f -= multByTime ? amount * Time.deltaTime : amount;
+ }
+ if (GUILayout.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
+ {
+ f += multByTime ? amount * Time.deltaTime : amount;
+ }
+ }
+
+
+ }
+}
diff --git a/src_2018/Inspectors/ReflectionWindow.cs b/src_2018/Inspectors/ReflectionWindow.cs
new file mode 100644
index 0000000..0d1ba66
--- /dev/null
+++ b/src_2018/Inspectors/ReflectionWindow.cs
@@ -0,0 +1,552 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using MelonLoader;
+using Mono.CSharp;
+using UnhollowerBaseLib;
+using UnityEngine;
+
+namespace Explorer
+{
+ public class ReflectionWindow : WindowManager.UIWindow
+ {
+ public override Il2CppSystem.String Name { get => "Object Reflection"; set => Name = value; }
+
+ public Type m_objectType;
+ public object m_object;
+
+ private List m_FieldInfos;
+ private List m_PropertyInfos;
+
+ private bool m_autoUpdate = false;
+ private string m_search = "";
+ public MemberFilter m_filter = MemberFilter.Property;
+
+ public enum MemberFilter
+ {
+ Both,
+ Property,
+ Field
+ }
+
+ 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();
+
+ 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 override void Init()
+ {
+ m_object = Target;
+
+ m_FieldInfos = new List();
+ m_PropertyInfos = new List();
+
+ var type = GetActualType(m_object);
+ if (type == null)
+ {
+ MelonLogger.Log("could not get underlying type for object. ToString(): " + m_object.ToString());
+ return;
+ }
+
+ m_objectType = type;
+ GetFields(m_object);
+ GetProperties(m_object);
+
+ UpdateValues();
+ }
+
+ public override void Update()
+ {
+ if (m_autoUpdate)
+ {
+ UpdateValues();
+ }
+ }
+
+ private void UpdateValues()
+ {
+ if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
+ {
+ foreach (var holder in this.m_FieldInfos)
+ {
+ if (m_search == "" || holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
+ {
+ holder.UpdateValue(m_object);
+ }
+ }
+ }
+
+ if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
+ {
+ foreach (var holder in this.m_PropertyInfos)
+ {
+ if (m_search == "" || holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
+ {
+ holder.UpdateValue(m_object);
+ }
+ }
+ }
+ }
+
+ public override void WindowFunction(int windowID)
+ {
+ try
+ {
+ Header();
+
+ GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Type: " + m_objectType.Name + "", null);
+
+ bool unityObj = m_object is UnityEngine.Object;
+
+ if (unityObj)
+ {
+ GUILayout.Label("Name: " + (m_object as UnityEngine.Object).name, null);
+ }
+ GUILayout.EndHorizontal();
+
+ if (unityObj)
+ {
+ GUILayout.BeginHorizontal(null);
+
+ GUILayout.Label("Tools:", new GUILayoutOption[] { GUILayout.Width(80) });
+
+ UIStyles.InstantiateButton((UnityEngine.Object)m_object);
+
+ if (m_object is Component comp && comp.gameObject is GameObject obj)
+ {
+ GUI.skin.label.alignment = TextAnchor.MiddleRight;
+ GUILayout.Label("GameObject:", null);
+ if (GUILayout.Button("" + obj.name + "", new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 350) }))
+ {
+ WindowManager.InspectObject(obj, out bool _);
+ }
+ GUI.skin.label.alignment = TextAnchor.UpperLeft;
+ }
+
+ GUILayout.EndHorizontal();
+ }
+
+ UIStyles.HorizontalLine(Color.grey);
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Search:", new GUILayoutOption[] { GUILayout.Width(75) });
+ m_search = GUILayout.TextField(m_search, null);
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Filter:", new GUILayoutOption[] { GUILayout.Width(75) });
+ FilterToggle(MemberFilter.Both, "Both");
+ FilterToggle(MemberFilter.Property, "Properties");
+ FilterToggle(MemberFilter.Field, "Fields");
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Values:", new GUILayoutOption[] { GUILayout.Width(75) });
+ if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) }))
+ {
+ UpdateValues();
+ }
+ GUI.color = m_autoUpdate ? Color.green : Color.red;
+ m_autoUpdate = GUILayout.Toggle(m_autoUpdate, "Auto-update?", new GUILayoutOption[] { GUILayout.Width(100) });
+ GUI.color = Color.white;
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(10);
+
+ scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
+
+ GUILayout.Space(10);
+
+ if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
+ {
+ UIStyles.HorizontalLine(Color.grey);
+
+ GUILayout.Label("Fields", 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)
+ {
+ UIStyles.HorizontalLine(Color.grey);
+
+ GUILayout.Label("Properties", 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();
+
+ m_rect = WindowManager.ResizeWindow(m_rect, windowID);
+
+ GUILayout.EndArea();
+ }
+ catch (Exception e)
+ {
+ MelonLogger.LogWarning("Exception on window draw. Message: " + e.Message);
+ DestroyWindow();
+ return;
+ }
+ }
+
+ private void FilterToggle(MemberFilter mode, string label)
+ {
+ if (m_filter == mode)
+ {
+ GUI.color = Color.green;
+ }
+ else
+ {
+ GUI.color = Color.white;
+ }
+ if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
+ {
+ m_filter = mode;
+ }
+ GUI.color = Color.white;
+ }
+
+ 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 names = null)
+ {
+ if (names == null)
+ {
+ names = new List();
+ }
+
+ var types = GetAllBaseTypes(m_object);
+
+ foreach (var type in types)
+ {
+ foreach (var pi in type.GetProperties(At.flags))
+ {
+ 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 names = null)
+ {
+ if (names == null)
+ {
+ names = new List();
+ }
+
+ 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);
+ }
+ }
+ }
+
+
+ /* *********************
+ * PROPERTYINFO HOLDER
+ */
+
+ public class PropertyInfoHolder
+ {
+ public Type classType;
+ public PropertyInfo propInfo;
+ public object m_value;
+
+ public PropertyInfoHolder(Type _type, PropertyInfo _propInfo)
+ {
+ classType = _type;
+ propInfo = _propInfo;
+ }
+
+ public void Draw(ReflectionWindow window)
+ {
+ if (propInfo.CanWrite)
+ {
+ UIStyles.DrawMember(ref m_value, propInfo.PropertyType.Name, propInfo.Name, window.m_rect, window.m_object, SetValue);
+ }
+ else
+ {
+ UIStyles.DrawMember(ref m_value, propInfo.PropertyType.Name, propInfo.Name, window.m_rect, window.m_object);
+ }
+ }
+
+ public void UpdateValue(object obj)
+ {
+ try
+ {
+ if (obj is Il2CppSystem.Object ilObject)
+ {
+ var declaringType = this.propInfo.DeclaringType;
+ if (declaringType == typeof(Il2CppObjectBase))
+ {
+ m_value = ilObject.Pointer;
+ }
+ else
+ {
+ var cast = CppExplorer.Il2CppCast(obj, declaringType);
+ m_value = this.propInfo.GetValue(cast, null);
+ }
+ }
+ else
+ {
+ m_value = this.propInfo.GetValue(obj, null);
+ }
+ }
+ catch (Exception e)
+ {
+ //MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name);
+ //MelonLogger.Log(e.GetType() + ", " + e.Message);
+
+ //var inner = e.InnerException;
+ //while (inner != null)
+ //{
+ // MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message);
+ // inner = inner.InnerException;
+ //}
+
+ m_value = null;
+ }
+ }
+
+ public void SetValue(object obj)
+ {
+ try
+ {
+ if (propInfo.PropertyType.IsEnum)
+ {
+ if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null)
+ {
+ m_value = enumValue;
+ }
+ }
+ else if (propInfo.PropertyType.IsPrimitive)
+ {
+ if (propInfo.PropertyType == typeof(float))
+ {
+ if (float.TryParse(m_value.ToString(), out float f))
+ {
+ m_value = f;
+ }
+ else
+ {
+ MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!");
+ }
+ }
+ else if (propInfo.PropertyType == typeof(double))
+ {
+ if (double.TryParse(m_value.ToString(), out double d))
+ {
+ m_value = d;
+ }
+ else
+ {
+ MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!");
+ }
+ }
+ else if (propInfo.PropertyType != typeof(bool))
+ {
+ if (int.TryParse(m_value.ToString(), out int i))
+ {
+ m_value = i;
+ }
+ else
+ {
+ MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType);
+ }
+ }
+ }
+
+ var declaring = propInfo.DeclaringType;
+ var cast = CppExplorer.Il2CppCast(obj, declaring);
+
+ propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null);
+ }
+ catch
+ {
+ //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name);
+ }
+ }
+ }
+
+
+ /* *********************
+ * FIELDINFO HOLDER
+ */
+
+ public class FieldInfoHolder
+ {
+ public Type classType;
+ public FieldInfo fieldInfo;
+ public object m_value;
+
+ public FieldInfoHolder(Type _type, FieldInfo _fieldInfo)
+ {
+ classType = _type;
+ fieldInfo = _fieldInfo;
+ }
+
+ public void UpdateValue(object obj)
+ {
+ m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj);
+ }
+
+ public void Draw(ReflectionWindow window)
+ {
+ bool canSet = !(fieldInfo.IsLiteral && !fieldInfo.IsInitOnly);
+
+ if (canSet)
+ {
+ UIStyles.DrawMember(ref m_value, fieldInfo.FieldType.Name, fieldInfo.Name, window.m_rect, window.m_object, SetValue);
+ }
+ else
+ {
+ UIStyles.DrawMember(ref m_value, fieldInfo.FieldType.Name, fieldInfo.Name, window.m_rect, window.m_object);
+ }
+ }
+
+ public void SetValue(object obj)
+ {
+ if (fieldInfo.FieldType.IsEnum)
+ {
+ if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null)
+ {
+ m_value = enumValue;
+ }
+ }
+ else if (fieldInfo.FieldType.IsPrimitive)
+ {
+ if (fieldInfo.FieldType == typeof(float))
+ {
+ if (float.TryParse(m_value.ToString(), out float f))
+ {
+ m_value = f;
+ }
+ else
+ {
+ MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!");
+ }
+ }
+ else if (fieldInfo.FieldType == typeof(double))
+ {
+ if (double.TryParse(m_value.ToString(), out double d))
+ {
+ m_value = d;
+ }
+ else
+ {
+ MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!");
+ }
+ }
+ else if (fieldInfo.FieldType != typeof(bool))
+ {
+ if (int.TryParse(m_value.ToString(), out int i))
+ {
+ m_value = i;
+ }
+ else
+ {
+ MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType);
+ }
+ }
+ }
+
+ fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value);
+ }
+ }
+ }
+}
diff --git a/src_2018/MainMenu/MainMenu.cs b/src_2018/MainMenu/MainMenu.cs
new file mode 100644
index 0000000..7488cef
--- /dev/null
+++ b/src_2018/MainMenu/MainMenu.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.EventSystems;
+using MelonLoader;
+
+namespace Explorer
+{
+ public class MainMenu
+ {
+ public static MainMenu Instance;
+
+ public MainMenu()
+ {
+ Instance = this;
+
+ Pages.Add(new ScenePage());
+ Pages.Add(new SearchPage());
+ Pages.Add(new ConsolePage());
+
+ foreach (var page in Pages)
+ {
+ page.Init();
+ }
+ }
+
+ public const int MainWindowID = 10;
+ public static Rect MainRect = new Rect(5, 5, 550, 700);
+ private static readonly List Pages = new List();
+ private static int m_currentPage = 0;
+
+ public static void SetCurrentPage(int index)
+ {
+ if (index < 0 || Pages.Count <= index)
+ {
+ MelonLogger.Log("cannot set page " + index);
+ return;
+ }
+ m_currentPage = index;
+ GUI.BringWindowToFront(MainWindowID);
+ GUI.FocusWindow(MainWindowID);
+ }
+
+ public void Update()
+ {
+ Pages[m_currentPage].Update();
+ }
+
+ public void OnGUI()
+ {
+ if (CppExplorer.ShowMenu)
+ {
+ var origSkin = GUI.skin;
+ GUI.skin = UIStyles.WindowSkin;
+
+ MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, "IL2CPP Runtime Explorer");
+
+ GUI.skin = origSkin;
+ }
+ }
+
+ private void MainWindow(int id)
+ {
+ GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20));
+
+ if (GUI.Button(new Rect(MainRect.width - 90, 2, 80, 20), "Hide (F7)"))
+ {
+ CppExplorer.ShowMenu = false;
+ return;
+ }
+
+ GUILayout.BeginArea(new Rect(5, 25, MainRect.width - 10, MainRect.height - 35), GUI.skin.box);
+
+ MainHeader();
+
+ var page = Pages[m_currentPage];
+ page.scroll = GUILayout.BeginScrollView(page.scroll, GUI.skin.scrollView);
+ page.DrawWindow();
+ GUILayout.EndScrollView();
+
+ MainRect = WindowManager.ResizeWindow(MainRect, MainWindowID);
+
+ GUILayout.EndArea();
+ }
+
+ private void MainHeader()
+ {
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Options:", new GUILayoutOption[] { GUILayout.Width(70) });
+ GUI.skin.label.alignment = TextAnchor.MiddleRight;
+ GUILayout.Label("Array Limit:", new GUILayoutOption[] { GUILayout.Width(70) });
+ GUI.skin.label.alignment = TextAnchor.UpperLeft;
+ var _input = GUILayout.TextField(CppExplorer.ArrayLimit.ToString(), new GUILayoutOption[] { GUILayout.Width(60) });
+ if (int.TryParse(_input, out int _lim))
+ {
+ CppExplorer.ArrayLimit = _lim;
+ }
+ CppExplorer.Instance.MouseInspect = GUILayout.Toggle(CppExplorer.Instance.MouseInspect, "Inspect Under Mouse (Shift + RMB)", null);
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal(null);
+ for (int i = 0; i < Pages.Count; i++)
+ {
+ if (m_currentPage == i)
+ GUI.color = Color.green;
+ else
+ GUI.color = Color.white;
+
+ if (GUILayout.Button(Pages[i].Name, null))
+ {
+ m_currentPage = i;
+ }
+ }
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(10);
+ GUI.color = Color.white;
+ }
+
+ public abstract class WindowPage
+ {
+ public virtual string Name { get; set; }
+
+ public Vector2 scroll = Vector2.zero;
+
+ public abstract void Init();
+
+ public abstract void DrawWindow();
+
+ public abstract void Update();
+ }
+ }
+}
diff --git a/src_2018/MainMenu/Pages/Console/REPL.cs b/src_2018/MainMenu/Pages/Console/REPL.cs
new file mode 100644
index 0000000..97ab678
--- /dev/null
+++ b/src_2018/MainMenu/Pages/Console/REPL.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Mono.CSharp;
+using UnityEngine;
+using Attribute = System.Attribute;
+using Object = UnityEngine.Object;
+
+namespace Explorer
+{
+ public class REPL : InteractiveBase
+ {
+ static REPL()
+ {
+ var go = new GameObject("UnityREPL");
+ GameObject.DontDestroyOnLoad(go);
+ //go.transform.parent = HPExplorer.Instance.transform;
+ MB = go.AddComponent();
+ }
+
+ [Documentation("MB - A dummy MonoBehaviour for accessing Unity.")]
+ public static ReplHelper MB { get; }
+
+ [Documentation("find() - find a UnityEngine.Object of type T.")]
+ public static T find() where T : Object
+ {
+ return MB.Find();
+ }
+
+ [Documentation("findAll() - find all UnityEngine.Object of type T.")]
+ public static T[] findAll() where T : Object
+ {
+ return MB.FindAll();
+ }
+
+ [Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
+ public static object runCoroutine(IEnumerator i)
+ {
+ return MB.RunCoroutine(i);
+ }
+
+ [Documentation("endCoroutine(co) - ends a Unity coroutine.")]
+ public static void endCoroutine(Coroutine c)
+ {
+ MB.EndCoroutine(c);
+ }
+
+ ////[Documentation("type() - obtain type info about a type T. Provides some Reflection helpers.")]
+ ////public static TypeHelper type()
+ ////{
+ //// return new TypeHelper(typeof(T));
+ ////}
+
+ ////[Documentation("type(obj) - obtain type info about object obj. Provides some Reflection helpers.")]
+ ////public static TypeHelper type(object instance)
+ ////{
+ //// return new TypeHelper(instance);
+ ////}
+
+ //[Documentation("dir(obj) - lists all available methods and fiels of a given obj.")]
+ //public static string dir(object instance)
+ //{
+ // return type(instance).info();
+ //}
+
+ //[Documentation("dir() - lists all available methods and fields of type T.")]
+ //public static string dir()
+ //{
+ // return type().info();
+ //}
+
+ //[Documentation("findrefs(obj) - find references to the object in currently loaded components.")]
+ //public static Component[] findrefs(object obj)
+ //{
+ // if (obj == null) throw new ArgumentNullException(nameof(obj));
+
+ // var results = new List();
+ // foreach (var component in Object.FindObjectsOfType())
+ // {
+ // var type = component.GetType();
+
+ // var nameBlacklist = new[] { "parent", "parentInternal", "root", "transform", "gameObject" };
+ // var typeBlacklist = new[] { typeof(bool) };
+
+ // foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+ // .Where(x => x.CanRead && !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.PropertyType)))
+ // {
+ // try
+ // {
+ // if (Equals(prop.GetValue(component, null), obj))
+ // {
+ // results.Add(component);
+ // goto finish;
+ // }
+ // }
+ // catch { }
+ // }
+ // foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+ // .Where(x => !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.FieldType)))
+ // {
+ // try
+ // {
+ // if (Equals(field.GetValue(component), obj))
+ // {
+ // results.Add(component);
+ // goto finish;
+ // }
+ // }
+ // catch { }
+ // }
+ // finish:;
+ // }
+
+ // return results.ToArray();
+ //}
+
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
+ private class DocumentationAttribute : Attribute
+ {
+ public DocumentationAttribute(string doc)
+ {
+ Docs = doc;
+ }
+
+ public string Docs { get; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src_2018/MainMenu/Pages/Console/REPLHelper.cs b/src_2018/MainMenu/Pages/Console/REPLHelper.cs
new file mode 100644
index 0000000..d228761
--- /dev/null
+++ b/src_2018/MainMenu/Pages/Console/REPLHelper.cs
@@ -0,0 +1,34 @@
+using System.Collections;
+//using Il2CppSystem;
+using MelonLoader;
+using UnityEngine;
+using System;
+using Object = UnityEngine.Object;
+
+namespace Explorer
+{
+ public class ReplHelper : MonoBehaviour
+ {
+ public ReplHelper(IntPtr intPtr) : base(intPtr) { }
+
+ public T Find() where T : Object
+ {
+ return FindObjectOfType();
+ }
+
+ public T[] FindAll() where T : Object
+ {
+ return FindObjectsOfType();
+ }
+
+ public object RunCoroutine(IEnumerator enumerator)
+ {
+ return MelonCoroutines.Start(enumerator);
+ }
+
+ public void EndCoroutine(Coroutine c)
+ {
+ StopCoroutine(c);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src_2018/MainMenu/Pages/ConsolePage.cs b/src_2018/MainMenu/Pages/ConsolePage.cs
new file mode 100644
index 0000000..ac583ed
--- /dev/null
+++ b/src_2018/MainMenu/Pages/ConsolePage.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using System.Reflection;
+using Mono.CSharp;
+using System.IO;
+using MelonLoader;
+
+namespace Explorer
+{
+ public class ConsolePage : MainMenu.WindowPage
+ {
+ public override string Name { get => "Console"; set => base.Name = value; }
+
+ private ScriptEvaluator _evaluator;
+ private readonly StringBuilder _sb = new StringBuilder();
+
+ private string MethodInput = "";
+ private string UsingInput = "";
+
+ public static List UsingDirectives;
+
+ private static readonly string[] m_defaultUsing = new string[]
+ {
+ "System",
+ "UnityEngine",
+ "System.Linq",
+ "System.Collections",
+ "System.Collections.Generic",
+ "System.Reflection",
+ "MelonLoader"
+ };
+
+ public override void Init()
+ {
+ UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp();
+
+ try
+ {
+ MethodInput = @"// This is a basic REPL console used to execute a method.
+// Some common directives are added by default, you can add more below.
+// If you want to return some output, MelonLogger.Log() it.
+
+MelonLogger.Log(""hello world"");";
+
+ ResetConsole();
+ }
+ catch (Exception e)
+ {
+ MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}\r\nStack: {e.StackTrace}");
+ }
+ }
+
+ public void ResetConsole()
+ {
+ if (_evaluator != null)
+ {
+ _evaluator.Dispose();
+ }
+
+ _evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) };
+
+ UsingDirectives = new List();
+ UsingDirectives.AddRange(m_defaultUsing);
+ foreach (string asm in UsingDirectives)
+ {
+ Evaluate(AsmToUsing(asm));
+ }
+ }
+
+ public string AsmToUsing(string asm, bool richtext = false)
+ {
+ if (richtext)
+ {
+ return $"using {asm};";
+ }
+ return $"using {asm};";
+ }
+
+ public void AddUsing(string asm)
+ {
+ if (!UsingDirectives.Contains(asm))
+ {
+ UsingDirectives.Add(asm);
+ Evaluate(AsmToUsing(asm));
+ }
+ }
+
+ public object Evaluate(string str)
+ {
+ object ret = VoidType.Value;
+
+ _evaluator.Compile(str, out var compiled);
+
+ try
+ {
+ compiled?.Invoke(ref ret);
+ }
+ catch (Exception e)
+ {
+ MelonLogger.LogWarning(e.ToString());
+ }
+
+ return ret;
+ }
+
+
+ public override void DrawWindow()
+ {
+ GUILayout.Label("REPL Console", null);
+
+ GUILayout.Label("Method:", null);
+ MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) });
+
+ if (GUILayout.Button("Execute", null))
+ {
+ try
+ {
+ MethodInput = MethodInput.Trim();
+
+ if (!string.IsNullOrEmpty(MethodInput))
+ {
+ var result = Evaluate(MethodInput);
+
+ if (result != null && !Equals(result, VoidType.Value))
+ {
+ MelonLogger.Log("[Console Output]\r\n" + result.ToString());
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ MelonLogger.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
+ }
+ }
+
+ GUILayout.Label("Using directives:", null);
+ foreach (var asm in UsingDirectives)
+ {
+ GUILayout.Label(AsmToUsing(asm, true), null);
+ }
+ GUILayout.BeginHorizontal(null);
+ GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(110) });
+ UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
+ if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(50) }))
+ {
+ AddUsing(UsingInput);
+ }
+ if (GUILayout.Button("Reset", null))
+ {
+ ResetConsole();
+ }
+ GUILayout.EndHorizontal();
+ }
+
+ public override void Update() { }
+
+ private class VoidType
+ {
+ public static readonly VoidType Value = new VoidType();
+ private VoidType() { }
+ }
+
+ }
+
+ internal class ScriptEvaluator : Evaluator, IDisposable
+ {
+ private static readonly HashSet StdLib =
+ new HashSet(StringComparer.InvariantCultureIgnoreCase) { "mscorlib", "System.Core", "System", "System.Xml" };
+
+ private readonly TextWriter _logger;
+
+ public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger))
+ {
+ _logger = logger;
+
+ ImportAppdomainAssemblies(ReferenceAssembly);
+ AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
+ }
+
+ public void Dispose()
+ {
+ AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
+ _logger.Dispose();
+ }
+
+ private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
+ {
+ string name = args.LoadedAssembly.GetName().Name;
+ if (StdLib.Contains(name))
+ return;
+ ReferenceAssembly(args.LoadedAssembly);
+ }
+
+ private static CompilerContext BuildContext(TextWriter tw)
+ {
+ var reporter = new StreamReportPrinter(tw);
+
+ var settings = new CompilerSettings
+ {
+ Version = LanguageVersion.Experimental,
+ GenerateDebugInfo = false,
+ StdLib = true,
+ Target = Target.Library,
+ WarningLevel = 0,
+ EnhancedWarnings = false
+ };
+
+ return new CompilerContext(settings, reporter);
+ }
+
+ private static void ImportAppdomainAssemblies(Action import)
+ {
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ string name = assembly.GetName().Name;
+ if (StdLib.Contains(name))
+ continue;
+ import(assembly);
+ }
+ }
+ }
+}
diff --git a/src_2018/MainMenu/Pages/ScenePage.cs b/src_2018/MainMenu/Pages/ScenePage.cs
new file mode 100644
index 0000000..59fc7c7
--- /dev/null
+++ b/src_2018/MainMenu/Pages/ScenePage.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MelonLoader;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+
+namespace Explorer
+{
+ public class ScenePage : MainMenu.WindowPage
+ {
+ public static ScenePage Instance;
+
+ public override string Name { get => "Scene Explorer"; set => base.Name = value; }
+
+ // ----- Holders for GUI elements ----- //
+
+ private string m_currentScene = "";
+
+ // gameobject list
+ private Transform m_currentTransform;
+ private List m_objectList = new List();
+
+ // search bar
+ private bool m_searching = false;
+ private string m_searchInput = "";
+ private List m_searchResults = new List();
+
+ // ------------ Init and Update ------------ //
+
+ public override void Init()
+ {
+ Instance = this;
+ }
+
+ public void OnSceneChange()
+ {
+ m_currentScene = CppExplorer.ActiveSceneName;
+
+ m_currentTransform = null;
+ CancelSearch();
+
+ }
+
+ public override void Update()
+ {
+ if (!m_searching)
+ {
+ m_objectList = new List();
+ if (m_currentTransform)
+ {
+ var noChildren = new List();
+ for (int i = 0; i < m_currentTransform.childCount; i++)
+ {
+ var child = m_currentTransform.GetChild(i);
+
+ if (child)
+ {
+ if (child.childCount > 0)
+ m_objectList.Add(child.gameObject);
+ else
+ noChildren.Add(child.gameObject);
+ }
+ }
+ m_objectList.AddRange(noChildren);
+ noChildren = null;
+ }
+ else
+ {
+ var scene = SceneManager.GetActiveScene();
+ var rootObjects = scene.GetRootGameObjects();
+
+ // add objects with children first
+ foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0))
+ {
+ m_objectList.Add(obj);
+ }
+ foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0))
+ {
+ m_objectList.Add(obj);
+ }
+ }
+ }
+ }
+
+ // --------- GUI Draw Functions --------- //
+
+ public override void DrawWindow()
+ {
+ try
+ {
+ // Current Scene label
+ GUILayout.Label("Current Scene: " + m_currentScene + "", null);
+
+ // ----- GameObject Search -----
+ GUILayout.BeginHorizontal(GUI.skin.box, null);
+ GUILayout.Label("Search Scene:", new GUILayoutOption[] { GUILayout.Width(100) });
+ m_searchInput = GUILayout.TextField(m_searchInput, null);
+ if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
+ {
+ Search();
+ }
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(15);
+
+ // ************** GameObject list ***************
+
+ // ----- main explorer ------
+ if (!m_searching)
+ {
+ if (m_currentTransform != null)
+ {
+ GUILayout.BeginHorizontal(null);
+ if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
+ {
+ TraverseUp();
+ }
+ else
+ {
+ GUILayout.Label(CppExplorer.GetGameObjectPath(m_currentTransform), null);
+ }
+ GUILayout.EndHorizontal();
+ }
+ else
+ {
+ GUILayout.Label("Scene Root GameObjects:", null);
+ }
+
+ if (m_objectList.Count > 0)
+ {
+ foreach (var obj in m_objectList)
+ {
+ UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
+ }
+ }
+ else
+ {
+ // if m_currentTransform != null ...
+ }
+ }
+ else // ------ Scene Search results ------
+ {
+ if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
+ {
+ CancelSearch();
+ }
+
+ GUILayout.Label("Search Results:", null);
+
+ if (m_searchResults.Count > 0)
+ {
+ foreach (var obj in m_searchResults)
+ {
+ UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
+ }
+ }
+ else
+ {
+ GUILayout.Label("No results found!", null);
+ }
+ }
+ }
+ catch
+ {
+ m_currentTransform = null;
+ }
+ }
+
+
+
+ // -------- Actual Methods (not drawing GUI) ---------- //
+
+ public void SetTransformTarget(GameObject obj)
+ {
+ m_currentTransform = obj.transform;
+ CancelSearch();
+ }
+
+ public void TraverseUp()
+ {
+ if (m_currentTransform.parent != null)
+ {
+ m_currentTransform = m_currentTransform.parent;
+ }
+ else
+ {
+ m_currentTransform = null;
+ }
+ }
+
+ public void Search()
+ {
+ m_searchResults = SearchSceneObjects(m_searchInput);
+ m_searching = true;
+ }
+
+ public void CancelSearch()
+ {
+ m_searching = false;
+ }
+
+ public List SearchSceneObjects(string _search)
+ {
+ var matches = new List();
+
+ foreach (var obj in Resources.FindObjectsOfTypeAll())
+ {
+ if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == CppExplorer.ActiveSceneName)
+ {
+ matches.Add(obj);
+ }
+ }
+
+ return matches;
+ }
+ }
+}
diff --git a/src_2018/MainMenu/Pages/SearchPage.cs b/src_2018/MainMenu/Pages/SearchPage.cs
new file mode 100644
index 0000000..603de38
--- /dev/null
+++ b/src_2018/MainMenu/Pages/SearchPage.cs
@@ -0,0 +1,434 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using System.Reflection;
+using UnityEngine.SceneManagement;
+using Object = UnityEngine.Object;
+using UnhollowerRuntimeLib;
+using MelonLoader;
+using UnhollowerBaseLib;
+
+namespace Explorer
+{
+ public class SearchPage : MainMenu.WindowPage
+ {
+ public static SearchPage Instance;
+
+ public override string Name { get => "Advanced Search"; set => base.Name = value; }
+
+ private string m_searchInput = "";
+ private string m_typeInput = "";
+ private int m_limit = 100;
+
+ public SceneFilter SceneMode = SceneFilter.Any;
+ public TypeFilter TypeMode = TypeFilter.Object;
+
+ public enum SceneFilter
+ {
+ Any,
+ This,
+ DontDestroy,
+ None
+ }
+
+ public enum TypeFilter
+ {
+ Object,
+ GameObject,
+ Component,
+ Custom
+ }
+
+ private List