Compare commits

..

8 Commits
1.6.0 ... 1.6.4

Author SHA1 Message Date
c8a3aecdf4 Update README.md 2020-09-08 17:09:23 +10:00
33c2378f41 Update README.md 2020-09-08 17:09:12 +10:00
38aafa7e5b 1.6.4
* Fix for games which do not load InputModule on startup. CppExplorer will now try to load the module itself.
* Cleanups
2020-09-08 17:07:10 +10:00
4bb0811b2c 1.6.3
* Merged the two builds into one, there is now only one release. Using Reflection for UnityEngine.Input
* A few small fixes and cleanups
2020-09-08 06:21:45 +10:00
4aefe1c5a3 a few tidy ups 2020-09-08 04:33:27 +10:00
c228d29707 Update CppExplorer.cs 2020-09-07 20:28:43 +10:00
56d1507aff 1.6.2
* Fix for a crash that can occur when inspecting unsupported Dictionaries
* Added a scroll bar to the REPL console input area, fixes the issue of the code just being cut off when it goes too long.
2020-09-07 20:28:33 +10:00
72d31eaa64 1.6.1
* Fix for when inspected object gets destroyed
* Fix for displaying Dictionaries/Lists nested inside a Dictionary
* Cleanups
2020-09-07 17:05:37 +10:00
24 changed files with 544 additions and 422 deletions

View File

