Compare commits

..

5 Commits
1.3.1 ... 1.3.3

Author SHA1 Message Date
b2a90c832f 1.3.3
Fix scene change buttons not actually working properly.
2020-08-14 16:19:39 +10:00
9a784fd467 Update README.md 2020-08-13 23:42:31 +10:00
d399b6acd1 Merging the two projects into one with build configurations
Removed the "src_2018" project, since it's now 1:1 with the main project and the only difference is references. Now using build configurations and preprocessor directives to accomplish the same thing.
2020-08-13 23:34:21 +10:00
0c3067973e 1.3.2 cleanup
- cleanup
- fixed a mistake with FieldInfos on reflection window, causing all values to be null.
- improved displaying of generic objects (now shows object Type after the name)
2020-08-13 18:43:34 +10:00
411593590d 1.3.2
- Fixed the Scene Filters on the search page, it was not functionally correctly at all.
- Fixed GameObjects and Components being displayed as basic objects on search view (they now open to GameObject inspector like they should).
2020-08-13 17:00:53 +10:00
32 changed files with 387 additions and 3946 deletions

View File

@ -4,14 +4,14 @@
An in-game explorer and a suite of debugging tools for [IL2CPP](https://docs.unity3d.com/Manual/IL2CPP.html) Unity games, using [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader).
This was designed to be an IL2CPP-compatible equivalent to [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor).
Most games running on Unity 2017 to 2019 should be supported. If you find that the GUI does not display properly and you get errors in the MelonLoader console about it, then this is likely due to a bug with Il2CppAssemblyUnhollower's unstripping. This bug is known by the developer of the tool and they will fix it as soon as they are able to.
## Features
* Scene hierarchy explorer
* Search loaded assets with filters
* Traverse and manipulate GameObjects
* Generic Reflection inspector
* REPL Console
* C# REPL Console
* Inspect-under-mouse
## How to install
@ -38,7 +38,7 @@ Search feature:
[![](https://i.imgur.com/F9ZfMvz.png)](https://i.imgur.com/F9ZfMvz.png)
REPL console:
C# REPL console:
[![](https://i.imgur.com/14Dbtf8.png)](https://i.imgur.com/14Dbtf8.png)

View File

@ -6,7 +6,9 @@ using System.Text;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using Harmony;
using Il2CppSystem.Runtime.InteropServices;
namespace Explorer
{
@ -15,10 +17,15 @@ namespace Explorer
// consts
public const string ID = "com.sinai.cppexplorer";
public const string NAME = "IL2CPP Runtime Explorer";
public const string VERSION = "1.3.1";
public const string VERSION = "1.3.3";
public const string AUTHOR = "Sinai";
#if Release_Unity2018
public const string NAME = "IL2CPP Runtime Explorer (Unity 2018)";
#else
public const string NAME = "IL2CPP Runtime Explorer";
#endif
// fields
public static CppExplorer Instance;
@ -31,6 +38,13 @@ namespace Explorer
public static int ArrayLimit { get; set; } = 20;
public bool MouseInspect { get; set; } = false;
// prop helpers
public static Il2CppSystem.Type GameObjectType => Il2CppType.Of<GameObject>();
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>();
public static Il2CppSystem.Type ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static Il2CppSystem.Type ComponentType => Il2CppType.Of<Component>();
public static string ActiveSceneName
{
get

View File

@ -8,31 +8,33 @@
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Explorer</RootNamespace>
<AssemblyName>CppExplorer</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AssemblyName>CppExplorer</AssemblyName>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants>
</DefineConstants>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Unity2018|AnyCPU' ">
<AssemblyName>CppExplorer_Unity2018</AssemblyName>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants>Release_Unity2018</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
@ -66,48 +68,70 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerRuntimeLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Unity.TextMeshPro.dll</HintPath>
<!-- Unity 2019 build (InputLegacyModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.InputModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ParticleSystemModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Reference Include="UnityEngine.UIElementsModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UIModule.dll</HintPath>
<!-- 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>
<Reference Include="UnityEngine.UIElementsModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>

View File

@ -8,13 +8,13 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Release_Unity2018|Any CPU = Release_Unity2018|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Release|Any CPU
{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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -22,7 +22,24 @@ namespace Explorer
public override void UpdateValue(object obj)
{
m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj);
try
{
if (obj is Il2CppSystem.Object ilObject)
{
var declaringType = this.fieldInfo.DeclaringType;
var cast = CppExplorer.Il2CppCast(obj, declaringType);
m_value = this.fieldInfo.GetValue(fieldInfo.IsStatic ? null : cast);
}
else
{
m_value = this.fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj);
}
}
catch (Exception e)
{
MelonLogger.Log($"Error updating FieldInfoHolder | {e.GetType()}: {e.Message}\r\n{e.StackTrace}");
}
}
public override void Draw(ReflectionWindow window)
@ -32,51 +49,72 @@ namespace Explorer
public override void SetValue(object obj)
{
if (fieldInfo.FieldType.IsEnum)
try
{
if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null)
if (fieldInfo.FieldType.IsEnum)
{
m_value = enumValue;
}
}
else if (fieldInfo.FieldType.IsPrimitive)
{
if (fieldInfo.FieldType == typeof(float))
{
if (float.TryParse(m_value.ToString(), out float f))
if (Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null)
{
m_value = f;
m_value = enumValue;
}
}
else if (fieldInfo.FieldType.IsPrimitive)
{
if (fieldInfo.FieldType == typeof(float))
{
if (float.TryParse(m_value.ToString(), out float f))
{
m_value = f;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!");
}
}
else if (fieldInfo.FieldType == typeof(double))
{
if (double.TryParse(m_value.ToString(), out double d))
{
m_value = d;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!");
}
}
else if (fieldInfo.FieldType != typeof(bool))
{
if (int.TryParse(m_value.ToString(), out int i))
{
m_value = i;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType);
}
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!");
MelonLogger.Log("Unsupported primitive field type: " + fieldInfo.FieldType.FullName);
}
}
else if (fieldInfo.FieldType == typeof(double))
{
if (double.TryParse(m_value.ToString(), out double d))
{
m_value = d;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!");
}
}
else if (fieldInfo.FieldType != typeof(bool))
{
if (int.TryParse(m_value.ToString(), out int i))
{
m_value = i;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType);
}
}
}
fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value);
if (obj is Il2CppSystem.Object ilObject)
{
var declaringType = this.fieldInfo.DeclaringType;
var cast = CppExplorer.Il2CppCast(obj, declaringType);
fieldInfo.SetValue(fieldInfo.IsStatic ? null : cast, m_value);
}
else
{
fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value);
}
}
catch (Exception e)
{
MelonLogger.Log($"Error setting FieldInfoHolder | {e.GetType()}: {e.Message}\r\n{e.StackTrace}");
}
}
}
}

View File

