diff --git a/README.md b/README.md
index 37d04cf..ae4b9e1 100644
--- a/README.md
+++ b/README.md
@@ -39,13 +39,15 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
* Press F7 to show or hide the menu.
* Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory.
+[](img.png)
+
+* An overview of the different CppExplorer menus.
+
### Scene Explorer
* A simple menu which allows you to traverse the Transform heirarchy of the scene.
* Click on a GameObject to set it as the current path, or Inspect it to send it to an Inspector Window.
-[](https://i.imgur.com/BzTOCvp.png)
-
### Inspectors
CppExplorer has two main inspector modes: GameObject Inspector, and Reflection Inspector.
@@ -59,30 +61,22 @@ CppExplorer has two main inspector modes: GameObject Inspector, and Re
* Allows you to see the children and components on a GameObject.
* Can use some basic GameObject Controls such as translating and rotating the object, destroy it, clone it, etc.
-[](https://i.imgur.com/DiDvl0Q.png)
-
### Reflection Inspector
* The Reflection Inspector is used for all other supported objects.
* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods.
* Can search and filter members for the ones you are interested in.
-[](https://i.imgur.com/Pq127XD.png)
-
### Object Search
* You can search for an `UnityEngine.Object` with the Object Search feature.
* Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too.
-[](https://i.imgur.com/lK2RthM.png)
-
### C# REPL console
* A simple C# REPL console, allows you to execute a method body on the fly.
-[](https://i.imgur.com/5U4D1a8.png)
-
### Inspect-under-mouse
* Press Shift+RMB (Right Mouse Button) while the CppExplorer menu is open to begin Inspect-Under-Mouse.
diff --git a/img.png b/img.png
new file mode 100644
index 0000000..1164d20
Binary files /dev/null and b/img.png differ
diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs
index 761b5cf..25714f4 100644
--- a/src/CppExplorer.cs
+++ b/src/CppExplorer.cs
@@ -13,7 +13,7 @@ namespace Explorer
public class CppExplorer : MelonMod
{
public const string NAME = "CppExplorer";
- public const string VERSION = "1.6.3";
+ public const string VERSION = "1.6.4";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.cppexplorer";
@@ -50,6 +50,8 @@ namespace Explorer
{
Instance = this;
+ InputHelper.CheckInput();
+
new MainMenu();
new WindowManager();
diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj
index 5ae78f1..6866327 100644
--- a/src/CppExplorer.csproj
+++ b/src/CppExplorer.csproj
@@ -11,7 +11,7 @@
v4.7.2
512
true
-
+
CppExplorer
false
none
@@ -22,7 +22,7 @@
4
x64
false
-
+
..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll
diff --git a/src/Extensions/ReflectionExtensions.cs b/src/Extensions/ReflectionExtensions.cs
index 66d159e..a45f724 100644
--- a/src/Extensions/ReflectionExtensions.cs
+++ b/src/Extensions/ReflectionExtensions.cs
@@ -3,14 +3,38 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Reflection;
namespace Explorer
{
public static class ReflectionExtensions
{
+ ///
+ /// Extension to allow for easy, non-generic Il2Cpp casting.
+ /// The extension is on System.Object, but only Il2Cpp objects would be a valid target.
+ ///
public static object Il2CppCast(this object obj, Type castTo)
{
return ReflectionHelpers.Il2CppCast(obj, castTo);
}
+
+ ///
+ /// Extension to safely try to get all Types from an Assembly, with a fallback for ReflectionTypeLoadException.
+ ///
+ public static IEnumerable TryGetTypes(this Assembly asm)
+ {
+ try
+ {
+ return asm.GetTypes();
+ }
+ catch (ReflectionTypeLoadException e)
+ {
+ return e.Types.Where(t => t != null);
+ }
+ catch
+ {
+ return Enumerable.Empty();
+ }
+ }
}
}
diff --git a/src/Helpers/InputHelper.cs b/src/Helpers/InputHelper.cs
index e477a82..e38a83b 100644
--- a/src/Helpers/InputHelper.cs
+++ b/src/Helpers/InputHelper.cs
@@ -1,51 +1,107 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using UnityEngine;
+using MelonLoader;
namespace Explorer
{
///
- /// Version-agnostic UnityEngine.Input module using Reflection
+ /// Version-agnostic UnityEngine Input module using Reflection.
///
- public class InputHelper
+ public static class InputHelper
{
- private static readonly Type input = ReflectionHelpers.GetTypeByName("UnityEngine.Input");
+ public static void CheckInput()
+ {
+ if (Input == null)
+ {
+ MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
- private static readonly PropertyInfo mousePositionInfo = input.GetProperty("mousePosition");
+ if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null)
+ {
+ MelonLogger.Log("Ok!");
+ }
+ else
+ {
+ MelonLogger.Log("Could not load Input module!");
+ }
- private static readonly MethodInfo getKey = input.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
- private static readonly MethodInfo getKeyDown = input.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
- private static readonly MethodInfo getMouseButtonDown = input.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
- private static readonly MethodInfo getMouseButton = input.GetMethod("GetMouseButton", new Type[] { typeof(int) });
+ bool TryLoad(string module)
+ {
+ var path = $@"MelonLoader\Managed\{module}";
+ if (!File.Exists(path)) return false;
+
+ try
+ {
+ Assembly.Load(File.ReadAllBytes(path));
+ return true;
+ }
+ catch (Exception e)
+ {
+ MelonLogger.Log(e.GetType() + ", " + e.Message);
+ return false;
+ }
+ }
+ }
+ }
+
+ public static Type Input => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
+ private static Type _input;
+
+ private static PropertyInfo MousePosInfo => _mousePosition ?? (_mousePosition = Input?.GetProperty("mousePosition"));
+ private static PropertyInfo _mousePosition;
+
+ private static MethodInfo GetKeyInfo => _getKey ?? (_getKey = Input?.GetMethod("GetKey", new Type[] { typeof(KeyCode) }));
+ private static MethodInfo _getKey;
+
+ private static MethodInfo GetKeyDownInfo => _getKeyDown ?? (_getKeyDown = Input?.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) }));
+ private static MethodInfo _getKeyDown;
+
+ private static MethodInfo GetMouseButtonInfo => _getMouseButton ?? (_getMouseButton = Input?.GetMethod("GetMouseButton", new Type[] { typeof(int) }));
+ private static MethodInfo _getMouseButton;
+
+ private static MethodInfo GetMouseButtonDownInfo => _getMouseButtonDown ?? (_getMouseButtonDown = Input?.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) }));
+ private static MethodInfo _getMouseButtonDown;
#pragma warning disable IDE1006 // Camel-case property (Unity style)
- public static Vector3 mousePosition => (Vector3)mousePositionInfo.GetValue(null);
+ public static Vector3 mousePosition
+ {
+ get
+ {
+ if (Input == null) return Vector3.zero;
+ return (Vector3)MousePosInfo.GetValue(null);
+ }
+ }
#pragma warning restore IDE1006
public static bool GetKeyDown(KeyCode key)
{
- return (bool)getKeyDown.Invoke(null, new object[] { key });
+ if (Input == null) return false;
+ return (bool)GetKeyDownInfo.Invoke(null, new object[] { key });
}
public static bool GetKey(KeyCode key)
{
- return (bool)getKey.Invoke(null, new object[] { key });
+ if (Input == null) return false;
+ return (bool)GetKeyInfo.Invoke(null, new object[] { key });
}
/// 1 = left, 2 = middle, 3 = right, etc
public static bool GetMouseButtonDown(int btn)
{
- return (bool)getMouseButtonDown.Invoke(null, new object[] { btn });
+ if (Input == null) return false;
+ return (bool)GetMouseButtonDownInfo.Invoke(null, new object[] { btn });
}
/// 1 = left, 2 = middle, 3 = right, etc
public static bool GetMouseButton(int btn)
{
- return (bool)getMouseButton.Invoke(null, new object[] { btn });
+ if (Input == null) return false;
+ return (bool)GetMouseButtonInfo.Invoke(null, new object[] { btn });
}
}
}
diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs
index 47c30bf..8c59525 100644
--- a/src/Helpers/ReflectionHelpers.cs
+++ b/src/Helpers/ReflectionHelpers.cs
@@ -1,16 +1,13 @@
using System;
+using System.Collections;
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 MelonLoader;
-using System.Collections;
-using Mono.CSharp;
+using ILType = Il2CppSystem.Type;
namespace Explorer
{
@@ -18,11 +15,11 @@ namespace Explorer
{
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
- public static Il2CppSystem.Type GameObjectType => Il2CppType.Of();
- public static Il2CppSystem.Type TransformType => Il2CppType.Of();
- public static Il2CppSystem.Type ObjectType => Il2CppType.Of();
- public static Il2CppSystem.Type ComponentType => Il2CppType.Of();
- public static Il2CppSystem.Type BehaviourType => Il2CppType.Of();
+ public static ILType GameObjectType => Il2CppType.Of();
+ public static ILType TransformType => Il2CppType.Of();
+ public static ILType ObjectType => Il2CppType.Of();
+ public static ILType ComponentType => Il2CppType.Of();
+ public static ILType BehaviourType => Il2CppType.Of();
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
@@ -35,47 +32,6 @@ namespace Explorer
.Invoke(obj, null);
}
- public static string ExceptionToString(Exception e)
- {
- if (IsFailedGeneric(e))
- {
- return "Unable to initialize this type.";
- }
- else if (IsObjectCollected(e))
- {
- return "Garbage collected in Il2Cpp.";
- }
-
- return e.GetType() + ", " + e.Message;
- }
-
- public static bool IsFailedGeneric(Exception e)
- {
- return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException));
- }
-
- public static bool IsObjectCollected(Exception e)
- {
- return IsExceptionOfType(e, typeof(ObjectCollectedException));
- }
-
- public static bool IsExceptionOfType(Exception e, Type t, bool strict = true, bool checkInner = true)
- {
- bool isType;
-
- if (strict)
- isType = e.GetType() == t;
- else
- isType = t.IsAssignableFrom(e.GetType());
-
- if (isType) return true;
-
- if (e.InnerException != null && checkInner)
- return IsExceptionOfType(e.InnerException, t, strict);
- else
- return false;
- }
-
public static bool IsEnumerable(Type t)
{
return typeof(IEnumerable).IsAssignableFrom(t);
@@ -117,7 +73,7 @@ namespace Explorer
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
- foreach (var type in GetTypesSafe(asm))
+ foreach (var type in asm.TryGetTypes())
{
if (type.FullName == fullName)
{
@@ -148,13 +104,6 @@ namespace Explorer
return obj.GetType();
}
- public static IEnumerable GetTypesSafe(Assembly asm)
- {
- try { return asm.GetTypes(); }
- catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
- catch { return Enumerable.Empty(); }
- }
-
public static Type[] GetAllBaseTypes(object obj)
{
var list = new List();
@@ -170,5 +119,46 @@ namespace Explorer
return list.ToArray();
}
+
+ public static string ExceptionToString(Exception e)
+ {
+ if (IsFailedGeneric(e))
+ {
+ return "Unable to initialize this type.";
+ }
+ else if (IsObjectCollected(e))
+ {
+ return "Garbage collected in Il2Cpp.";
+ }
+
+ return e.GetType() + ", " + e.Message;
+ }
+
+ public static bool IsFailedGeneric(Exception e)
+ {
+ return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException));
+ }
+
+ public static bool IsObjectCollected(Exception e)
+ {
+ return IsExceptionOfType(e, typeof(ObjectCollectedException));
+ }
+
+ public static bool IsExceptionOfType(Exception e, Type t, bool strict = true, bool checkInner = true)
+ {
+ bool isType;
+
+ if (strict)
+ isType = e.GetType() == t;
+ else
+ isType = t.IsAssignableFrom(e.GetType());
+
+ if (isType) return true;
+
+ if (e.InnerException != null && checkInner)
+ return IsExceptionOfType(e.InnerException, t, strict);
+ else
+ return false;
+ }
}
}
diff --git a/src/MainMenu/Pages/ScenePage.cs b/src/MainMenu/Pages/ScenePage.cs
index 26d173e..3472789 100644
--- a/src/MainMenu/Pages/ScenePage.cs
+++ b/src/MainMenu/Pages/ScenePage.cs
@@ -192,7 +192,7 @@ namespace Explorer
}
}
- // --------- GUI Draw Function --------- //
+ // --------- GUI Draw Function --------- //
public override void DrawWindow()
{
@@ -215,11 +215,9 @@ namespace Explorer
GUILayout.EndVertical();
}
- catch (Exception e)
+ catch
{
- MelonLogger.Log("Exception drawing ScenePage! " + e.GetType() + ", " + e.Message);
- MelonLogger.Log(e.StackTrace);
- m_currentTransform = null;
+ // supress
}
}
@@ -229,39 +227,7 @@ namespace Explorer
// Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
- try
- {
- // Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
- var scenes = SceneManager.GetAllScenes().ToList();
-
- if (scenes.Count > 1)
- {
- int changeWanted = 0;
- if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
- {
- changeWanted = -1;
- }
- if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
- {
- changeWanted = 1;
- }
- if (changeWanted != 0)
- {
- int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
- index += changeWanted;
- if (index > scenes.Count - 1)
- {
- index = 0;
- }
- else if (index < 0)
- {
- index = scenes.Count - 1;
- }
- m_currentScene = scenes[index].name;
- }
- }
- }
- catch { }
+ SceneChangeButtons();
GUILayout.Label("" + m_currentScene + "", null); //new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
@@ -269,7 +235,9 @@ namespace Explorer
// ----- 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();
@@ -279,6 +247,39 @@ namespace Explorer
GUILayout.Space(5);
}
+ private void SceneChangeButtons()
+ {
+ // Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
+ var scenes = SceneManager.GetAllScenes().ToList();
+
+ if (scenes.Count > 1)
+ {
+ int changeWanted = 0;
+ if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
+ {
+ changeWanted = -1;
+ }
+ if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
+ {
+ changeWanted = 1;
+ }
+ if (changeWanted != 0)
+ {
+ int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
+ index += changeWanted;
+ if (index > scenes.Count - 1)
+ {
+ index = 0;
+ }
+ else if (index < 0)
+ {
+ index = scenes.Count - 1;
+ }
+ m_currentScene = scenes[index].name;
+ }
+ }
+ }
+
private void DrawPageButtons()
{
GUILayout.BeginHorizontal(null);
diff --git a/src/MainMenu/Pages/SearchPage.cs b/src/MainMenu/Pages/SearchPage.cs
index 4c4059d..78067be 100644
--- a/src/MainMenu/Pages/SearchPage.cs
+++ b/src/MainMenu/Pages/SearchPage.cs
@@ -385,8 +385,8 @@ namespace Explorer
public static IEnumerable