@ -18,14 +18,6 @@
* CppExplorer may experience a `MissingMethodException` when trying to use certain UnityEngine methods. If you experience this, please open an issue and I will do my best to fix it. * CppExplorer may experience a `MissingMethodException` when trying to use certain UnityEngine methods. If you experience this, please open an issue and I will do my best to fix it.
* Scrolling with mouse wheel in the CppExplorer menu may not work on all games at the moment. * Scrolling with mouse wheel in the CppExplorer menu may not work on all games at the moment.
## Features
* Scene hierarchy explorer
* Search loaded assets with filters
* Traverse and manipulate GameObjects
* Generic Reflection inspector
* C# REPL Console
* Inspect-under-mouse
## How to install ## How to install
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game. Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
@ -39,13 +31,23 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
* Press F7 to show or hide the menu. * Press F7 to show or hide the menu.
* Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory. * Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory.
### Features
[![](img.png)](img.png)
<i>An overview of the different CppExplorer menus.</i>
* Scene hierarchy explorer
* Search loaded assets with filters
* Traverse and manipulate GameObjects
* Generic Reflection inspector
* C# REPL Console
* Inspect-under-mouse
### Scene Explorer ### Scene Explorer
* A simple menu which allows you to traverse the Transform heirarchy of the scene. * 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. * 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)](https://i.imgur.com/BzTOCvp.png)
### Inspectors ### Inspectors
CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>. 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. * 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. * Can use some basic GameObject Controls such as translating and rotating the object, destroy it, clone it, etc.
[![](https://i.imgur.com/DiDvl0Q.png)](https://i.imgur.com/DiDvl0Q.png)
### Reflection Inspector ### Reflection Inspector
* The Reflection Inspector is used for all other supported objects. * 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. * 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. * Can search and filter members for the ones you are interested in.
[![](https://i.imgur.com/Pq127XD.png)](https://i.imgur.com/Pq127XD.png)
### Object Search ### Object Search
* You can search for an `UnityEngine.Object` with the Object Search feature. * You can search for an `UnityEngine.Object` with the Object Search feature.
* Filter by name, type, etc. * Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too. * For GameObjects and Transforms you can filter which scene they are found in too.
[![](https://i.imgur.com/lK2RthM.png)](https://i.imgur.com/lK2RthM.png)
### C# REPL console ### C# REPL console
* A simple C# REPL console, allows you to execute a method body on the fly. * A simple C# REPL console, allows you to execute a method body on the fly.
[![](https://i.imgur.com/5U4D1a8.png)](https://i.imgur.com/5U4D1a8.png)
### Inspect-under-mouse ### Inspect-under-mouse
* Press Shift+RMB (Right Mouse Button) while the CppExplorer menu is open to begin Inspect-Under-Mouse. * Press Shift+RMB (Right Mouse Button) while the CppExplorer menu is open to begin Inspect-Under-Mouse.

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

View File

@ -16,15 +16,14 @@ namespace Explorer
public string ValueTypeName; public string ValueTypeName;
public Type ValueType; public Type ValueType;
// Reflection Inspector only
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; } public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; } public object DeclaringInstance { get; set; }
public string ReflectionException { get; set; }
public int PropertyIndex { get; private set; } public int PropertyIndex { get; private set; }
private string m_propertyIndexInput = "0"; private string m_propertyIndexInput = "0";
public string ReflectionException { get; set; }
public string RichTextName => m_richTextName ?? GetRichTextName(); public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName; private string m_richTextName;

View File

@ -7,16 +7,17 @@ using System.Threading.Tasks;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
using System.Reflection; using System.Reflection;
using UnhollowerBaseLib;
namespace Explorer namespace Explorer
{ {
public class CacheDictionary : CacheObjectBase public class CacheDictionary : CacheObjectBase, IExpandHeight
{ {
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public PageHelper Pages = new PageHelper(); public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public float WhiteSpace = 215f; public PageHelper Pages = new PageHelper();
public float ButtonWidthOffset = 290f;
private CacheObjectBase[] m_cachedKeys; private CacheObjectBase[] m_cachedKeys;
private CacheObjectBase[] m_cachedValues; private CacheObjectBase[] m_cachedValues;
@ -48,15 +49,13 @@ namespace Explorer
} }
private IDictionary m_iDictionary; private IDictionary m_iDictionary;
// ========== Methods ==========
// This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary. // This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary.
private IDictionary Il2CppDictionaryToMono() private IDictionary Il2CppDictionaryToMono()
{ {
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type. // note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
// make generic dictionary from key and value type
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
.MakeGenericType(TypeOfKeys, TypeOfValues));
// get keys and values // get keys and values
var keys = ValueType.GetProperty("Keys") .GetValue(Value); var keys = ValueType.GetProperty("Keys") .GetValue(Value);
var values = ValueType.GetProperty("Values").GetValue(Value); var values = ValueType.GetProperty("Values").GetValue(Value);
@ -65,27 +64,15 @@ namespace Explorer
var keyList = new List<object>(); var keyList = new List<object>();
var valueList = new List<object>(); var valueList = new List<object>();
// get keys enumerator and store keys // store entries with reflection
var keyEnumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null); EnumerateWithReflection(keys, keyList);
var keyCollectionType = keyEnumerator.GetType(); EnumerateWithReflection(values, valueList);
var keyMoveNext = keyCollectionType.GetMethod("MoveNext");
var keyCurrent = keyCollectionType.GetProperty("Current");
while ((bool)keyMoveNext.Invoke(keyEnumerator, null))
{
keyList.Add(keyCurrent.GetValue(keyEnumerator));
}
// get values enumerator and store values // make actual mono dictionary
var valueEnumerator = values.GetType().GetMethod("GetEnumerator").Invoke(values, null); var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
var valueCollectionType = valueEnumerator.GetType(); .MakeGenericType(TypeOfKeys, TypeOfValues));
var valueMoveNext = valueCollectionType.GetMethod("MoveNext");
var valueCurrent = valueCollectionType.GetProperty("Current");
while ((bool)valueMoveNext.Invoke(valueEnumerator, null))
{
valueList.Add(valueCurrent.GetValue(valueEnumerator));
}
// finally iterate into actual dictionary // finally iterate into dictionary
for (int i = 0; i < keyList.Count; i++) for (int i = 0; i < keyList.Count; i++)
{ {
dict.Add(keyList[i], valueList[i]); dict.Add(keyList[i], valueList[i]);
@ -94,47 +81,63 @@ namespace Explorer
return dict; return dict;
} }
// ========== Methods ========== private void EnumerateWithReflection(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
list.Add(current.GetValue(enumerator));
}
}
private void GetGenericArguments() private void GetGenericArguments()
{ {
if (m_keysType == null || m_valuesType == null) if (this.MemInfo != null)
{ {
if (this.MemInfo != null) Type memberType = null;
switch (this.MemInfo.MemberType)
{ {
Type memberType = null; case MemberTypes.Field:
switch (this.MemInfo.MemberType) memberType = (MemInfo as FieldInfo).FieldType;
{ break;
case MemberTypes.Field: case MemberTypes.Property:
memberType = (MemInfo as FieldInfo).FieldType; memberType = (MemInfo as PropertyInfo).PropertyType;
break; break;
case MemberTypes.Property:
memberType = (MemInfo as PropertyInfo).PropertyType;
break;
}
if (memberType != null && memberType.IsGenericType)
{
m_keysType = memberType.GetGenericArguments()[0];
m_valuesType = memberType.GetGenericArguments()[1];
}
} }
else if (Value != null)
if (memberType != null && memberType.IsGenericType)
{ {
var type = Value.GetType(); m_keysType = memberType.GetGenericArguments()[0];
if (type.IsGenericType) m_valuesType = memberType.GetGenericArguments()[1];
{ }
m_keysType = type.GetGenericArguments()[0]; }
m_valuesType = type.GetGenericArguments()[1]; else if (Value != null)
} {
var type = Value.GetType();
if (type.IsGenericType)
{
m_keysType = type.GetGenericArguments()[0];
m_valuesType = type.GetGenericArguments()[1];
} }
} }
return;
} }
public override void UpdateValue() public override void UpdateValue()
{ {
// first make sure we won't run into a TypeInitializationException.
if (!EnsureDictionaryIsSupported())
{
ReflectionException = "Dictionary Type not supported with Reflection!";
return;
}
base.UpdateValue(); base.UpdateValue();
// reset // reset
@ -165,6 +168,28 @@ namespace Explorer
m_cachedValues = values.ToArray(); m_cachedValues = values.ToArray();
} }
private bool EnsureDictionaryIsSupported()
{
try
{
return Check(TypeOfKeys) && Check(TypeOfValues);
bool Check(Type type)
{
var ptr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(type)
.GetField("NativeClassPtr")
.GetValue(null);
return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
}
}
catch
{
return false;
}
}
// ============= GUI Draw ============= // ============= GUI Draw =============
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
@ -258,15 +283,11 @@ namespace Explorer
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.MinWidth((window.width / 3) - 60f) });
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
key.DrawValue(window, (window.width / 2) - 30f); key.DrawValue(window, (window.width / 2) - 30f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
val.DrawValue(window, (window.width / 2) - 30f); val.DrawValue(window, (window.width / 2) - 30f);
GUILayout.EndHorizontal();
} }
} }

View File

@ -8,13 +8,13 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheList : CacheObjectBase public class CacheList : CacheObjectBase, IExpandHeight
{ {
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public PageHelper Pages = new PageHelper(); public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public float WhiteSpace = 215f; public PageHelper Pages = new PageHelper();
public float ButtonWidthOffset = 290f;
private CacheObjectBase[] m_cachedEntries; private CacheObjectBase[] m_cachedEntries;
@ -52,6 +52,7 @@ namespace Explorer
{ {
get => GetItemProperty(); get => GetItemProperty();
} }
private PropertyInfo m_itemProperty; private PropertyInfo m_itemProperty;
// ========== Methods ========== // ========== Methods ==========

View File

@ -18,28 +18,17 @@ namespace Explorer
private ParameterInfo[] m_arguments; private ParameterInfo[] m_arguments;
private string[] m_argumentInput; private string[] m_argumentInput;
public bool HasParameters public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
{
get
{
if (m_hasParams == null)
{
m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
}
return (bool)m_hasParams;
}
}
private bool? m_hasParams;
public static bool CanEvaluate(MethodInfo mi) public static bool CanEvaluate(MethodInfo mi)
{ {
// generic type args not supported yet // TODO generic args
if (mi.GetGenericArguments().Length > 0) if (mi.GetGenericArguments().Length > 0)
{ {
return false; return false;
} }
// only primitive and string args supported // primitive and string args supported
foreach (var param in mi.GetParameters()) foreach (var param in mi.GetParameters())
{ {
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string)) if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
@ -64,7 +53,84 @@ namespace Explorer
public override void UpdateValue() public override void UpdateValue()
{ {
//base.UpdateValue(); //base.UpdateValue();
} }
private void Evaluate()
{
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]);
m_evaluated = true;
}
else
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type == typeof(string))
{
parsedArgs.Add(input);
}
else
{
try
{
if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed)
{
parsedArgs.Add(parsed);
}
else
{
// try add a null arg i guess
parsedArgs.Add(null);
}
}
catch
{
MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
break;
}
}
}
try
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, parsedArgs.ToArray());
m_evaluated = true;
}
catch (Exception e)
{
MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}");
}
}
if (ret != null)
{
m_cachedReturnValue = GetCacheObject(ret);
if (m_cachedReturnValue is IExpandHeight expander)
{
expander.WhiteSpace = 0f;
expander.ButtonWidthOffset += 70f;
}
m_cachedReturnValue.UpdateValue();
}
else
{
m_cachedReturnValue = null;
}
}
// ==== GUI DRAW ====
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
@ -124,15 +190,7 @@ namespace Explorer
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
{ {
try m_cachedReturnValue.DrawValue(window, width);
{
m_cachedReturnValue.DrawValue(window, width);
}
catch (Exception e)
{
MelonLogger.Log("Exception drawing m_cachedReturnValue!");
MelonLogger.Log(e.ToString());
}
} }
else else
{ {
@ -147,82 +205,5 @@ namespace Explorer
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
private void Evaluate()
{
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]);
m_evaluated = true;
}
else
{
var arguments = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type == typeof(string))
{
arguments.Add(input);
}
else
{
try
{
if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed)
{
arguments.Add(parsed);
}
else
{
throw new Exception();
}
}
catch
{
MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
break;
}
}
}
if (arguments.Count == m_arguments.Length)
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, arguments.ToArray());
m_evaluated = true;
}
else
{
MelonLogger.Log($"Did not invoke because {m_arguments.Length - arguments.Count} arguments could not be parsed!");
}
}
if (ret != null)
{
m_cachedReturnValue = GetCacheObject(ret);
if (m_cachedReturnValue is CacheList cacheList)
{
cacheList.WhiteSpace = 0f;
cacheList.ButtonWidthOffset += 70f;
}
else if (m_cachedReturnValue is CacheDictionary cacheDict)
{
cacheDict.WhiteSpace = 0f;
cacheDict.ButtonWidthOffset += 70f;
}
m_cachedReturnValue.UpdateValue();
}
else
{
m_cachedReturnValue = null;
}
}
} }
} }