@ -33,6 +33,7 @@ namespace Explorer
if (obj is Il2CppSystem.Object ilObject)
{
var declaringType = this.propInfo.DeclaringType;
if (declaringType == typeof(Il2CppObjectBase))
{
m_value = ilObject.Pointer;
@ -40,7 +41,7 @@ namespace Explorer
else
{
var cast = CppExplorer.Il2CppCast(obj, declaringType);
m_value = this.propInfo.GetValue(cast, null);
m_value = this.propInfo.GetValue(this.propInfo.GetAccessors()[0].IsStatic ? null : cast, null);
}
}
else
@ -70,7 +71,7 @@ namespace Explorer
{
if (propInfo.PropertyType.IsEnum)
{
if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null)
if (Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null)
{
m_value = enumValue;
}

View File

@ -31,54 +31,6 @@ namespace Explorer
Field
}
public Type GetActualType(object m_object)
{
if (m_object is Il2CppSystem.Object ilObject)
{
var iltype = ilObject.GetIl2CppType();
return Type.GetType(iltype.AssemblyQualifiedName);
}
else
{
return m_object.GetType();
}
}
public Type[] GetAllBaseTypes(object m_object)
{
var list = new List<Type>();
if (m_object is Il2CppSystem.Object ilObject)
{
var ilType = ilObject.GetIl2CppType();
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
{
list.Add(ilTypeToManaged);
while (ilType.BaseType != null)
{
ilType = ilType.BaseType;
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
{
list.Add(ilBaseTypeToManaged);
}
}
}
}
else
{
var type = m_object.GetType();
list.Add(type);
while (type.BaseType != null)
{
type = type.BaseType;
list.Add(type);
}
}
return list.ToArray();
}
public override void Init()
{
m_object = Target;
@ -101,7 +53,7 @@ namespace Explorer
}
catch { }
UpdateValues();
UpdateValues(true);
}
public override void Update()
@ -112,24 +64,24 @@ namespace Explorer
}
}
private void UpdateValues()
private void UpdateValues(bool forceAll = false)
{
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
if (forceAll || m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
{
foreach (var holder in this.m_FieldInfos)
{
if (m_search == "" || holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
if (forceAll || m_search == "" || holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
{
holder.UpdateValue(m_object);
}
}
}
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
if (forceAll || m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
{
foreach (var holder in this.m_PropertyInfos)
{
if (m_search == "" || holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
if (forceAll || m_search == "" || holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
{
holder.UpdateValue(m_object);
}
@ -278,6 +230,56 @@ namespace Explorer
GUI.color = Color.white;
}
// ============ HELPERS ===============
public Type GetActualType(object m_object)
{
if (m_object is Il2CppSystem.Object ilObject)
{
var iltype = ilObject.GetIl2CppType();
return Type.GetType(iltype.AssemblyQualifiedName);
}
else
{
return m_object.GetType();
}
}
public Type[] GetAllBaseTypes(object m_object)
{
var list = new List<Type>();
if (m_object is Il2CppSystem.Object ilObject)
{
var ilType = ilObject.GetIl2CppType();
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
{
list.Add(ilTypeToManaged);
while (ilType.BaseType != null)
{
ilType = ilType.BaseType;
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
{
list.Add(ilBaseTypeToManaged);
}
}
}
}
else
{
var type = m_object.GetType();
list.Add(type);
while (type.BaseType != null)
{
type = type.BaseType;
list.Add(type);
}
}
return list.ToArray();
}
public static bool IsList(Type t)
{
return t.IsGenericType

View File

@ -56,7 +56,7 @@ namespace Explorer
var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin;
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, "IL2CPP Runtime Explorer");
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME);
GUI.skin = origSkin;
}

View File

@ -146,11 +146,11 @@ MelonLogger.Log(""hello world"");";
GUILayout.BeginHorizontal(null);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(110) });
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(50) }))
if (GUILayout.Button("<b><color=lime>Add</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
AddUsing(UsingInput);
}
if (GUILayout.Button("<color=red>Reset</color>", null))
if (GUILayout.Button("<b><color=red>Clear All</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
ResetConsole();
}

View File

@ -21,7 +21,6 @@ namespace Explorer
// gameobject list
private Transform m_currentTransform;
private List<GameObjectCache> m_objectList = new List<GameObjectCache>();
private float m_timeOfLastUpdate = -1f;
// search bar
private bool m_searching = false;
@ -45,14 +44,6 @@ namespace Explorer
public override void Update()
{
if (Time.time - m_timeOfLastUpdate < 0.2f)
{
return;
}
m_timeOfLastUpdate = Time.time;
var start = Time.realtimeSinceStartup;
if (!m_searching)
{
m_objectList = new List<GameObjectCache>();
@ -101,33 +92,39 @@ namespace Explorer
GUILayout.BeginHorizontal(null);
// Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
if (SceneManager.sceneCount > 1)
try
{
int changeWanted = 0;
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
var scenes = SceneManager.GetAllScenes().ToList();
if (scenes.Count > 1)
{
changeWanted = -1;
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
{
changeWanted = 1;
}
if (changeWanted != 0)
{
var scenes = SceneManager.GetAllScenes();
int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
index += changeWanted;
if (index >= scenes.Count - 1)
int changeWanted = 0;
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
{
index = 0;
changeWanted = -1;
}
else if (index > 0)
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
{
index = scenes.Count - 1;
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;
}
m_currentScene = scenes[index].name;
}
}
catch { }
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
@ -169,17 +166,11 @@ namespace Explorer
if (m_objectList.Count > 0)
{
var start = Time.realtimeSinceStartup;
foreach (var obj in m_objectList)
{
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
var diff = Time.realtimeSinceStartup - start;
}
else
{
// if m_currentTransform != null ...
}
}
else // ------ Scene Search results ------

View File

@ -20,8 +20,10 @@ namespace Explorer
private string m_searchInput = "";
private string m_typeInput = "";
private int m_limit = 100;
private int m_limit = 20;
private int m_pageOffset = 0;
private List<object> m_searchResults = new List<object>();
private Vector2 resultsScroll = Vector2.zero;
public SceneFilter SceneMode = SceneFilter.Any;
public TypeFilter TypeMode = TypeFilter.Object;
@ -42,9 +44,6 @@ namespace Explorer
Custom
}
private List<object> m_searchResults = new List<object>();
private Vector2 resultsScroll = Vector2.zero;
public override void Init()
{
Instance = this;
@ -86,11 +85,11 @@ namespace Explorer
int count = m_searchResults.Count;
if (count > CppExplorer.ArrayLimit)
if (count > this.m_limit)
{
// prev/next page buttons
GUILayout.BeginHorizontal(null);
int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit);
int maxOffset = (int)Mathf.Ceil(count / this.m_limit);
if (GUILayout.Button("< Prev", null))
{
if (m_pageOffset > 0) m_pageOffset--;
@ -111,10 +110,10 @@ namespace Explorer
if (m_searchResults.Count > 0)
{
int offset = m_pageOffset * CppExplorer.ArrayLimit;
int offset = m_pageOffset * this.m_limit;
int preiterated = 0;
if (offset >= count) offset = 0;
if (offset >= count) m_pageOffset = 0;
for (int i = 0; i < m_searchResults.Count; i++)
{
@ -124,7 +123,7 @@ namespace Explorer
continue;
}
if (i - offset > CppExplorer.ArrayLimit - 1)
if (i - offset > this.m_limit - 1)
{
break;
}
@ -246,7 +245,117 @@ namespace Explorer
}
// -------------- ACTUAL METHODS (not Gui draw) ----------------- //
// -------------- ACTUAL METHODS (not Gui draw) ----------------- //
// ======= search functions =======
private void Search()
{
m_pageOffset = 0;
m_searchResults = FindAllObjectsOfType(m_searchInput, m_typeInput);
}
private List<object> FindAllObjectsOfType(string _search, string _type)
{
Il2CppSystem.Type searchType = null;
if (TypeMode == TypeFilter.Custom)
{
try
{
var findType = CppExplorer.GetType(_type);
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
}
catch (Exception e)
{
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
}
}
else if (TypeMode == TypeFilter.Object)
{
searchType = CppExplorer.ObjectType;
}
else if (TypeMode == TypeFilter.GameObject)
{
searchType = CppExplorer.GameObjectType;
}
else if (TypeMode == TypeFilter.Component)
{
searchType = CppExplorer.ComponentType;
}
if (!CppExplorer.ObjectType.IsAssignableFrom(searchType))
{
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!");
return new List<object>();
}
var matches = new List<object>();
var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType);
foreach (var obj in allObjectsOfType)
{
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
{
continue;
}
if (searchType == CppExplorer.ComponentType && CppExplorer.TransformType.IsAssignableFrom(obj.GetIl2CppType()))
{
// Transforms shouldn't really be counted as Components, skip them.
// They're more akin to GameObjects.
continue;
}
if (SceneMode != SceneFilter.Any && !FilterScene(obj, this.SceneMode))
{
continue;
}
if (!matches.Contains(obj))
{
matches.Add(obj);
}
}
return matches;
}
public static bool FilterScene(object obj, SceneFilter filter)
{
GameObject go;
if (obj is Il2CppSystem.Object ilObject)
{
go = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Component>().gameObject;
}
else
{
go = (obj as GameObject) ?? (obj as Component).gameObject;
}
if (!go)
{
// object is not on a GameObject, cannot perform scene filter operation.
return false;
}
if (filter == SceneFilter.None)
{
return string.IsNullOrEmpty(go.scene.name);
}
else if (filter == SceneFilter.This)
{
return go.scene.name == CppExplorer.ActiveSceneName;
}
else if (filter == SceneFilter.DontDestroy)
{
return go.scene.name == "DontDestroyOnLoad";
}
return false;
}
// ====== other ========
// credit: ManlyMarco (RuntimeUnityEditor)
public static IEnumerable<object> GetInstanceClassScanner()
@ -286,172 +395,5 @@ namespace Explorer
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
catch { return Enumerable.Empty<Type>(); }
}
// ======= search functions =======
private void Search()
{
m_searchResults = FindAllObjectsOfType(m_searchInput, m_typeInput);
}
private List<object> FindAllObjectsOfType(string _search, string _type)
{
Il2CppSystem.Type type = null;
if (TypeMode == TypeFilter.Custom)
{
try
{
var findType = CppExplorer.GetType(_type);
type = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
}
catch (Exception e)
{
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
}
}
else if (TypeMode == TypeFilter.Object)
{
type = Il2CppType.Of<Object>();
}
else if (TypeMode == TypeFilter.GameObject)
{
type = Il2CppType.Of<GameObject>();
}
else if (TypeMode == TypeFilter.Component)
{
type = Il2CppType.Of<Component>();
}
if (!Il2CppType.Of<Object>().IsAssignableFrom(type))
{
MelonLogger.LogError("Your Class Type must inherit from UnityEngine.Object! Leave blank to default to UnityEngine.Object");
return new List<object>();
}
var matches = new List<object>();
foreach (var obj in Resources.FindObjectsOfTypeAll(type))
{
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
{
continue;
}
if (SceneMode != SceneFilter.Any)
{
if (SceneMode == SceneFilter.None)
{
if (!NoSceneFilter(obj, obj.GetType()))
{
continue;
}
}
else
{
GameObject go;
var objtype = obj.GetType();
if (objtype == typeof(GameObject))
{
go = obj as GameObject;
}
else if (typeof(Component).IsAssignableFrom(objtype))
{
go = (obj as Component).gameObject;
}
else { continue; }
if (!go) { continue; }
if (SceneMode == SceneFilter.This)
{
if (go.scene.name != CppExplorer.ActiveSceneName || go.scene.name == "DontDestroyOnLoad")
{
continue;
}
}
else if (SceneMode == SceneFilter.DontDestroy)
{
if (go.scene.name != "DontDestroyOnLoad")
{
continue;
}
}
}
}
if (!matches.Contains(obj))
{
matches.Add(obj);
}
}
return matches;
}
public static bool ThisSceneFilter(object obj, Type type)
{
if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type))
{
var go = obj as GameObject ?? (obj as Component).gameObject;
if (go != null && go.scene.name == CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad")
{
return true;
}
}
return false;
}
public static bool DontDestroyFilter(object obj, Type type)
{
if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type))
{
var go = obj as GameObject ?? (obj as Component).gameObject;
if (go != null && go.scene.name == "DontDestroyOnLoad")
{
return true;
}
}
return false;
}
public static bool NoSceneFilter(object obj, Type type)
{
if (type == typeof(GameObject))
{
var go = obj as GameObject;
if (go.scene.name != CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad")
{
return true;
}
else
{
return false;
}
}
else if (typeof(Component).IsAssignableFrom(type))
{
var go = (obj as Component).gameObject;
if (go == null || (go.scene.name != CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad"))
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
}
}

View File

@ -225,20 +225,28 @@ namespace Explorer
}
var valueType = value.GetType();
Il2CppSystem.Type ilType = null;
if (value is Il2CppSystem.Object ilObject)
{
ilType = ilObject.GetIl2CppType();
}
if (valueType.IsPrimitive || value.GetType() == typeof(string))
{
DrawPrimitive(ref value, rect, setTarget, setAction);
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
else if (ilType != null && ilType == CppExplorer.GameObjectType || CppExplorer.TransformType.IsAssignableFrom(ilType))
{
GameObject go;
if (value.GetType() == typeof(Transform))
var ilObj = value as Il2CppSystem.Object;
if (ilType == CppExplorer.GameObjectType)
{
go = (value as Transform).gameObject;
go = ilObj.TryCast<GameObject>();
}
else
{
go = (value as GameObject);
go = ilObj.TryCast<Transform>().gameObject;
}
GameobjButton(go, null, false, rect.width - 250);
@ -417,6 +425,20 @@ namespace Explorer
label = col.ToString();
}
string typeLabel;
if (ilType != null)
{
typeLabel = ilType.FullName;
}
else
{
typeLabel = value.GetType().FullName;
}
if (!label.Contains(typeLabel))
{
label += $" ({typeLabel})";
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{
@ -426,25 +448,6 @@ namespace Explorer
}
}
//public static void DrawMember(ref object value, string valueType, string memberName, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
//{
// GUILayout.Label("<color=cyan>" + memberName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
// DrawValue(ref value, rect, valueType, memberName, setTarget, setAction, autoSet);
//}
//public static void DrawValue(ref object value, Rect rect, string nullValueType = null, string memberName = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
//{
// if (value == null)
// {
// GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
// }
// else
// {
// }
//}
// Helper for drawing primitive values (with Apply button)
public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action<object> setAction = null, bool autoSet = false)

View File

@ -1,220 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
using Harmony;
namespace Explorer
{
public class CppExplorer : MelonMod
{
// consts
public const string ID = "com.sinai.cppexplorer";
public const string NAME = "IL2CPP Runtime Explorer (Unity 2018)";
public const string VERSION = "1.3.1";
public const string AUTHOR = "Sinai";
// fields
public static CppExplorer Instance;
private string m_objUnderMouseName = "";
private Camera m_main;
// props
public static bool ShowMenu { get; set; } = false;
public static int ArrayLimit { get; set; } = 20;
public bool MouseInspect { get; set; } = false;
public static string ActiveSceneName
{
get
{
return UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
}
}
public Camera MainCamera
{
get
{
if (m_main == null)
{
m_main = Camera.main;
}
return m_main;
}
}
// methods
public override void OnApplicationStart()
{
base.OnApplicationStart();
Instance = this;
new MainMenu();
new WindowManager();
// done init
ShowMenu = true;
}
// On scene change
public override void OnLevelWasLoaded(int level)
{
if (ScenePage.Instance != null)
{
ScenePage.Instance.OnSceneChange();
SearchPage.Instance.OnSceneChange();
}
}
// Update
public override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.F7))
{
ShowMenu = !ShowMenu;
}
if (ShowMenu)
{
if (!Cursor.visible)
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
MainMenu.Instance.Update();
WindowManager.Instance.Update();
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1))
{
MouseInspect = !MouseInspect;
}
if (MouseInspect)
{
InspectUnderMouse();
}
}
else if (MouseInspect)
{
MouseInspect = false;
}
}
private void InspectUnderMouse()
{
Ray ray = MainCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{
var obj = hit.transform.gameObject;
m_objUnderMouseName = GetGameObjectPath(obj.transform);
if (Input.GetMouseButtonDown(0))
{
MouseInspect = false;
m_objUnderMouseName = "";
WindowManager.InspectObject(obj, out _);
}
}
else
{
m_objUnderMouseName = "";
}
}
public override void OnGUI()
{
base.OnGUI();
MainMenu.Instance.OnGUI();
WindowManager.Instance.OnGUI();
if (MouseInspect)
{
if (m_objUnderMouseName != "")
{
var pos = Input.mousePosition;
var rect = new Rect(
pos.x - (Screen.width / 2), // x
Screen.height - pos.y - 50, // y
Screen.width, // w
50 // h
);
var origAlign = GUI.skin.label.alignment;
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
//shadow text
GUI.Label(rect, $"<color=black>{m_objUnderMouseName}</color>");
//white text
GUI.Label(new Rect(rect.x - 1, rect.y + 1, rect.width, rect.height), m_objUnderMouseName);
GUI.skin.label.alignment = origAlign;
}
}
}
// ************** public helpers **************
public static object Il2CppCast(object obj, Type castTo)
{
var method = typeof(Il2CppObjectBase).GetMethod("TryCast");
var generic = method.MakeGenericMethod(castTo);
return generic.Invoke(obj, null);
}
public static string GetGameObjectPath(Transform _transform)
{
return GetGameObjectPath(_transform, true);
}
public static string GetGameObjectPath(Transform _transform, bool _includeItemName)
{
string text = _includeItemName ? ("/" + _transform.name) : "";
GameObject gameObject = _transform.gameObject;
while (gameObject.transform.parent != null)
{
gameObject = gameObject.transform.parent.gameObject;
text = "/" + gameObject.name + text;
}
return text;
}
public static Type GetType(string _type)
{
try
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
if (asm.GetType(_type) is Type type)
{
return type;
}
}
catch { }
}
return null;
}
catch
{
return null;
}
}
}
}

