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.
This commit is contained in:
sinaioutlander 2020-08-13 23:34:21 +10:00
parent 0c3067973e
commit d399b6acd1
25 changed files with 69 additions and 3608 deletions

View File

@ -16,10 +16,15 @@ namespace Explorer
// consts // consts
public const string ID = "com.sinai.cppexplorer"; public const string ID = "com.sinai.cppexplorer";
public const string NAME = "IL2CPP Runtime Explorer";
public const string VERSION = "1.3.2"; public const string VERSION = "1.3.2";
public const string AUTHOR = "Sinai"; 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 // fields
public static CppExplorer Instance; public static CppExplorer Instance;

View File

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

View File

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

View File

@ -70,7 +70,7 @@ namespace Explorer
{ {
foreach (var holder in this.m_FieldInfos) 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); holder.UpdateValue(m_object);
} }
@ -81,7 +81,7 @@ namespace Explorer
{ {
foreach (var holder in this.m_PropertyInfos) 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); holder.UpdateValue(m_object);
} }

View File

@ -56,7 +56,7 @@ namespace Explorer
var origSkin = GUI.skin; var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin; 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; GUI.skin = origSkin;
} }

View File

@ -1,228 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
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.2";
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;
// 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
{
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,120 +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)
{
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)
{
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)
{
try
{
if (fieldInfo.FieldType.IsEnum)
{
if (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);
}
}
else
{
MelonLogger.Log("Unsupported primitive field type: " + fieldInfo.FieldType.FullName);
}
}
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

@ -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,127 +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(this.propInfo.GetAccessors()[0].IsStatic ? null : 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 (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,357 +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 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(true);
}
public override void Update()
{
if (m_autoUpdate)
{
UpdateValues();
}
}
private void UpdateValues(bool forceAll = false)
{
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()))
{
holder.UpdateValue(m_object);
}
}
}
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()))
{
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;
}
// ============ 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
&& 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,399 +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 = 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;
public enum SceneFilter
{
Any,
This,
DontDestroy,
None
}
public enum TypeFilter
{
Object,
GameObject,
Component,
Custom
}
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 > this.m_limit)
{
// prev/next page buttons
GUILayout.BeginHorizontal(null);
int maxOffset = (int)Mathf.Ceil(count / this.m_limit);
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 * this.m_limit;
int preiterated = 0;
if (offset >= count) m_pageOffset = 0;
for (int i = 0; i < m_searchResults.Count; i++)
{
if (offset > 0 && preiterated < offset)
{
preiterated++;
continue;
}
if (i - offset > this.m_limit - 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) ----------------- //
// ======= 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()
{
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>(); }
}
}
}

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,505 +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();
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 (ilType != null && ilType == CppExplorer.GameObjectType || CppExplorer.TransformType.IsAssignableFrom(ilType))
{
GameObject go;
var ilObj = value as Il2CppSystem.Object;
if (ilType == CppExplorer.GameObjectType)
{
go = ilObj.TryCast<GameObject>();
}
else
{
go = ilObj.TryCast<Transform>().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();
}
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) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
}
// 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;
}
}
}