Compare commits

...

10 Commits
1.6.2 ... 1.6.5

Author SHA1 Message Date
0769b7ef23 1.6.5
* Add expander to Unity struct inspectors, collapsed by default
* `UnityEngine.Color` labels in Reflection Inspector are now the same color as the value for convenience
* Cleaned up InputHelper
2020-09-08 23:47:17 +10:00
5086dcc82b Update README.md 2020-09-08 20:21:34 +10:00
56abd38e92 Add build instructions to Readme 2020-09-08 20:18:37 +10:00
a7e6ae87df Update README.md 2020-09-08 19:47:07 +10:00
b5c584bb02 Update README.md 2020-09-08 19:46:54 +10:00
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
21 changed files with 463 additions and 348 deletions

View File

@ -16,16 +16,9 @@
### Known issues ### Known issues
* 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.
* Reflection may fail with certain types (eg. `Nullable<T>`, some Dictionary types, etc). Please see [Il2CppAssemblyUnhollower](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details.
* 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 +32,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 +62,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.
@ -97,6 +92,18 @@ CppExplorer can force the mouse to be visible and unlocked when the menu is open
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl) * For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
* You can create your own plugin using one of the two plugins above as an example. Usually only a few simple Harmony patches are needed to fix the problem. * You can create your own plugin using one of the two plugins above as an example. Usually only a few simple Harmony patches are needed to fix the problem.
## Building
If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc.
1. Install MelonLoader for your game.
2. Download this repository and open the `CppExplorer.sln` file in the `src` folder.
3. In Visual Studio, right-click the CppExplorer C# Project in the solution browser and click "Unload Project".
4. Right-click the "CppExplorer (unloaded)" and select "Edit Project File".
5. Scroll down until you see the `<ItemGroup>` containing the References.
6. Fix all of the paths in the `..\Steam\` directory for your game (use the full path if you need to).
7. Reload the project and everything should be working, you can now build and run it.
## Credits ## Credits
Written by Sinai. Written by Sinai.

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

View File

@ -56,10 +56,6 @@ namespace Explorer
{ {
// 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);
@ -68,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]);
@ -97,6 +81,22 @@ namespace Explorer
return dict; return dict;
} }
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 (this.MemInfo != null) if (this.MemInfo != null)
@ -172,80 +172,22 @@ namespace Explorer
{ {
try try
{ {
//var ilTypes = new List<Il2CppSystem.Type>(); return Check(TypeOfKeys) && Check(TypeOfValues);
var monoTypes = new Type[] { TypeOfKeys, TypeOfValues };
foreach (var type in monoTypes) bool Check(Type type)
{ {
var generic = typeof(Il2CppClassPointerStore<>).MakeGenericType(type); var ptr = (IntPtr)typeof(Il2CppClassPointerStore<>)
if (generic == null) return false; .MakeGenericType(type)
.GetField("NativeClassPtr")
.GetValue(null);
var genericPtr = (IntPtr)generic.GetField("NativeClassPtr").GetValue(null); return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
if (genericPtr == null) return false;
var classPtr = IL2CPP.il2cpp_class_get_type(genericPtr);
if (classPtr == null) return false;
var internalType = Il2CppSystem.Type.internal_from_handle(classPtr);
if (internalType == null) return false;
//ilTypes.Add(internalType);
} }
} }
catch catch
{ {
return false; return false;
} }
// Should be fine if we got this far, but I'll leave the rest below commented out just in case.
return true;
//MelonLogger.Log("Got both generic types, continuing...");
//var dictIlClass = IL2CPP.GetIl2CppClass("mscorlib.dll", "System.Collections.Generic", "Dictionary`2");
//if (dictIlClass == null) return;
//MelonLogger.Log("Got base dictionary Il2Cpp type");
//var ilClassFromType = IL2CPP.il2cpp_class_get_type(dictIlClass);
//if (ilClassFromType == null) return;
//MelonLogger.Log("got IntPtr from base dictionary type");
//var internalHandle = Il2CppSystem.Type.internal_from_handle(ilClassFromType);
//if (internalHandle == null) return;
//var generic = internalHandle.MakeGenericType(new Il2CppReferenceArray<Il2CppSystem.Type>(new Il2CppSystem.Type[]
//{
// ilTypes[0], ilTypes[1]
//}));
//if (generic == null) return;
//MelonLogger.Log("Made generic handle for our entry types");
//var nativeClassPtr = generic.TypeHandle.value;
//if (nativeClassPtr == null) return;
//MelonLogger.Log("Got the actual nativeClassPtr for the handle");
//var dictType = typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).MakeGenericType(TypeOfKeys, TypeOfValues);
//if (dictType == null) return;
//MelonLogger.Log("Made the generic type for the dictionary");
//var pointerStoreType = typeof(Il2CppClassPointerStore<>).MakeGenericType(dictType);
//if (pointerStoreType == null) return;
//MelonLogger.Log("Made the generic PointerStoreType for our dict");
//var ptrToSet = IL2CPP.il2cpp_class_from_type(nativeClassPtr);
//if (ptrToSet == null) return;
//MelonLogger.Log("Got class from nativeClassPtr, setting value...");
//pointerStoreType.GetField("NativeClassPtr").SetValue(null, ptrToSet);
//MelonLogger.Log("Ok");
} }
// ============= GUI Draw ============= // ============= GUI Draw =============

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