View File

@ -1,127 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Explorer</RootNamespace>
<AssemblyName>CppExplorer_Unity2018</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Il2Cppmscorlib">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\Il2CppSystem.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="mcs">
<HintPath>..\lib\mcs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MelonLoader.ModHandler">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="UnhollowerBaseLib">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnhollowerRuntimeLib">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerRuntimeLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.InputModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CppExplorer.cs" />
<Compile Include="Inspectors\Reflection\FieldInfoHolder.cs" />
<Compile Include="Inspectors\Reflection\MemberInfoHolder.cs" />
<Compile Include="Inspectors\Reflection\PropertyInfoHolder.cs" />
<Compile Include="Inspectors\UIWindow.cs" />
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
<Compile Include="MainMenu\Pages\Console\REPL.cs" />
<Compile Include="MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="MainMenu\Pages\WindowPage.cs" />
<Compile Include="WindowManager.cs" />
<Compile Include="MainMenu\MainMenu.cs" />
<Compile Include="Inspectors\GameObjectWindow.cs" />
<Compile Include="Inspectors\ReflectionWindow.cs" />
<Compile Include="MainMenu\Pages\ScenePage.cs" />
<Compile Include="MainMenu\Pages\SearchPage.cs" />
<Compile Include="UIStyles.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="utils\AccessTools.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30128.74
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppExplorer", "CppExplorer.csproj", "{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD5C0A5D-03F1-4CC3-8B4D-E10834908C5A}
EndGlobalSection
EndGlobal

View File

@ -1,448 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Explorer
{
public class GameObjectWindow : UIWindow
{
public override string Name { get => "GameObject Inspector"; set => Name = value; }
public GameObject m_object;
// gui element holders
private string m_name;
private string m_scene;
private Vector2 m_transformScroll = Vector2.zero;
private Transform[] m_children;
private Vector2 m_compScroll = Vector2.zero;
//private Component[] m_components;
private float m_translateAmount = 0.3f;
private float m_rotateAmount = 50f;
private float m_scaleAmount = 0.1f;
List<Component> m_cachedDestroyList = new List<Component>();
//private string m_addComponentInput = "";
private string m_setParentInput = "";
public bool GetObjectAsGameObject()
{
if (Target == null)
{
MelonLogger.Log("Target is null!");
return false;
}
var targetType = Target.GetType();
if (targetType == typeof(GameObject))
{
m_object = Target as GameObject;
return true;
}
else if (targetType == typeof(Transform))
{
m_object = (Target as Transform).gameObject;
return true;
}
MelonLogger.Log("Error: Target is null or not a GameObject/Transform!");
DestroyWindow();
return false;
}
public override void Init()
{
if (!GetObjectAsGameObject())
{
return;
}
m_name = m_object.name;
m_scene = m_object.scene == null ? "null" : m_object.scene.name;
//var listComps = new Il2CppSystem.Collections.Generic.List<Component>();
//m_object.GetComponents(listComps);
//m_components = listComps.ToArray();
var list = new List<Transform>();
for (int i = 0; i < m_object.transform.childCount; i++)
{
list.Add(m_object.transform.GetChild(i));
}
m_children = list.ToArray();
}
public override void Update()
{
if (!m_object && !GetObjectAsGameObject())
{
MelonLogger.Log("Object is null! Destroying window...");
DestroyWindow();
}
}
private void InspectGameObject(GameObject obj)
{
var window = WindowManager.InspectObject(obj, out bool created);
if (created)
{
window.m_rect = new Rect(this.m_rect.x, this.m_rect.y, this.m_rect.width, this.m_rect.height);
DestroyWindow();
}
}
private void ReflectObject(Il2CppSystem.Object obj)
{
var window = WindowManager.InspectObject(obj, out bool created);
if (created)
{
if (this.m_rect.x <= (Screen.width - this.m_rect.width - 100))
{
window.m_rect = new Rect(
this.m_rect.x + this.m_rect.width + 20,
this.m_rect.y,
550,
700);
}
else
{
window.m_rect = new Rect(this.m_rect.x + 50, this.m_rect.y + 50, 550, 700);
}
}
}
public override void WindowFunction(int windowID)
{
Header();
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
if (m_scene == CppExplorer.ActiveSceneName)
{
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
{
ScenePage.Instance.SetTransformTarget(m_object);
MainMenu.SetCurrentPage(0);
}
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = CppExplorer.GetGameObjectPath(m_object.transform);
if (m_object.transform.parent != null)
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
InspectGameObject(m_object.transform.parent.gameObject);
}
}
GUILayout.TextArea(pathlabel, null);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUILayout.TextArea(m_name, null);
GUILayout.EndHorizontal();
// --- Horizontal Columns section ---
GUILayout.BeginHorizontal(null);
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
TransformList();
GUILayout.EndVertical();
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
ComponentList();
GUILayout.EndVertical();
GUILayout.EndHorizontal(); // end horiz columns
GameObjectControls();
GUILayout.EndScrollView();
m_rect = WindowManager.ResizeWindow(m_rect, windowID);
GUILayout.EndArea();
}
private void TransformList()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Height(250) });
m_transformScroll = GUILayout.BeginScrollView(m_transformScroll, GUI.skin.scrollView);
GUILayout.Label("<b>Children:</b>", null);
if (m_children != null && m_children.Length > 0)
{
foreach (var obj in m_children.Where(x => x.childCount > 0))
{
if (!obj)
{
GUILayout.Label("null", null);
continue;
}
UIStyles.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
}
foreach (var obj in m_children.Where(x => x.childCount == 0))
{
if (!obj)
{
GUILayout.Label("null", null);
continue;
}
UIStyles.GameobjButton(obj.gameObject, InspectGameObject, false, this.m_rect.width / 2 - 60);
}
}
else
{
GUILayout.Label("<i>None</i>", null);
}
GUILayout.EndScrollView();
GUILayout.EndVertical();
}
private void ComponentList()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Height(250) });
m_compScroll = GUILayout.BeginScrollView(m_compScroll, GUI.skin.scrollView);
GUILayout.Label("<b><size=15>Components</size></b>", null);
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (m_cachedDestroyList.Count > 0)
{
m_cachedDestroyList.Clear();
}
var m_components = new Il2CppSystem.Collections.Generic.List<Component>();
m_object.GetComponentsInternal(Il2CppType.Of<Component>(), false, false, true, false, m_components);
var ilTypeOfTransform = Il2CppType.Of<Transform>();
var ilTypeOfBehaviour = Il2CppType.Of<Behaviour>();
foreach (var component in m_components)
{
var ilType = component.GetIl2CppType();
if (ilType == ilTypeOfTransform)
{
continue;
}
GUILayout.BeginHorizontal(null);
if (ilTypeOfBehaviour.IsAssignableFrom(ilType))
{
BehaviourEnabledBtn(component.TryCast<Behaviour>());
}
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) }))
{
ReflectObject(component);
}
if (GUILayout.Button("<color=red>-</color>", new GUILayoutOption[] { GUILayout.Width(20) }))
{
m_cachedDestroyList.Add(component);
}
GUILayout.EndHorizontal();
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
if (m_cachedDestroyList.Count > 0)
{
for (int i = m_cachedDestroyList.Count - 1; i >= 0; i--)
{
var comp = m_cachedDestroyList[i];
GameObject.Destroy(comp);
}
}
GUILayout.EndScrollView();
//GUILayout.BeginHorizontal(null);
//m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 150) });
//if (GUILayout.Button("Add Component", new GUILayoutOption[] { GUILayout.Width(120) }))
//{
// if (HPExplorer.GetType(m_addComponentInput) is Type type && typeof(Component).IsAssignableFrom(type))
// {
// var comp = m_object.AddComponent(type);
// var list = m_components.ToList();
// list.Add(comp);
// m_components = list.ToArray();
// }
// else
// {
// MelonLogger.LogWarning($"Could not get type '{m_addComponentInput}'. If it's not a typo, try the fully qualified name.");
// }
//}
//GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
private void BehaviourEnabledBtn(Behaviour obj)
{
var _col = GUI.color;
bool _enabled = obj.enabled;
if (_enabled)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.red;
}
// ------ toggle active button ------
_enabled = GUILayout.Toggle(_enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
if (obj.enabled != _enabled)
{
obj.enabled = _enabled;
}
GUI.color = _col;
}
private void GameObjectControls()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(530) });
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", null);
GUILayout.BeginHorizontal(null);
bool m_active = m_object.activeSelf;
m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>",
new GUILayoutOption[] { GUILayout.Width(80) });
if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); }
UIStyles.InstantiateButton(m_object, 100);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("Remove from parent", new GUILayoutOption[] { GUILayout.Width(160) }))
{
m_object.transform.parent = null;
}
m_setParentInput = GUILayout.TextField(m_setParentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width - 280) });
if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
{
if (GameObject.Find(m_setParentInput) is GameObject newparent)
{
m_object.transform.parent = newparent.transform;
}
else
{
MelonLogger.LogWarning($"Could not find gameobject '{m_setParentInput}'");
}
}
GUILayout.EndHorizontal();
GUILayout.BeginVertical(GUI.skin.box, null);
var t = m_object.transform;
TranslateControl(t, TranslateType.Position, ref m_translateAmount, false);
TranslateControl(t, TranslateType.Rotation, ref m_rotateAmount, true);
TranslateControl(t, TranslateType.Scale, ref m_scaleAmount, false);
GUILayout.EndVertical();
if (GUILayout.Button("<color=red><b>Destroy</b></color>", null))
{
GameObject.Destroy(m_object);
DestroyWindow();
return;
}
GUILayout.EndVertical();
}
public enum TranslateType
{
Position,
Rotation,
Scale
}
private void TranslateControl(Transform transform, TranslateType mode, ref float amount, bool multByTime)
{
GUILayout.BeginHorizontal(null);
GUILayout.Label("<color=cyan><b>" + mode + "</b></color>:", new GUILayoutOption[] { GUILayout.Width(65) });
Vector3 vector = Vector3.zero;
switch (mode)
{
case TranslateType.Position: vector = transform.localPosition; break;
case TranslateType.Rotation: vector = transform.localRotation.eulerAngles; break;
case TranslateType.Scale: vector = transform.localScale; break;
}
GUILayout.Label(vector.ToString(), new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref vector.x, amount, multByTime);
GUILayout.Label("<color=cyan>Y:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref vector.y, amount, multByTime);
GUILayout.Label("<color=cyan>Z:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref vector.z, amount, multByTime);
switch (mode)
{
case TranslateType.Position: transform.localPosition = vector; break;
case TranslateType.Rotation: transform.localRotation = Quaternion.Euler(vector); break;
case TranslateType.Scale: transform.localScale = vector; break;
}
GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) });
var input = amount.ToString("F3");
input = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(40) });
if (float.TryParse(input, out float f))
{
amount = f;
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.EndHorizontal();
}
private void PlusMinusFloat(ref float f, float amount, bool multByTime)
{
string s = f.ToString("F3");
s = GUILayout.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(s, out float f2))
{
f = f2;
}
if (GUILayout.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{
f -= multByTime ? amount * Time.deltaTime : amount;
}
if (GUILayout.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{
f += multByTime ? amount * Time.deltaTime : amount;
}
}
}
}