View File

@ -50,7 +50,7 @@ namespace Explorer
} }
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width) })) if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }

View File

@ -12,15 +12,10 @@ namespace Explorer
{ {
public class CppExplorer : MelonMod public class CppExplorer : MelonMod
{ {
public const string GUID = "com.sinai.cppexplorer"; public const string NAME = "CppExplorer";
public const string VERSION = "1.6.0"; public const string VERSION = "1.6.4";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.cppexplorer";
public const string NAME = "CppExplorer"
#if Release_Unity2018
+ " (Unity 2018)"
#endif
;
public static CppExplorer Instance { get; private set; } public static CppExplorer Instance { get; private set; }
@ -55,6 +50,8 @@ namespace Explorer
{ {
Instance = this; Instance = this;
InputHelper.CheckInput();
new MainMenu(); new MainMenu();
new WindowManager(); new WindowManager();
@ -79,7 +76,7 @@ namespace Explorer
public override void OnUpdate() public override void OnUpdate()
{ {
// Check main toggle key input // Check main toggle key input
if (Input.GetKeyDown(KeyCode.F7)) if (InputHelper.GetKeyDown(KeyCode.F7))
{ {
ShowMenu = !ShowMenu; ShowMenu = !ShowMenu;
} }
@ -87,7 +84,7 @@ namespace Explorer
if (ShowMenu) if (ShowMenu)
{ {
// Check Force-Unlock input // Check Force-Unlock input
if (Input.GetKeyDown(KeyCode.LeftAlt)) if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
{ {
ForceUnlockMouse = !ForceUnlockMouse; ForceUnlockMouse = !ForceUnlockMouse;
} }

View File

@ -2,7 +2,7 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid> <ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
@ -12,26 +12,12 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AssemblyName>CppExplorer</AssemblyName> <AssemblyName>CppExplorer</AssemblyName>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType> <DebugType>none</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\Release\2019\</OutputPath> <OutputPath>..\Release\</OutputPath>
<DefineConstants>Release_2019</DefineConstants> <DefineConstants />
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Unity2018|AnyCPU' ">
<AssemblyName>CppExplorer_Unity2018</AssemblyName>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\2018\</OutputPath>
<DefineConstants>Release_Unity2018</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
@ -60,66 +46,34 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- Unity 2019 build (InputLegacyModule.dll) --> <!-- Replace these references with ones from your game (..\MelonLoader\ folder) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.PhysicsModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.UI">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- Unity 2018 build (InputModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.InputModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Helpers\IExpandHeight.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" /> <Compile Include="CachedObjects\Struct\CacheColor.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" /> <Compile Include="CachedObjects\Object\CacheDictionary.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" /> <Compile Include="CachedObjects\Struct\CacheEnum.cs" />
@ -133,6 +87,7 @@
<Compile Include="CachedObjects\Struct\CacheRect.cs" /> <Compile Include="CachedObjects\Struct\CacheRect.cs" />
<Compile Include="CppExplorer.cs" /> <Compile Include="CppExplorer.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" /> <Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" /> <Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" /> <Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
<Compile Include="Extensions\UnityExtensions.cs" /> <Compile Include="Extensions\UnityExtensions.cs" />

View File

@ -7,14 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppExplorer", "CppExplorer.
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU
Release_Unity2018|Any CPU = Release_Unity2018|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Release|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_Unity2018|Any CPU.ActiveCfg = Release_Unity2018|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_Unity2018|Any CPU.Build.0 = Release_Unity2018|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -3,14 +3,38 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reflection;
namespace Explorer namespace Explorer
{ {
public static class ReflectionExtensions 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) public static object Il2CppCast(this object obj, Type castTo)
{ {
return ReflectionHelpers.Il2CppCast(obj, 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>();
}
}
} }
} }

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Explorer
{
interface IExpandHeight
{
bool IsExpanded { get; set; }
float WhiteSpace { get; set; }
float ButtonWidthOffset { get; set; }
}
}

107
src/Helpers/InputHelper.cs Normal file
View File

@ -0,0 +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.
/// </summary>
public static class InputHelper
{
public static void CheckInput()
{
if (Input == null)
{
MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null)
{
MelonLogger.Log("Ok!");
}
else
{
MelonLogger.Log("Could not load Input module!");
}
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
{
get
{
if (Input == null) return Vector3.zero;
return (Vector3)MousePosInfo.GetValue(null);
}
}
#pragma warning restore IDE1006
public static bool GetKeyDown(KeyCode key)
{
if (Input == null) return false;
return (bool)GetKeyDownInfo.Invoke(null, new object[] { key });
}
public static bool GetKey(KeyCode 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)
{
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)
{
if (Input == null) return false;
return (bool)GetMouseButtonInfo.Invoke(null, new object[] { btn });
}
}
}

View File

@ -1,14 +1,13 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using MelonLoader; using ILType = Il2CppSystem.Type;
namespace Explorer namespace Explorer
{ {
@ -16,11 +15,11 @@ namespace Explorer
{ {
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
public static Il2CppSystem.Type GameObjectType => Il2CppType.Of<GameObject>(); public static ILType GameObjectType => Il2CppType.Of<GameObject>();
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>(); public static ILType TransformType => Il2CppType.Of<Transform>();
public static Il2CppSystem.Type ObjectType => Il2CppType.Of<UnityEngine.Object>(); public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static Il2CppSystem.Type ComponentType => Il2CppType.Of<Component>(); public static ILType ComponentType => Il2CppType.Of<Component>();
public static Il2CppSystem.Type BehaviourType => Il2CppType.Of<Behaviour>(); public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast"); private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
@ -33,6 +32,94 @@ namespace Explorer
.Invoke(obj, null); .Invoke(obj, null);
} }
public static bool IsEnumerable(Type t)
{
return typeof(IEnumerable).IsAssignableFrom(t);
}
// Only Il2Cpp List needs this check. C# List is IEnumerable.
public static bool IsCppList(Type t)
{
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g);
}
else
{
return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
}
}
public static bool IsDictionary(Type t)
{
if (typeof(IDictionary).IsAssignableFrom(t))
{
return true;
}
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{
return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g);
}
else
{
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t);
}
}
public static Type GetTypeByName(string fullName)
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes())
{
if (type.FullName == fullName)
{
return type;
}
}
}
return null;
}
public static Type GetActualType(object obj)
{
if (obj == null) return null;
if (obj is Il2CppSystem.Object ilObject)
{
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
{
return t;
}
return ilObject.GetType();
}
return obj.GetType();
}
public static Type[] GetAllBaseTypes(object obj)
{
var list = new List<Type>();
var type = GetActualType(obj);
list.Add(type);
while (type.BaseType != null)
{
type = type.BaseType;
list.Add(type);
}
return list.ToArray();
}
public static string ExceptionToString(Exception e) public static string ExceptionToString(Exception e)
{ {
if (IsFailedGeneric(e)) if (IsFailedGeneric(e))
@ -73,92 +160,5 @@ namespace Explorer
else else
return false; return false;
} }
public static bool IsEnumerable(Type t)
{
return typeof(System.Collections.IEnumerable).IsAssignableFrom(t);
}
// Only Il2Cpp List needs this check. C# List is IEnumerable.
public static bool IsCppList(Type t)
{
if (t.IsGenericType)
{
var generic = t.GetGenericTypeDefinition();
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
}
else
{
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList));
}
}
public static bool IsDictionary(Type t)
{
return t.IsGenericType
&& t.GetGenericTypeDefinition() is Type typeDef
&& (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>))
|| typeDef.IsAssignableFrom(typeof(Dictionary<,>)));
}
public static Type GetTypeByName(string typeName)
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in GetTypesSafe(asm))
{
if (type.FullName == typeName)
{
return type;
}
}
}
return null;
}
public static Type GetActualType(object obj)
{
if (obj == null) return null;
if (obj is Il2CppSystem.Object ilObject)
{
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
{
return t;
}
return ilObject.GetType();
}
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>();
var type = GetActualType(obj);
list.Add(type);
while (type.BaseType != null)
{
type = type.BaseType;
list.Add(type);
}
return list.ToArray();
}
} }
} }

