mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 06:08:16 +08:00
1.6.4
* Fix for games which do not load InputModule on startup. CppExplorer will now try to load the module itself. * Cleanups
This commit is contained in:
parent
4bb0811b2c
commit
38aafa7e5b
14
README.md
14
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)
|
||||
|
||||
* <i>An overview of the different CppExplorer menus.</i>
|
||||
|
||||
### 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 <b>Inspect</b> it to send it to an Inspector Window.
|
||||
|
||||
[](https://i.imgur.com/BzTOCvp.png)
|
||||
|
||||
### Inspectors
|
||||
|
||||
CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
|
||||
@ -59,30 +61,22 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>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.
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to allow for easy, non-generic Il2Cpp casting.
|
||||
/// The extension is on System.Object, but only Il2Cpp objects would be a valid target.
|
||||
/// </summary>
|
||||
public static object Il2CppCast(this object obj, Type castTo)
|
||||
{
|
||||
return ReflectionHelpers.Il2CppCast(obj, castTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension to safely try to get all Types from an Assembly, with a fallback for ReflectionTypeLoadException.
|
||||
/// </summary>
|
||||
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
||||
{
|
||||
try
|
||||
{
|
||||
return asm.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
return e.Types.Where(t => t != null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Version-agnostic UnityEngine.Input module using Reflection
|
||||
/// Version-agnostic UnityEngine Input module using Reflection.
|
||||
/// </summary>
|
||||
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 });
|
||||
}
|
||||
|
||||
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
|
||||
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 });
|
||||
}
|
||||
|
||||
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<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 Il2CppSystem.Type BehaviourType => Il2CppType.Of<Behaviour>();
|
||||
public static ILType GameObjectType => Il2CppType.Of<GameObject>();
|
||||
public static ILType TransformType => Il2CppType.Of<Transform>();
|
||||
public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
|
||||
public static ILType ComponentType => Il2CppType.Of<Component>();
|
||||
public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
|
||||
|
||||
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<Type> GetTypesSafe(Assembly asm)
|
||||
{
|
||||
try { return asm.GetTypes(); }
|
||||
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
|
||||
catch { return Enumerable.Empty<Type>(); }
|
||||
}
|
||||
|
||||
public static Type[] GetAllBaseTypes(object obj)
|
||||
{
|
||||
var list = new List<Type>();
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,7 +227,27 @@ namespace Explorer
|
||||
|
||||
// Current Scene label
|
||||
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
|
||||
try
|
||||
SceneChangeButtons();
|
||||
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
// ----- GameObject Search -----
|
||||
GUILayout.BeginHorizontal(GUI.skin.box, null);
|
||||
GUILayout.Label("<b>Search Scene:</b>", 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(5);
|
||||
}
|
||||
|
||||
private void SceneChangeButtons()
|
||||
{
|
||||
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
|
||||
var scenes = SceneManager.GetAllScenes().ToList();
|
||||
@ -261,23 +279,6 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
// ----- GameObject Search -----
|
||||
GUILayout.BeginHorizontal(GUI.skin.box, null);
|
||||
GUILayout.Label("<b>Search Scene:</b>", 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(5);
|
||||
}
|
||||
|
||||
private void DrawPageButtons()
|
||||
{
|
||||
|
@ -385,7 +385,7 @@ namespace Explorer
|
||||
public static IEnumerable<object> GetInstanceClassScanner()
|
||||
{
|
||||
var query = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(ReflectionHelpers.GetTypesSafe)
|
||||
.SelectMany(t => t.TryGetTypes())
|
||||
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
|
||||
|
||||
var flags = BindingFlags.Public | BindingFlags.Static;
|
||||
|
Loading…
x
Reference in New Issue
Block a user