View File

@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnhollowerBaseLib;
namespace Explorer
{
public class FieldInfoHolder : MemberInfoHolder
{
public FieldInfo fieldInfo;
public object m_value;
public FieldInfoHolder(Type _type, FieldInfo _fieldInfo)
{
classType = _type;
fieldInfo = _fieldInfo;
}
public override void UpdateValue(object obj)
{
m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj);
}
public override void Draw(ReflectionWindow window)
{
UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.fieldInfo, window.m_rect, window.m_object, SetValue);
}
public override void SetValue(object obj)
{
if (fieldInfo.FieldType.IsEnum)
{
if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null)
{
m_value = enumValue;
}
}
else if (fieldInfo.FieldType.IsPrimitive)
{
if (fieldInfo.FieldType == typeof(float))
{
if (float.TryParse(m_value.ToString(), out float f))
{
m_value = f;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!");
}
}
else if (fieldInfo.FieldType == typeof(double))
{
if (double.TryParse(m_value.ToString(), out double d))
{
m_value = d;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!");
}
}
else if (fieldInfo.FieldType != typeof(bool))
{
if (int.TryParse(m_value.ToString(), out int i))
{
m_value = i;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType);
}
}
}
fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value);
}
}
}

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Explorer
{
public abstract class MemberInfoHolder
{
public Type classType;
public bool IsExpanded = false;
public int arrayOffset = 0;
public abstract void Draw(ReflectionWindow window);
public abstract void UpdateValue(object obj);
public abstract void SetValue(object obj);
}
}

View File

@ -1,126 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnhollowerBaseLib;
namespace Explorer
{
public class PropertyInfoHolder : MemberInfoHolder
{
public PropertyInfo propInfo;
public object m_value;
public PropertyInfoHolder(Type _type, PropertyInfo _propInfo)
{
classType = _type;
propInfo = _propInfo;
}
public override void Draw(ReflectionWindow window)
{
UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.propInfo, window.m_rect, window.m_object, SetValue);
}
public override void UpdateValue(object obj)
{
try
{
if (obj is Il2CppSystem.Object ilObject)
{
var declaringType = this.propInfo.DeclaringType;
if (declaringType == typeof(Il2CppObjectBase))
{
m_value = ilObject.Pointer;
}
else
{
var cast = CppExplorer.Il2CppCast(obj, declaringType);
m_value = this.propInfo.GetValue(cast, null);
}
}
else
{
m_value = this.propInfo.GetValue(obj, null);
}
}
catch (Exception e)
{
MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name);
MelonLogger.Log(e.GetType() + ", " + e.Message);
var inner = e.InnerException;
while (inner != null)
{
MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message);
inner = inner.InnerException;
}
m_value = null;
}
}
public override void SetValue(object obj)
{
try
{
if (propInfo.PropertyType.IsEnum)
{
if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null)
{
m_value = enumValue;
}
}
else if (propInfo.PropertyType.IsPrimitive)
{
if (propInfo.PropertyType == typeof(float))
{
if (float.TryParse(m_value.ToString(), out float f))
{
m_value = f;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!");
}
}
else if (propInfo.PropertyType == typeof(double))
{
if (double.TryParse(m_value.ToString(), out double d))
{
m_value = d;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!");
}
}
else if (propInfo.PropertyType != typeof(bool))
{
if (int.TryParse(m_value.ToString(), out int i))
{
m_value = i;
}
else
{
MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType);
}
}
}
var declaring = propInfo.DeclaringType;
var cast = CppExplorer.Il2CppCast(obj, declaring);
propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null);
}
catch
{
//MelonLogger.Log("Exception trying to set property " + this.propInfo.Name);
}
}
}
}

View File