View File

@ -15,7 +15,7 @@ namespace Explorer
{ {
get get
{ {
if (m_mainCamera == null) if (!m_mainCamera)
{ {
m_mainCamera = Camera.main; m_mainCamera = Camera.main;
} }

View File

@ -17,7 +17,7 @@ namespace Explorer
{ {
if (CppExplorer.ShowMenu) if (CppExplorer.ShowMenu)
{ {
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1)) if (InputHelper.GetKey(KeyCode.LeftShift) && InputHelper.GetMouseButtonDown(1))
{ {
EnableInspect = !EnableInspect; EnableInspect = !EnableInspect;
} }
@ -35,7 +35,10 @@ namespace Explorer
public static void InspectRaycast() public static void InspectRaycast()
{ {
Ray ray = UnityHelpers.MainCamera.ScreenPointToRay(Input.mousePosition); if (!UnityHelpers.MainCamera)
return;
var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputHelper.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f)) if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{ {
@ -43,7 +46,7 @@ namespace Explorer
m_objUnderMouseName = obj.transform.GetGameObjectPath(); m_objUnderMouseName = obj.transform.GetGameObjectPath();
if (Input.GetMouseButtonDown(0)) if (InputHelper.GetMouseButtonDown(0))
{ {
EnableInspect = false; EnableInspect = false;
m_objUnderMouseName = ""; m_objUnderMouseName = "";
@ -63,7 +66,7 @@ namespace Explorer
{ {
if (m_objUnderMouseName != "") if (m_objUnderMouseName != "")
{ {
var pos = Input.mousePosition; var pos = InputHelper.mousePosition;
var rect = new Rect( var rect = new Rect(
pos.x - (Screen.width / 2), // x pos.x - (Screen.width / 2), // x
Screen.height - pos.y - 50, // y Screen.height - pos.y - 50, // y

View File

@ -19,6 +19,8 @@ namespace Explorer
private ScriptEvaluator _evaluator; private ScriptEvaluator _evaluator;
private readonly StringBuilder _sb = new StringBuilder(); private readonly StringBuilder _sb = new StringBuilder();
private Vector2 inputAreaScroll;
private string MethodInput = ""; private string MethodInput = "";
private string UsingInput = ""; private string UsingInput = "";
@ -124,7 +126,12 @@ MelonLogger.Log(""hello world"");";
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.Label("Enter code here as though it is a method body:", null); GUILayout.Label("Enter code here as though it is a method body:", null);
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(250) });
inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) });
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) });
GUIUnstrip.EndScrollView();
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null)) if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
{ {

View File

@ -192,7 +192,7 @@ namespace Explorer
} }
} }
// --------- GUI Draw Function --------- // // --------- GUI Draw Function --------- //
public override void DrawWindow() public override void DrawWindow()
{ {
@ -215,11 +215,9 @@ namespace Explorer
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
catch (Exception e) catch
{ {
MelonLogger.Log("Exception drawing ScenePage! " + e.GetType() + ", " + e.Message); // supress
MelonLogger.Log(e.StackTrace);
m_currentTransform = null;
} }
} }
@ -229,39 +227,7 @@ namespace Explorer
// Current Scene label // Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
try 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;
}
}
}
catch { }
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) }); GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -269,7 +235,9 @@ namespace Explorer
// ----- GameObject Search ----- // ----- GameObject Search -----
GUILayout.BeginHorizontal(GUI.skin.box, null); GUILayout.BeginHorizontal(GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, null); m_searchInput = GUILayout.TextField(m_searchInput, null);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
Search(); Search();
@ -279,6 +247,39 @@ namespace Explorer
GUILayout.Space(5); 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() private void DrawPageButtons()
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);