@ -9,6 +9,8 @@ namespace Explorer
{ {
public class CacheColor : CacheObjectBase public class CacheColor : CacheObjectBase
{ {
private bool IsExpanded;
private string r = "0"; private string r = "0";
private string g = "0"; private string g = "0";
private string b = "0"; private string b = "0";
@ -28,9 +30,30 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
GUILayout.Label($"<color=yellow>Color</color>: {((Color)Value).ToString()}", null);
if (CanWrite) if (CanWrite)
{
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
}
var c = (Color)Value;
GUI.color = c;
GUILayout.Label($"<color=yellow>Color:</color> {c.ToString()}", null);
GUI.color = Color.white;
if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90; var whitespace = window.width - width - 90;

View File

@ -9,6 +9,8 @@ namespace Explorer
{ {
public class CacheQuaternion : CacheObjectBase public class CacheQuaternion : CacheObjectBase
{ {
private bool IsExpanded;
private string x = "0"; private string x = "0";
private string y = "0"; private string y = "0";
private string z = "0"; private string z = "0";
@ -26,9 +28,27 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (CanWrite)
{
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
}
GUILayout.Label($"<color=yellow>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null); GUILayout.Label($"<color=yellow>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null);
if (CanWrite) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90; var whitespace = window.width - width - 90;

View File

@ -9,6 +9,8 @@ namespace Explorer
{ {
public class CacheRect : CacheObjectBase public class CacheRect : CacheObjectBase
{ {
private bool IsExpanded;
private string x = "0"; private string x = "0";
private string y = "0"; private string y = "0";
private string w = "0"; private string w = "0";
@ -28,9 +30,27 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (CanWrite)
{
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
}
GUILayout.Label($"<color=yellow>Rect</color>: {((Rect)Value).ToString()}", null); GUILayout.Label($"<color=yellow>Rect</color>: {((Rect)Value).ToString()}", null);
if (CanWrite) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90; var whitespace = window.width - width - 90;

View File

@ -10,6 +10,8 @@ namespace Explorer
{ {
public class CacheVector : CacheObjectBase public class CacheVector : CacheObjectBase
{ {
private bool IsExpanded;
public int VectorSize = 2; public int VectorSize = 2;
private string x = "0"; private string x = "0";
@ -63,9 +65,27 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (CanWrite)
{
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
}
GUILayout.Label($"<color=yellow>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null); GUILayout.Label($"<color=yellow>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null);
if (CanWrite) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90; var whitespace = window.width - width - 90;

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.2"; public const string VERSION = "1.6.5";
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.Init();
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

@ -12,32 +12,19 @@
<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>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- Replace the '..\Steam\..` references with ones from your game (make sure to use the 'MelonLoader\' folder) -->
<Reference Include="Il2Cppmscorlib"> <Reference Include="Il2Cppmscorlib">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private> <Private>False</Private>
@ -60,64 +47,30 @@
<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) --> <Reference Include="UnityEngine">
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
<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="Helpers\IExpandHeight.cs" />
@ -134,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 = Debug|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 = Debug|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>();
}
}
} }
} }

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

@ -0,0 +1,120 @@
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
{
// If Input module failed to load at all
public static bool NO_INPUT;
// Base UnityEngine.Input class
private static Type Input => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
private static Type _input;
// Cached member infos
private static PropertyInfo _mousePosition;
private static MethodInfo _getKey;
private static MethodInfo _getKeyDown;
private static MethodInfo _getMouseButton;
private static MethodInfo _getMouseButtonDown;
public static void Init()
{
if (Input == null && !TryManuallyLoadInput())
{
NO_INPUT = true;
return;
}
// Cache reflection now that we know Input is loaded
_mousePosition = Input.GetProperty("mousePosition");
_getKey = Input.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
_getKeyDown = Input.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
_getMouseButton = Input.GetMethod("GetMouseButton", new Type[] { typeof(int) });
_getMouseButtonDown = Input.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
}
#pragma warning disable IDE1006 // Camel-case property (Unity style)
public static Vector3 mousePosition
{
get
{
if (NO_INPUT) return Vector3.zero;
return (Vector3)_mousePosition.GetValue(null);
}
}
#pragma warning restore IDE1006
public static bool GetKeyDown(KeyCode key)
{
if (NO_INPUT) return false;
return (bool)_getKeyDown.Invoke(null, new object[] { key });
}
public static bool GetKey(KeyCode key)
{
if (NO_INPUT) return false;
return (bool)_getKey.Invoke(null, new object[] { key });
}
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButtonDown(int btn)
{
if (NO_INPUT) return false;
return (bool)_getMouseButtonDown.Invoke(null, new object[] { btn });
}
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButton(int btn)
{
if (NO_INPUT) return false;
return (bool)_getMouseButton.Invoke(null, new object[] { btn });
}
private static bool TryManuallyLoadInput()
{
MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null)
{
MelonLogger.Log("Ok!");
return true;
}
else
{
MelonLogger.Log("Could not load Input module!");
return false;
}
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;
}
}
}
}
}

View File

@ -1,16 +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;
using System.Collections;
using Mono.CSharp;
namespace Explorer namespace Explorer
{ {
@ -18,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");
@ -35,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))
@ -75,100 +160,5 @@ namespace Explorer
else else
return false; return false;
} }
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 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

@ -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

@ -42,7 +42,6 @@ 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

@ -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()