@ -1,355 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MelonLoader;
using Mono.CSharp;
using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer
{
public class ReflectionWindow : UIWindow
{
public override string Name { get => "Object Reflection"; set => Name = value; }
public Type m_objectType;
public object m_object;
private List<FieldInfoHolder> m_FieldInfos;
private List<PropertyInfoHolder> m_PropertyInfos;
private bool m_autoUpdate = false;
private string m_search = "";
public MemberFilter m_filter = MemberFilter.Property;
public enum MemberFilter
{
Both,
Property,
Field
}
public Type GetActualType(object m_object)
{
if (m_object is Il2CppSystem.Object ilObject)
{
var iltype = ilObject.GetIl2CppType();
return Type.GetType(iltype.AssemblyQualifiedName);
}
else
{
return m_object.GetType();
}
}
public Type[] GetAllBaseTypes(object m_object)
{
var list = new List<Type>();
if (m_object is Il2CppSystem.Object ilObject)
{
var ilType = ilObject.GetIl2CppType();
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
{
list.Add(ilTypeToManaged);
while (ilType.BaseType != null)
{
ilType = ilType.BaseType;
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
{
list.Add(ilBaseTypeToManaged);
}
}
}
}
else
{
var type = m_object.GetType();
list.Add(type);
while (type.BaseType != null)
{
type = type.BaseType;
list.Add(type);
}
}
return list.ToArray();
}
public override void Init()
{
m_object = Target;
m_FieldInfos = new List<FieldInfoHolder>();
m_PropertyInfos = new List<PropertyInfoHolder>();
var type = GetActualType(m_object);
if (type == null)
{
MelonLogger.Log("could not get underlying type for object. ToString(): " + m_object.ToString());
return;
}
try
{
m_objectType = type;
GetFields(m_object);
GetProperties(m_object);
}
catch { }
UpdateValues();
}
public override void Update()
{
if (m_autoUpdate)
{
UpdateValues();
}
}
private void UpdateValues()
{
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
{
foreach (var holder in this.m_FieldInfos)
{
if (m_search == "" || holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
{
holder.UpdateValue(m_object);
}
}
}
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
{
foreach (var holder in this.m_PropertyInfos)
{
if (m_search == "" || holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
{
holder.UpdateValue(m_object);
}
}
}
}
public override void WindowFunction(int windowID)
{
try
{
Header();
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Type:</b> <color=cyan>" + m_objectType.Name + "</color>", null);
bool unityObj = m_object is UnityEngine.Object;
if (unityObj)
{
GUILayout.Label("Name: " + (m_object as UnityEngine.Object).name, null);
}
GUILayout.EndHorizontal();
if (unityObj)
{
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
UIStyles.InstantiateButton((UnityEngine.Object)m_object);
if (m_object is Component comp && comp.gameObject is GameObject obj)
{
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("GameObject:", null);
if (GUILayout.Button("<color=#00FF00>" + obj.name + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 350) }))
{
WindowManager.InspectObject(obj, out bool _);
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
GUILayout.EndHorizontal();
}
UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
m_search = GUILayout.TextField(m_search, null);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
FilterToggle(MemberFilter.Both, "Both");
FilterToggle(MemberFilter.Property, "Properties");
FilterToggle(MemberFilter.Field, "Fields");
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Values:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) }))
{
UpdateValues();
}
GUI.color = m_autoUpdate ? Color.green : Color.red;
m_autoUpdate = GUILayout.Toggle(m_autoUpdate, "Auto-update?", new GUILayoutOption[] { GUILayout.Width(100) });
GUI.color = Color.white;
GUILayout.EndHorizontal();
GUILayout.Space(10);
scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
GUILayout.Space(10);
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field)
{
UIStyles.HorizontalLine(Color.grey);
GUILayout.Label("<size=18><b><color=gold>Fields</color></b></size>", null);
foreach (var holder in this.m_FieldInfos)
{
if (m_search != "" && !holder.fieldInfo.Name.ToLower().Contains(m_search.ToLower()))
{
continue;
}
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
holder.Draw(this);
GUILayout.EndHorizontal();
}
}
if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property)
{
UIStyles.HorizontalLine(Color.grey);
GUILayout.Label("<size=18><b><color=gold>Properties</color></b></size>", null);
foreach (var holder in this.m_PropertyInfos)
{
if (m_search != "" && !holder.propInfo.Name.ToLower().Contains(m_search.ToLower()))
{
continue;
}
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
holder.Draw(this);
GUILayout.EndHorizontal();
}
}
GUILayout.EndScrollView();
m_rect = WindowManager.ResizeWindow(m_rect, windowID);
GUILayout.EndArea();
}
catch (Exception e)
{
MelonLogger.LogWarning("Exception on window draw. Message: " + e.Message);
DestroyWindow();
return;
}
}
private void FilterToggle(MemberFilter mode, string label)
{
if (m_filter == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
m_filter = mode;
}
GUI.color = Color.white;
}
public static bool IsList(Type t)
{
return t.IsGenericType
&& t.GetGenericTypeDefinition() is Type typeDef
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
}
private void GetProperties(object m_object, List<string> names = null)
{
if (names == null)
{
names = new List<string>();
}
var types = GetAllBaseTypes(m_object);
foreach (var type in types)
{
PropertyInfo[] propInfos = new PropertyInfo[0];
try
{
propInfos = type.GetProperties(At.flags);
}
catch (TypeLoadException)
{
MelonLogger.Log($"Couldn't get Properties for Type '{type.Name}', it may not support Il2Cpp Reflection at the moment.");
}
foreach (var pi in propInfos)
{
// this member causes a crash when inspected, so just skipping it for now.
if (pi.Name == "Il2CppType")
{
continue;
}
if (names.Contains(pi.Name))
{
continue;
}
names.Add(pi.Name);
var piHolder = new PropertyInfoHolder(type, pi);
m_PropertyInfos.Add(piHolder);
}
}
}
private void GetFields(object m_object, List<string> names = null)
{
if (names == null)
{
names = new List<string>();
}
var types = GetAllBaseTypes(m_object);
foreach (var type in types)
{
foreach (var fi in type.GetFields(At.flags))
{
if (names.Contains(fi.Name))
{
continue;
}
names.Add(fi.Name);
var fiHolder = new FieldInfoHolder(type, fi);
m_FieldInfos.Add(fiHolder);
}
}
}
}
}

View File

@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace Explorer
{
public abstract class UIWindow
{
public abstract string Name { get; set; }
public object Target;
public int windowID;
public Rect m_rect = new Rect(0, 0, 550, 700);
public Vector2 scroll = Vector2.zero;
public static UIWindow CreateWindow<T>(object target) where T : UIWindow
{
//var component = (UIWindow)AddToGameObject<T>(Instance.gameObject);
var window = Activator.CreateInstance<T>();
window.Target = target;
window.windowID = WindowManager.NextWindowID();
window.m_rect = WindowManager.GetNewWindowRect();
WindowManager.Windows.Add(window);
window.Init();
return window;
}
public void DestroyWindow()
{
try
{
WindowManager.Windows.Remove(this);
}
catch (Exception e)
{
MelonLogger.Log("Exception removing Window from WindowManager.Windows list!");
MelonLogger.Log($"{e.GetType()} : {e.Message}\r\n{e.StackTrace}");
}
//Destroy(this);
}
public abstract void Init();
public abstract void WindowFunction(int windowID);
public abstract void Update();
public void OnGUI()
{
if (CppExplorer.ShowMenu)
{
var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin;
m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Name);
GUI.skin = origSkin;
}
}
public void Header()
{
GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20));
if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), "<color=red><b>X</b></color>"))
{
DestroyWindow();
return;
}
}
}
}

View File

@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using MelonLoader;
namespace Explorer
{
public class MainMenu
{
public static MainMenu Instance;
public MainMenu()
{
Instance = this;
Pages.Add(new ScenePage());
Pages.Add(new SearchPage());
Pages.Add(new ConsolePage());
foreach (var page in Pages)
{
page.Init();
}
}
public const int MainWindowID = 10;
public static Rect MainRect = new Rect(5, 5, 550, 700);
private static readonly List<WindowPage> Pages = new List<WindowPage>();
private static int m_currentPage = 0;
public static void SetCurrentPage(int index)
{
if (index < 0 || Pages.Count <= index)
{
MelonLogger.Log("cannot set page " + index);
return;
}
m_currentPage = index;
GUI.BringWindowToFront(MainWindowID);
GUI.FocusWindow(MainWindowID);
}
public void Update()
{
Pages[m_currentPage].Update();
}
public void OnGUI()
{
if (CppExplorer.ShowMenu)
{
var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin;
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, "IL2CPP Runtime Explorer");
GUI.skin = origSkin;
}
}
private void MainWindow(int id)
{
GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20));
if (GUI.Button(new Rect(MainRect.width - 90, 2, 80, 20), "Hide (F7)"))
{
CppExplorer.ShowMenu = false;
return;
}
GUILayout.BeginArea(new Rect(5, 25, MainRect.width - 10, MainRect.height - 35), GUI.skin.box);
MainHeader();
var page = Pages[m_currentPage];
page.scroll = GUILayout.BeginScrollView(page.scroll, GUI.skin.scrollView);
page.DrawWindow();
GUILayout.EndScrollView();
MainRect = WindowManager.ResizeWindow(MainRect, MainWindowID);
GUILayout.EndArea();
}
private void MainHeader()
{
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Options:</b>", new GUILayoutOption[] { GUILayout.Width(70) });
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Array Limit:", new GUILayoutOption[] { GUILayout.Width(70) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
var _input = GUILayout.TextField(CppExplorer.ArrayLimit.ToString(), new GUILayoutOption[] { GUILayout.Width(60) });
if (int.TryParse(_input, out int _lim))
{
CppExplorer.ArrayLimit = _lim;
}
CppExplorer.Instance.MouseInspect = GUILayout.Toggle(CppExplorer.Instance.MouseInspect, "Inspect Under Mouse (Shift + RMB)", null);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
for (int i = 0; i < Pages.Count; i++)
{
if (m_currentPage == i)
GUI.color = Color.green;
else
GUI.color = Color.white;
if (GUILayout.Button(Pages[i].Name, null))
{
m_currentPage = i;
}
}
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUI.color = Color.white;
}
}
}

View File

@ -1,131 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Mono.CSharp;
using UnityEngine;
using Attribute = System.Attribute;
using Object = UnityEngine.Object;
namespace Explorer
{
public class REPL : InteractiveBase
{
static REPL()
{
var go = new GameObject("UnityREPL");
GameObject.DontDestroyOnLoad(go);
//go.transform.parent = HPExplorer.Instance.transform;
MB = go.AddComponent<ReplHelper>();
}
[Documentation("MB - A dummy MonoBehaviour for accessing Unity.")]
public static ReplHelper MB { get; }
[Documentation("find<T>() - find a UnityEngine.Object of type T.")]
public static T find<T>() where T : Object
{
return MB.Find<T>();
}
[Documentation("findAll<T>() - find all UnityEngine.Object of type T.")]
public static T[] findAll<T>() where T : Object
{
return MB.FindAll<T>();
}
//[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
//public static object runCoroutine(IEnumerator i)
//{
// return MB.RunCoroutine(i);
//}
//[Documentation("endCoroutine(co) - ends a Unity coroutine.")]
//public static void endCoroutine(Coroutine c)
//{
// MB.EndCoroutine(c);
//}
////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")]
////public static TypeHelper type<T>()
////{
//// return new TypeHelper(typeof(T));
////}
////[Documentation("type(obj) - obtain type info about object obj. Provides some Reflection helpers.")]
////public static TypeHelper type(object instance)
////{
//// return new TypeHelper(instance);
////}
//[Documentation("dir(obj) - lists all available methods and fiels of a given obj.")]
//public static string dir(object instance)
//{
// return type(instance).info();
//}
//[Documentation("dir<T>() - lists all available methods and fields of type T.")]
//public static string dir<T>()
//{
// return type<T>().info();
//}
//[Documentation("findrefs(obj) - find references to the object in currently loaded components.")]
//public static Component[] findrefs(object obj)
//{
// if (obj == null) throw new ArgumentNullException(nameof(obj));
// var results = new List<Component>();
// foreach (var component in Object.FindObjectsOfType<Component>())
// {
// var type = component.GetType();
// var nameBlacklist = new[] { "parent", "parentInternal", "root", "transform", "gameObject" };
// var typeBlacklist = new[] { typeof(bool) };
// foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// .Where(x => x.CanRead && !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.PropertyType)))
// {
// try
// {
// if (Equals(prop.GetValue(component, null), obj))
// {
// results.Add(component);
// goto finish;
// }
// }
// catch { }
// }
// foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// .Where(x => !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.FieldType)))
// {
// try
// {
// if (Equals(field.GetValue(component), obj))
// {
// results.Add(component);
// goto finish;
// }
// }
// catch { }
// }
// finish:;
// }
// return results.ToArray();
//}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
private class DocumentationAttribute : Attribute
{
public DocumentationAttribute(string doc)
{
Docs = doc;
}
public string Docs { get; }
}
}
}