View File

@ -385,8 +385,8 @@ namespace Explorer
public static IEnumerable<object> GetInstanceClassScanner() public static IEnumerable<object> GetInstanceClassScanner()
{ {
var query = AppDomain.CurrentDomain.GetAssemblies() var query = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(ReflectionHelpers.GetTypesSafe) .SelectMany(t => t.TryGetTypes())
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters); .Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
var flags = BindingFlags.Public | BindingFlags.Static; var flags = BindingFlags.Public | BindingFlags.Static;
var flatFlags = flags | BindingFlags.FlattenHierarchy; var flatFlags = flags | BindingFlags.FlattenHierarchy;

View File

@ -41,8 +41,7 @@ namespace Explorer
} }
} }
private static PropertyInfo m_scrollViewStatesInfo; private static PropertyInfo m_scrollViewStatesInfo;
// ======= public methods ======= //
public static Rect GetLastRect() public static Rect GetLastRect()
{ {
@ -73,7 +72,6 @@ namespace Explorer
catch catch
{ {
ScrollFailed = true; ScrollFailed = true;
return scroll;
} }
} }
@ -86,10 +84,8 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception on GUIUnstrip.BeginScrollView_ImplLayout: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace); MelonLogger.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
ManualUnstripFailed = true; ManualUnstripFailed = true;
return scroll;
} }
} }
@ -113,8 +109,6 @@ namespace Explorer
} }
} }
// ======= private methods ======= //
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{ {

View File

@ -48,12 +48,6 @@ namespace Explorer
public bool GetObjectAsGameObject() public bool GetObjectAsGameObject()
{ {
if (Target == null)
{
MelonLogger.Log("Target is null!");
return false;
}
var targetType = Target.GetType(); var targetType = Target.GetType();
if (targetType == typeof(GameObject)) if (targetType == typeof(GameObject))
@ -108,6 +102,22 @@ namespace Explorer
{ {
try try
{ {
if (Target == null)
{
MelonLogger.Log("Target is null!");
DestroyWindow();
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
MelonLogger.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
if (!m_object && !GetObjectAsGameObject()) if (!m_object && !GetObjectAsGameObject())
{ {
throw new Exception("Object is null!"); throw new Exception("Object is null!");

View File

@ -22,8 +22,6 @@ namespace Explorer
private CacheObjectBase[] m_cachedMembersFiltered; private CacheObjectBase[] m_cachedMembersFiltered;
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
//private int m_pageOffset;
//private int m_limitPerPage = 20;
private bool m_autoUpdate = false; private bool m_autoUpdate = false;
private string m_search = ""; private string m_search = "";
@ -69,6 +67,20 @@ namespace Explorer
public override void Update() public override void Update()
{ {
if (Target == null)
{
DestroyWindow();
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
DestroyWindow();
return;
}
}
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
if (m_autoUpdate) if (m_autoUpdate)

View File

@ -34,15 +34,17 @@ namespace Explorer
//var r = GUILayoutUtility.GetLastRect(); //var r = GUILayoutUtility.GetLastRect();
var r = GUIUnstrip.GetLastRect(); var r = GUIUnstrip.GetLastRect();
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); var mousePos = InputHelper.mousePosition;
if (r.Contains(mouse) && Input.GetMouseButtonDown(0)) Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
{ {
isResizing = true; isResizing = true;
m_currentWindow = ID; m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height); m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
} }
else if (!Input.GetMouseButton(0)) else if (!InputHelper.GetMouseButton(0))
{ {
isResizing = false; isResizing = false;
} }

View File

@ -90,7 +90,7 @@ namespace Explorer
{ {
createdNew = false; createdNew = false;
if (Input.GetKey(KeyCode.LeftShift)) if (InputHelper.GetKey(KeyCode.LeftShift))
{ {
forceReflection = true; forceReflection = true;
} }
@ -192,7 +192,8 @@ namespace Explorer
private static bool RectContainsMouse(Rect rect) private static bool RectContainsMouse(Rect rect)
{ {
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); var mousePos = InputHelper.mousePosition;
return rect.Contains(new Vector2(mousePos.x, Screen.height - mousePos.y));
} }
public static int NextWindowID() public static int NextWindowID()