View File

@ -1,34 +0,0 @@
using System.Collections;
//using Il2CppSystem;
using MelonLoader;
using UnityEngine;
using System;
using Object = UnityEngine.Object;
namespace Explorer
{
public class ReplHelper : MonoBehaviour
{
public ReplHelper(IntPtr intPtr) : base(intPtr) { }
public T Find<T>() where T : Object
{
return FindObjectOfType<T>();
}
public T[] FindAll<T>() where T : Object
{
return FindObjectsOfType<T>();
}
//public object RunCoroutine(IEnumerator enumerator)
//{
// return MelonCoroutines.Start(enumerator);
//}
//public void EndCoroutine(Coroutine c)
//{
// StopCoroutine(c);
//}
}
}

View File

@ -1,227 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using System.Reflection;
using Mono.CSharp;
using System.IO;
using MelonLoader;
namespace Explorer
{
public class ConsolePage : WindowPage
{
public override string Name { get => "C# Console"; set => base.Name = value; }
private ScriptEvaluator _evaluator;
private readonly StringBuilder _sb = new StringBuilder();
private string MethodInput = "";
private string UsingInput = "";
public static List<string> UsingDirectives;
private static readonly string[] m_defaultUsing = new string[]
{
"System",
"UnityEngine",
"System.Linq",
"System.Collections",
"System.Collections.Generic",
"System.Reflection",
"MelonLoader"
};
public override void Init()
{
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<ReplHelper>();
try
{
MethodInput = @"// This is a basic C# REPL console.
// Some common using directives are added by default, you can add more below.
// If you want to return some output, MelonLogger.Log() it.
MelonLogger.Log(""hello world"");";
ResetConsole();
foreach (var use in m_defaultUsing)
{
AddUsing(use);
}
}
catch (Exception e)
{
MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}\r\nStack: {e.StackTrace}");
}
}
public void ResetConsole()
{
if (_evaluator != null)
{
_evaluator.Dispose();
}
_evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) };
UsingDirectives = new List<string>();
}
public string AsmToUsing(string asm, bool richtext = false)
{
if (richtext)
{
return $"<color=#569cd6>using</color> {asm};";
}
return $"using {asm};";
}
public void AddUsing(string asm)
{
if (!UsingDirectives.Contains(asm))
{
UsingDirectives.Add(asm);
Evaluate(AsmToUsing(asm));
}
}
public object Evaluate(string str)
{
object ret = VoidType.Value;
_evaluator.Compile(str, out var compiled);
try
{
compiled?.Invoke(ref ret);
}
catch (Exception e)
{
MelonLogger.LogWarning(e.ToString());
}
return ret;
}
public override void DrawWindow()
{
GUILayout.Label("<b><size=15><color=cyan>C# REPL Console</color></size></b>", null);
GUILayout.Label("Method:", null);
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) });
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
{
try
{
MethodInput = MethodInput.Trim();
if (!string.IsNullOrEmpty(MethodInput))
{
var result = Evaluate(MethodInput);
if (result != null && !Equals(result, VoidType.Value))
{
MelonLogger.Log("[Console Output]\r\n" + result.ToString());
}
}
}
catch (Exception e)
{
MelonLogger.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
}
}
GUILayout.Label("<b>Using directives:</b>", null);
foreach (var asm in UsingDirectives)
{
GUILayout.Label(AsmToUsing(asm, true), null);
}
GUILayout.BeginHorizontal(null);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(110) });
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(50) }))
{
AddUsing(UsingInput);
}
if (GUILayout.Button("<color=red>Reset</color>", null))
{
ResetConsole();
}
GUILayout.EndHorizontal();
}
public override void Update() { }
private class VoidType
{
public static readonly VoidType Value = new VoidType();
private VoidType() { }
}
}
internal class ScriptEvaluator : Evaluator, IDisposable
{
private static readonly HashSet<string> StdLib =
new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "mscorlib", "System.Core", "System", "System.Xml" };
private readonly TextWriter _logger;
public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger))
{
_logger = logger;
ImportAppdomainAssemblies(ReferenceAssembly);
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
_logger.Dispose();
}
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
string name = args.LoadedAssembly.GetName().Name;
if (StdLib.Contains(name))
return;
ReferenceAssembly(args.LoadedAssembly);
}
private static CompilerContext BuildContext(TextWriter tw)
{
var reporter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
{
Version = LanguageVersion.Experimental,
GenerateDebugInfo = false,
StdLib = true,
Target = Target.Library,
WarningLevel = 0,
EnhancedWarnings = false
};
return new CompilerContext(settings, reporter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
string name = assembly.GetName().Name;
if (StdLib.Contains(name))
continue;
import(assembly);
}
}
}
}

View File

@ -1,297 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Explorer
{
public class ScenePage : WindowPage
{
public static ScenePage Instance;
public override string Name { get => "Scene Explorer"; set => base.Name = value; }
// ----- Holders for GUI elements ----- //
private string m_currentScene = "";
// gameobject list
private Transform m_currentTransform;
private List<GameObjectCache> m_objectList = new List<GameObjectCache>();
private float m_timeOfLastUpdate = -1f;
// search bar
private bool m_searching = false;
private string m_searchInput = "";
private List<GameObjectCache> m_searchResults = new List<GameObjectCache>();
// ------------ Init and Update ------------ //
public override void Init()
{
Instance = this;
}
public void OnSceneChange()
{
m_currentScene = CppExplorer.ActiveSceneName;
m_currentTransform = null;
CancelSearch();
}
public override void Update()
{
if (Time.time - m_timeOfLastUpdate < 0.2f)
{
return;
}
m_timeOfLastUpdate = Time.time;
var start = Time.realtimeSinceStartup;
if (!m_searching)
{
m_objectList = new List<GameObjectCache>();
if (m_currentTransform)
{
var endAppend = new List<GameObjectCache>();
for (int i = 0; i < m_currentTransform.childCount; i++)
{
var child = m_currentTransform.GetChild(i);
if (child)
{
if (child.childCount > 0)
m_objectList.Add(new GameObjectCache(child.gameObject));
else
endAppend.Add(new GameObjectCache(child.gameObject));
}
}
m_objectList.AddRange(endAppend);
endAppend = null;
}
else
{
var scene = SceneManager.GetSceneByName(m_currentScene);
var rootObjects = scene.GetRootGameObjects();
// add objects with children first
foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0))
{
m_objectList.Add(new GameObjectCache(obj));
}
foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0))
{
m_objectList.Add(new GameObjectCache(obj));
}
}
}
}
// --------- GUI Draw Functions --------- //
public override void DrawWindow()
{
try
{
GUILayout.BeginHorizontal(null);
// Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
if (SceneManager.sceneCount > 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)
{
var scenes = SceneManager.GetAllScenes();
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;
}
}
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
// ----- GameObject Search -----
GUILayout.BeginHorizontal(GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, null);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Search();
}
GUILayout.EndHorizontal();
GUILayout.Space(15);
// ************** GameObject list ***************
// ----- main explorer ------
if (!m_searching)
{
if (m_currentTransform != null)
{
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
TraverseUp();
}
else
{
GUILayout.Label(CppExplorer.GetGameObjectPath(m_currentTransform), null);
}
GUILayout.EndHorizontal();
}
else
{
GUILayout.Label("Scene Root GameObjects:", null);
}
if (m_objectList.Count > 0)
{
var start = Time.realtimeSinceStartup;
foreach (var obj in m_objectList)
{
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
var diff = Time.realtimeSinceStartup - start;
}
else
{
// if m_currentTransform != null ...
}
}
else // ------ Scene Search results ------
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
CancelSearch();
}
GUILayout.Label("Search Results:", null);
if (m_searchResults.Count > 0)
{
foreach (var obj in m_searchResults)
{
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", null);
}
}
}
catch
{
m_currentTransform = null;
}
}
// -------- Actual Methods (not drawing GUI) ---------- //
public void SetTransformTarget(GameObject obj)
{
m_currentTransform = obj.transform;
CancelSearch();
}
public void TraverseUp()
{
if (m_currentTransform.parent != null)
{
m_currentTransform = m_currentTransform.parent;
}
else
{
m_currentTransform = null;
}
}
public void Search()
{
m_searchResults = SearchSceneObjects(m_searchInput);
m_searching = true;
}
public void CancelSearch()
{
m_searching = false;
}
public List<GameObjectCache> SearchSceneObjects(string _search)
{
var matches = new List<GameObjectCache>();
foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>())
{
if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == m_currentScene)
{
matches.Add(new GameObjectCache(obj));
}
}
return matches;
}
public class GameObjectCache
{
public GameObject RefGameObject;
public string Label;
public Color EnabledColor;
public int ChildCount;
public GameObjectCache(GameObject obj)
{
RefGameObject = obj;
ChildCount = obj.transform.childCount;
Label = (ChildCount > 0) ? "[" + obj.transform.childCount + " children] " : "";
Label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
if (enabled)
{
if (childCount > 0)
{
EnabledColor = Color.green;
}
else
{
EnabledColor = UIStyles.LightGreen;
}
}
else
{
EnabledColor = Color.red;
}
}
}
}
}

View File

@ -1,457 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Reflection;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
using UnhollowerRuntimeLib;
using MelonLoader;
using UnhollowerBaseLib;
namespace Explorer
{
public class SearchPage : WindowPage
{
public static SearchPage Instance;
public override string Name { get => "Object Search"; set => base.Name = value; }
private string m_searchInput = "";
private string m_typeInput = "";
private int m_limit = 100;
private int m_pageOffset = 0;
public SceneFilter SceneMode = SceneFilter.Any;
public TypeFilter TypeMode = TypeFilter.Object;
public enum SceneFilter
{
Any,
This,
DontDestroy,
None
}
public enum TypeFilter
{
Object,
GameObject,
Component,
Custom
}
private List<object> m_searchResults = new List<object>();
private Vector2 resultsScroll = Vector2.zero;
public override void Init()
{
Instance = this;
}
public void OnSceneChange()
{
m_searchResults.Clear();
m_pageOffset = 0;
}
public override void Update()
{
}
public override void DrawWindow()
{
try
{
// helpers
GUILayout.BeginHorizontal(GUI.skin.box, null);
GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) });
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
{
m_searchResults = GetInstanceClassScanner().ToList();
m_pageOffset = 0;
}
GUILayout.EndHorizontal();
// search box
SearchBox();
// results
GUILayout.BeginVertical(GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", null);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
int count = m_searchResults.Count;
if (count > CppExplorer.ArrayLimit)
{
// prev/next page buttons
GUILayout.BeginHorizontal(null);
int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit);
if (GUILayout.Button("< Prev", null))
{
if (m_pageOffset > 0) m_pageOffset--;
}
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
if (GUILayout.Button("Next >", null))
{
if (m_pageOffset < maxOffset) m_pageOffset++;
}
GUILayout.EndHorizontal();
}
resultsScroll = GUILayout.BeginScrollView(resultsScroll, GUI.skin.scrollView);
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
if (m_searchResults.Count > 0)
{
int offset = m_pageOffset * CppExplorer.ArrayLimit;
int preiterated = 0;
if (offset >= count) offset = 0;
for (int i = 0; i < m_searchResults.Count; i++)
{
if (offset > 0 && preiterated < offset)
{
preiterated++;
continue;
}
if (i - offset > CppExplorer.ArrayLimit - 1)
{
break;
}
var obj = m_searchResults[i];
bool _ = false;
int __ = 0;
UIStyles.DrawValue(ref obj, ref _, ref __, _temprect);
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", null);
}
GUILayout.EndScrollView();
GUILayout.EndVertical();
}
catch
{
m_searchResults.Clear();
}
}
private void SearchBox()
{
GUILayout.BeginVertical(GUI.skin.box, null);
// ----- GameObject Search -----
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Search</color></b>", null);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.BeginHorizontal(null);
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
var resultinput = m_limit.ToString();
resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
if (int.TryParse(resultinput, out int _i) && _i > 0)
{
m_limit = _i;
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
ClassFilterToggle(TypeFilter.Object, "Object");
ClassFilterToggle(TypeFilter.GameObject, "GameObject");
ClassFilterToggle(TypeFilter.Component, "Component");
ClassFilterToggle(TypeFilter.Custom, "Custom");
GUILayout.EndHorizontal();
if (TypeMode == TypeFilter.Custom)
{
GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
m_typeInput = GUILayout.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100);
SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140);
SceneFilterToggle(SceneFilter.None, "No Scene", 80);
GUILayout.EndHorizontal();
if (GUILayout.Button("<b><color=cyan>Search</color></b>", null))
{
Search();
}
GUILayout.EndVertical();
}
private void ClassFilterToggle(TypeFilter mode, string label)
{
if (TypeMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
TypeMode = mode;
}
GUI.color = Color.white;
}
private void SceneFilterToggle(SceneFilter mode, string label, float width)
{
if (SceneMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width) }))
{
SceneMode = mode;
}
GUI.color = Color.white;
}
// -------------- ACTUAL METHODS (not Gui draw) ----------------- //
// credit: ManlyMarco (RuntimeUnityEditor)
public static IEnumerable<object> GetInstanceClassScanner()
{
var query = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => !x.FullName.StartsWith("Mono"))
.SelectMany(GetTypesSafe)
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
foreach (var type in query)
{
object obj = null;
try
{
obj = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetValue(null, null);
}
catch
{
try
{
obj = type.GetField("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetValue(null);
}
catch
{
}
}
if (obj != null && !obj.ToString().StartsWith("Mono"))
{
yield return obj;
}
}
}
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>(); }
}
// ======= search functions =======
private void Search()
{
m_searchResults = FindAllObjectsOfType(m_searchInput, m_typeInput);
}
private List<object> FindAllObjectsOfType(string _search, string _type)
{
Il2CppSystem.Type type = null;
if (TypeMode == TypeFilter.Custom)
{
try
{
var findType = CppExplorer.GetType(_type);
type = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
}
catch (Exception e)
{
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
}
}
else if (TypeMode == TypeFilter.Object)
{
type = Il2CppType.Of<Object>();
}
else if (TypeMode == TypeFilter.GameObject)
{
type = Il2CppType.Of<GameObject>();
}
else if (TypeMode == TypeFilter.Component)
{
type = Il2CppType.Of<Component>();
}
if (!Il2CppType.Of<Object>().IsAssignableFrom(type))
{
MelonLogger.LogError("Your Class Type must inherit from UnityEngine.Object! Leave blank to default to UnityEngine.Object");
return new List<object>();
}
var matches = new List<object>();
foreach (var obj in Resources.FindObjectsOfTypeAll(type))
{
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
{
continue;
}
if (SceneMode != SceneFilter.Any)
{
if (SceneMode == SceneFilter.None)
{
if (!NoSceneFilter(obj, obj.GetType()))
{
continue;
}
}
else
{
GameObject go;
var objtype = obj.GetType();
if (objtype == typeof(GameObject))
{
go = obj as GameObject;
}
else if (typeof(Component).IsAssignableFrom(objtype))
{
go = (obj as Component).gameObject;
}
else { continue; }
if (!go) { continue; }
if (SceneMode == SceneFilter.This)
{
if (go.scene.name != CppExplorer.ActiveSceneName || go.scene.name == "DontDestroyOnLoad")
{
continue;
}
}
else if (SceneMode == SceneFilter.DontDestroy)
{
if (go.scene.name != "DontDestroyOnLoad")
{
continue;
}
}
}
}
if (!matches.Contains(obj))
{
matches.Add(obj);
}
}
return matches;
}
public static bool ThisSceneFilter(object obj, Type type)
{
if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type))
{
var go = obj as GameObject ?? (obj as Component).gameObject;
if (go != null && go.scene.name == CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad")
{
return true;
}
}
return false;
}
public static bool DontDestroyFilter(object obj, Type type)
{
if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type))
{
var go = obj as GameObject ?? (obj as Component).gameObject;
if (go != null && go.scene.name == "DontDestroyOnLoad")
{
return true;
}
}
return false;
}
public static bool NoSceneFilter(object obj, Type type)
{
if (type == typeof(GameObject))
{
var go = obj as GameObject;
if (go.scene.name != CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad")
{
return true;
}
else
{
return false;
}
}
else if (typeof(Component).IsAssignableFrom(type))
{
var go = (obj as Component).gameObject;
if (go == null || (go.scene.name != CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad"))
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
}
}

View File

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer
{
public abstract class WindowPage
{
public virtual string Name { get; set; }
public Vector2 scroll = Vector2.zero;
public abstract void Init();
public abstract void DrawWindow();
public abstract void Update();
}
}

View File

@ -1,41 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Explorer;
using MelonLoader;
[assembly: MelonInfo(typeof(CppExplorer), CppExplorer.NAME, CppExplorer.VERSION, CppExplorer.AUTHOR)]
[assembly: MelonGame(null, null)]
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle(CppExplorer.NAME)]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(CppExplorer.AUTHOR)]
[assembly: AssemblyProduct(CppExplorer.NAME)]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b21dbde3-5d6f-4726-93ab-cc3cc68bae7d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,502 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Il2CppSystem.Collections;
//using Il2CppSystem.Reflection;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Explorer
{
public class UIStyles
{
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
public static GUISkin WindowSkin
{
get
{
if (_customSkin == null)
{
try
{
_customSkin = CreateWindowSkin();
}
catch
{
_customSkin = GUI.skin;
}
}
return _customSkin;
}
}
public static void HorizontalLine(Color color)
{
var c = GUI.color;
GUI.color = color;
GUILayout.Box(GUIContent.none, HorizontalBar, null);
GUI.color = c;
}
private static GUISkin _customSkin;
public static Texture2D m_nofocusTex;
public static Texture2D m_focusTex;
private static GUIStyle _horizBarStyle;
private static GUIStyle HorizontalBar
{
get
{
if (_horizBarStyle == null)
{
_horizBarStyle = new GUIStyle();
_horizBarStyle.normal.background = Texture2D.whiteTexture;
_horizBarStyle.margin = new RectOffset(0, 0, 4, 4);
_horizBarStyle.fixedHeight = 2;
}
return _horizBarStyle;
}
}
private static GUISkin CreateWindowSkin()
{
var newSkin = Object.Instantiate(GUI.skin);
Object.DontDestroyOnLoad(newSkin);
m_nofocusTex = MakeTex(550, 700, new Color(0.1f, 0.1f, 0.1f, 0.7f));
m_focusTex = MakeTex(550, 700, new Color(0.3f, 0.3f, 0.3f, 1f));
newSkin.window.normal.background = m_nofocusTex;
newSkin.window.onNormal.background = m_focusTex;
newSkin.box.normal.textColor = Color.white;
newSkin.window.normal.textColor = Color.white;
newSkin.button.normal.textColor = Color.white;
newSkin.textField.normal.textColor = Color.white;
newSkin.label.normal.textColor = Color.white;
return newSkin;
}
public static Texture2D MakeTex(int width, int height, Color col)
{
Color[] pix = new Color[width * height];
for (int i = 0; i < pix.Length; ++i)
{
pix[i] = col;
}
Texture2D result = new Texture2D(width, height);
result.SetPixels(pix);
result.Apply();
return result;
}
// *********************************** METHODS FOR DRAWING VALUES IN GUI ************************************
// helper for "Instantiate" button on UnityEngine.Objects
public static void InstantiateButton(Object obj, float width = 100)
{
if (GUILayout.Button("Instantiate", new GUILayoutOption[] { GUILayout.Width(width) }))
{
var newobj = Object.Instantiate(obj);
WindowManager.InspectObject(newobj, out _);
}
}
// helper for drawing a styled button for a GameObject or Transform
public static void GameobjButton(GameObject obj, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
bool children = obj.transform.childCount > 0;
string label = children ? "[" + obj.transform.childCount + " children] " : "";
label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
Color color;
if (enabled)
{
if (childCount > 0)
{
color = Color.green;
}
else
{
color = LightGreen;
}
}
else
{
color = Color.red;
}
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
}
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
if (!obj)
{
GUILayout.Label("<i><color=red>null</color></i>", null);
return;
}
// ------ toggle active button ------
GUILayout.BeginHorizontal(null);
GUI.skin.button.alignment = TextAnchor.UpperLeft;
GUI.color = activeColor;
enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
if (obj.activeSelf != enabled)
{
obj.SetActive(enabled);
}
// ------- actual button ---------
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Height(22), GUILayout.Width(width) }))
{
if (specialInspectMethod != null)
{
specialInspectMethod(obj);
}
else
{
WindowManager.InspectObject(obj, out bool _);
}
}
// ------ small "Inspect" button on the right ------
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUI.color = Color.white;
if (showSmallInspectBtn)
{
if (GUILayout.Button("Inspect", null))
{
WindowManager.InspectObject(obj, out bool _);
}
}
GUILayout.EndHorizontal();
}
public static void DrawMember(ref object value, ref bool isExpanded, ref int arrayOffset, MemberInfo memberInfo, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
{
GUILayout.Label("<color=cyan>" + memberInfo.Name + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
string valueType = "";
bool canWrite = true;
if (memberInfo is FieldInfo fi)
{
valueType = fi.FieldType.Name;
canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
}
else if (memberInfo is PropertyInfo pi)
{
valueType = pi.PropertyType.Name;
canWrite = pi.CanWrite;
}
DrawValue(ref value, ref isExpanded, ref arrayOffset, rect, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet);
}
public static void DrawValue(ref object value, ref bool isExpanded, ref int arrayOffset, Rect rect, string nullValueType = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
{
if (value == null)
{
GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
return;
}
var valueType = value.GetType();
if (valueType.IsPrimitive || value.GetType() == typeof(string))
{
DrawPrimitive(ref value, rect, setTarget, setAction);
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
GameObject go;
if (value.GetType() == typeof(Transform))
{
go = (value as Transform).gameObject;
}
else
{
go = (value as GameObject);
}
GameobjButton(go, null, false, rect.width - 250);
}
else if (valueType.IsEnum)
{
if (setAction != null)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, -1);
setAction.Invoke(setTarget);
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, 1);
setAction.Invoke(setTarget);
}
}
GUILayout.Label(value.ToString(), null);
}
else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
{
System.Collections.IEnumerable enumerable;
if (value is System.Collections.IEnumerable isEnumerable)
{
enumerable = isEnumerable;
}
else
{
var listValueType = value.GetType().GetGenericArguments()[0];
var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType });
var method = listType.GetMethod("ToArray");
enumerable = (System.Collections.IEnumerable)method.Invoke(value, new object[0]);
}
int count = enumerable.Cast<object>().Count();
if (!isExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
isExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
isExpanded = false;
}
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = "<color=yellow>[" + count + "] " + valueType + "</color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 260) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
if (isExpanded)
{
if (count > CppExplorer.ArrayLimit)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(190);
int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit);
GUILayout.Label($"Page {arrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
// prev/next page buttons
if (GUILayout.Button("< Prev", null))
{
if (arrayOffset > 0) arrayOffset--;
}
if (GUILayout.Button("Next >", null))
{
if (arrayOffset < maxOffset) arrayOffset++;
}
}
int offset = arrayOffset * CppExplorer.ArrayLimit;
if (offset >= count) offset = 0;
var enumerator = enumerable.GetEnumerator();
if (enumerator != null)
{
int i = 0;
int preiterated = 0;
while (enumerator.MoveNext())
{
if (offset > 0 && preiterated < offset)
{
preiterated++;
continue;
}
var obj = enumerator.Current;
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(190);
if (i > CppExplorer.ArrayLimit - 1)
{
//GUILayout.Label($"<i><color=red>{count - CppExplorer.ArrayLimit} results omitted, array is too long!</color></i>", null);
break;
}
if (obj == null)
{
GUILayout.Label("<i><color=grey>null</color></i>", null);
}
else
{
var type = obj.GetType();
var lbl = (i + offset) + ": <color=cyan>" + obj.ToString() + "</color>";
if (type.IsPrimitive || typeof(string).IsAssignableFrom(type))
{
GUILayout.Label(lbl, null);
}
else
{
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button(lbl, null))
{
WindowManager.InspectObject(obj, out _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
//var type = obj.GetType();
//DrawMember(ref obj, type.ToString(), i.ToString(), rect, setTarget, setAction, 25, true);
}
i++;
}
}
}
}
else
{
var label = value.ToString();
if (valueType == typeof(Object))
{
label = (value as Object).name;
}
else if (value is Vector4 vec4)
{
label = vec4.ToString();
}
else if (value is Vector3 vec3)
{
label = vec3.ToString();
}
else if (value is Vector2 vec2)
{
label = vec2.ToString();
}
else if (value is Rect rec)
{
label = rec.ToString();
}
else if (value is Matrix4x4 matrix)
{
label = matrix.ToString();
}
else if (value is Color col)
{
label = col.ToString();
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
}
//public static void DrawMember(ref object value, string valueType, string memberName, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
//{
// GUILayout.Label("<color=cyan>" + memberName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
// DrawValue(ref value, rect, valueType, memberName, setTarget, setAction, autoSet);
//}
//public static void DrawValue(ref object value, Rect rect, string nullValueType = null, string memberName = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
//{
// if (value == null)
// {
// GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
// }
// else
// {
// }
//}
// Helper for drawing primitive values (with Apply button)
public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
{
bool allowSet = setAction != null;
if (value.GetType() == typeof(bool))
{
bool b = (bool)value;
var color = "<color=" + (b ? "lime>" : "red>");
if (allowSet)
{
value = GUILayout.Toggle((bool)value, color + value.ToString() + "</color>", null);
if (b != (bool)value)
{
setAction.Invoke(setTarget);
}
}
}
else
{
if (value.ToString().Length > 37)
{
value = GUILayout.TextArea(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) });
}
else
{
value = GUILayout.TextField(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) });
}
if (autoSet || (allowSet && GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) })))
{
setAction.Invoke(setTarget);
}
}
}
// Helper for setting an enum
public static void SetEnum(ref object value, int change)
{
var type = value.GetType();
var names = Enum.GetNames(type).ToList();
int newindex = names.IndexOf(value.ToString()) + change;
if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count))
{
value = Enum.Parse(type, names[newindex]);
}
}
}
}

View File

@ -1,189 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace Explorer
{
public class WindowManager
{
public static WindowManager Instance;
public static List<UIWindow> Windows = new List<UIWindow>();
public static int CurrentWindowID { get; set; } = 500000;
private static Rect m_lastWindowRect;
public WindowManager()
{
Instance = this;
}
public void Update()
{
foreach (var window in Windows)
{
window.Update();
}
}
public void OnGUI()
{
foreach (var window in Windows)
{
window.OnGUI();
}
}
// ========= Public Helpers =========
public static bool IsMouseInWindow
{
get
{
if (!CppExplorer.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
}
private static bool RectContainsMouse(Rect rect)
{
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
}
public static int NextWindowID()
{
return CurrentWindowID++;
}
public static Rect GetNewWindowRect()
{
return GetNewWindowRect(ref m_lastWindowRect);
}
public static Rect GetNewWindowRect(ref Rect lastRect)
{
Rect rect = new Rect(0, 0, 550, 700);
var mainrect = MainMenu.MainRect;
if (mainrect.x <= (Screen.width - mainrect.width - 100))
{
rect = new Rect(mainrect.x + mainrect.width + 20, mainrect.y, rect.width, rect.height);
}
if (lastRect.x == rect.x)
{
rect = new Rect(rect.x + 25, rect.y + 25, rect.width, rect.height);
}
lastRect = rect;
return rect;
}
public static UIWindow InspectObject(object obj, out bool createdNew)
{
createdNew = false;
foreach (var window in Windows)
{
if (obj == window.Target)
{
GUI.BringWindowToFront(window.windowID);
GUI.FocusWindow(window.windowID);
return window;
}
}
createdNew = true;
if (obj is GameObject || obj is Transform)
{
return InspectGameObject(obj as GameObject ?? (obj as Transform).gameObject);
}
else
{
return InspectReflection(obj);
}
}
private static UIWindow InspectGameObject(GameObject obj)
{
var new_window = UIWindow.CreateWindow<GameObjectWindow>(obj);
GUI.FocusWindow(new_window.windowID);
return new_window;
}
public static UIWindow InspectReflection(object obj)
{
var new_window = UIWindow.CreateWindow<ReflectionWindow>(obj);
GUI.FocusWindow(new_window.windowID);
return new_window;
}
// ============= Resize Window Helper ============
// static readonly GUIContent gcDrag = new GUIContent("<->", "drag to resize");
private static readonly GUIContent gcDrag = new GUIContent("<->");
private static bool isResizing = false;
private static Rect m_currentResize;
private static int m_currentWindow;
public static Rect ResizeWindow(Rect _rect, int ID)
{
try
{
GUILayout.BeginHorizontal(null);
GUILayout.Space(_rect.width - 35);
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Width(25), GUILayout.Height(25) });
var r = GUILayoutUtility.GetLastRect();
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
if (r.Contains(mouse) && Input.GetMouseButtonDown(0))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!Input.GetMouseButton(0))
{
isResizing = false;
}
if (isResizing && ID == m_currentWindow)
{
_rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x));
_rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y));
_rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
}
GUILayout.EndHorizontal();
}
catch { }
return _rect;
}
}
}

View File

@ -1,65 +0,0 @@
using System;
using System.Reflection;
namespace Explorer
{
/// <summary>
/// AccessTools
/// Some helpers for Reflection (GetValue, SetValue, Call, InheritBaseValues)
/// </summary>
public static class At
{
public static Il2CppSystem.Reflection.BindingFlags ilFlags = Il2CppSystem.Reflection.BindingFlags.Public
| Il2CppSystem.Reflection.BindingFlags.NonPublic
| Il2CppSystem.Reflection.BindingFlags.Instance
| Il2CppSystem.Reflection.BindingFlags.Static;
public static BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static;
//reflection call
public static object Call(object obj, string method, params object[] args)
{
var methodInfo = obj.GetType().GetMethod(method, flags);
if (methodInfo != null)
{
return methodInfo.Invoke(obj, args);
}
return null;
}
// set value
public static void SetValue<T>(T value, Type type, object obj, string field)
{
FieldInfo fieldInfo = type.GetField(field, flags);
if (fieldInfo != null)
{
fieldInfo.SetValue(obj, value);
}
}
// get value
public static object GetValue(Type type, object obj, string value)
{
FieldInfo fieldInfo = type.GetField(value, flags);
if (fieldInfo != null)
{
return fieldInfo.GetValue(obj);
}
else
{
return null;
}
}
// inherit base values
public static void InheritBaseValues(object _derived, object _base)
{
foreach (FieldInfo fi in _base.GetType().GetFields(flags))
{
try { _derived.GetType().GetField(fi.Name).SetValue(_derived, fi.GetValue(_base)); } catch { }
}
return;
}
}
}