Compare commits

..

20 Commits
v1.0.0 ... 1.2

Author SHA1 Message Date
c5d889f9c7 Update README.md 2020-08-10 19:28:10 +10:00
ec39c68ffa Update README.md 2020-08-09 21:50:55 +10:00
0b78b4398f Merge branch 'master' of https://github.com/sinaioutlander/CppExplorer 2020-08-08 22:45:55 +10:00
ab08d9dc96 1.1
- fixed list and array support
- fixed displaying of certain Unity struct types such as VectorX, color, etc
- improved performance
2020-08-08 22:45:06 +10:00
ca172189be Update README.md 2020-08-08 22:42:39 +10:00
308966e664 Update README.md 2020-08-08 05:23:03 +10:00
0a08dbc495 Update README.md 2020-08-08 05:16:55 +10:00
13d54477a3 Update README.md 2020-08-08 03:58:12 +10:00
359f6e6f5c Update README.md 2020-08-08 03:50:51 +10:00
4f000c5494 Update README.md 2020-08-08 03:48:35 +10:00
e7866f56fe Update README.md 2020-08-08 03:33:40 +10:00
ab2770327e Update README.md 2020-08-08 03:19:00 +10:00
f568be2d3b Update README.md 2020-08-08 01:54:33 +10:00
3c374f3903 Update README.md 2020-08-08 01:42:04 +10:00
d5e22e8156 Update README.md 2020-08-08 01:40:24 +10:00
8bece453e3 1.0.0 finalize 2020-08-08 00:18:57 +10:00
692a721840 Update README.md 2020-08-08 00:02:32 +10:00
ef73cce92f Update README.md 2020-08-08 00:02:01 +10:00
2030b5309d move mcs.dll reference into lib folder 2020-08-07 23:58:43 +10:00
142a75bbbb Update README.md 2020-08-07 23:57:08 +10:00
7 changed files with 156 additions and 113 deletions

View File

@ -2,48 +2,50 @@
[![Version](https://img.shields.io/badge/MelonLoader-0.2.6-green.svg)]() [![Version](https://img.shields.io/badge/MelonLoader-0.2.6-green.svg)]()
Universal Runtime Inspector/Explorer for Unity IL2CPP games. An in-game explorer and a suite of debugging tools for [IL2CPP](https://docs.unity3d.com/Manual/IL2CPP.html) Unity games, using [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader).
This was designed to be an IL2CPP-compatible equivalent to [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor).
## Features ## Features
* Scene exploration (traverse in the same way as the Unity Editor) * Scene hierarchy explorer
* Inspect GameObjects/Transforms and manipulate them * Search loaded assets with filters
* Inspect any object with Reflection, set primitive values, etc * Traverse and manipulate GameObjects
* REPL Console for executing on-the-fly code * Generic Reflection inspector
* REPL Console
* Inspect-under-mouse
## How to install
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
1. Download <b>CppExplorer.zip</b> from [Releases](https://github.com/sinaioutlander/CppExplorer/releases).
2. Unzip the file into the `Mods` folder in your game's installation directory, created by MelonLoader.
3. Make sure it's not in a sub-folder, `CppExplorer.dll` and `mcs.dll` should be directly in the `Mods\` folder.
## How to use
* Press F7 to show or hide the menu.
* Simply browse through the scene, search for objects, etc, it's pretty self-explanatory.
## Images
Scene explorer, and inspection of a MonoBehaviour object:
[![](https://i.imgur.com/Yxizwcz.png)](https://i.imgur.com/Yxizwcz.png)
Search feature:
[![](https://i.imgur.com/F9ZfMvz.png)](https://i.imgur.com/F9ZfMvz.png)
REPL console:
[![](https://i.imgur.com/14Dbtf8.png)](https://i.imgur.com/14Dbtf8.png)
## Credits ## Credits
Written by Sinai. Written by Sinai.
Credits to ManlyMarco for his [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for the REPL Console and the "Find instances" snippet, and used the same MCS that he uses*. Thanks to:
* [ManlyMarco](https://github.com/ManlyMarco) for their [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for the REPL Console and the "Find instances" snippet, and the UI style.
<i>* note: I commented out the `SkipVisibilityExt` constructor since it was causing an exception for some reason.</i> * [denikson](https://github.com/denikson) for [mcs-unity](https://github.com/denikson/mcs-unity). I commented out the `SkipVisibilityExt` constructor in `mcs.dll` since it was causing an exception with the Hook it attempted.
## How to install
This requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
1. Download <b>CppExplorer.dll</b> from the Releases folder.
2. Put the file in your `MyGame/Mods/` folder.
## How to use
* Press F7 to show or hide the menu.
* Currently does <b>not</b> grant locked mouse or prevent clicking-through the menu, be careful of this.
* Simply browse through the scene, search for objects, etc, it's pretty self-explanatory.
If you have any specific questions about it you can contact me here, on NexusMods (Sinaioutlander), or on Discord (Sinai#4637, in MelonLoader discord).
## Images
Scene explorer, and inspection of a MonoBehaviour object.
[![](https://i.imgur.com/Yxizwcz.png)]()
Advanced search feature.
[![](https://i.imgur.com/F9ZfMvz.png)]()
REPL console.
[![](https://i.imgur.com/14Dbtf8.png)]()

BIN
lib/mcs.dll Normal file

Binary file not shown.

View File

@ -4,15 +4,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using System.IO;
using System.Reflection;
using MelonLoader; using MelonLoader;
using Harmony;
using UnhollowerBaseLib.Runtime;
using UnhollowerRuntimeLib;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using System.Runtime.CompilerServices;
using UnhollowerBaseLib.Attributes;
namespace Explorer namespace Explorer
{ {
@ -20,9 +13,9 @@ namespace Explorer
{ {
// consts // consts
public const string ID = "com.sinai.explorer"; public const string ID = "com.sinai.cppexplorer";
public const string NAME = "IL2CPP Runtime Explorer"; public const string NAME = "IL2CPP Runtime Explorer";
public const string VERSION = "0.91"; public const string VERSION = "1.1.0";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
// fields // fields
@ -65,32 +58,16 @@ namespace Explorer
Instance = this; Instance = this;
LoadMCS();
new MainMenu(); new MainMenu();
new WindowManager(); new WindowManager();
var harmony = HarmonyInstance.Create(ID); //var harmony = HarmonyInstance.Create(ID);
harmony.PatchAll(); //harmony.PatchAll();
// done init // done init
ShowMenu = true; ShowMenu = true;
} }
private void LoadMCS()
{
var mcsPath = @"Mods\mcs.dll";
if (File.Exists(mcsPath))
{
Assembly.Load(File.ReadAllBytes(mcsPath));
MelonLogger.Log("Loaded mcs.dll");
}
else
{
MelonLogger.LogError("Could not find mcs.dll!");
}
}
public override void OnLevelWasLoaded(int level) public override void OnLevelWasLoaded(int level)
{ {
if (ScenePage.Instance != null) if (ScenePage.Instance != null)

View File

@ -44,10 +44,9 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="mcs, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="mcs">
<SpecificVersion>False</SpecificVersion> <HintPath>..\lib\mcs.dll</HintPath>
<HintPath>..\Release\mcs.dll</HintPath> <Private>True</Private>
<Private>False</Private>
</Reference> </Reference>
<Reference Include="MelonLoader.ModHandler"> <Reference Include="MelonLoader.ModHandler">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\MelonLoader.ModHandler.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\MelonLoader.ModHandler.dll</HintPath>

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using MelonLoader; using MelonLoader;
using Mono.CSharp;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
@ -275,10 +276,11 @@ namespace Explorer
public static bool IsList(Type t) public static bool IsList(Type t)
{ {
return t.IsGenericType && t.GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>)); 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) private void GetProperties(object m_object, List<string> names = null)
{ {
if (names == null) if (names == null)
@ -292,6 +294,11 @@ namespace Explorer
{ {
foreach (var pi in type.GetProperties(At.flags)) foreach (var pi in type.GetProperties(At.flags))
{ {
if (pi.Name == "Il2CppType")
{
continue;
}
if (names.Contains(pi.Name)) if (names.Contains(pi.Name))
{ {
continue; continue;
@ -382,15 +389,15 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); //MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name);
MelonLogger.Log(e.GetType() + ", " + e.Message); //MelonLogger.Log(e.GetType() + ", " + e.Message);
var inner = e.InnerException; //var inner = e.InnerException;
while (inner != null) //while (inner != null)
{ //{
MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); // MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message);
inner = inner.InnerException; // inner = inner.InnerException;
} //}
m_value = null; m_value = null;
} }
@ -402,7 +409,7 @@ namespace Explorer
{ {
if (propInfo.PropertyType.IsEnum) if (propInfo.PropertyType.IsEnum)
{ {
if (Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null) if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null)
{ {
m_value = enumValue; m_value = enumValue;
} }
@ -451,7 +458,7 @@ namespace Explorer
} }
catch catch
{ {
MelonLogger.Log("Exception trying to set property " + this.propInfo.Name); //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name);
} }
} }
} }
@ -496,7 +503,7 @@ namespace Explorer
{ {
if (fieldInfo.FieldType.IsEnum) if (fieldInfo.FieldType.IsEnum)
{ {
if (Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null) if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null)
{ {
m_value = enumValue; m_value = enumValue;
} }

View File

@ -10,12 +10,12 @@ using MelonLoader;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("CppExplorer")] [assembly: AssemblyTitle(CppExplorer.NAME)]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany(CppExplorer.AUTHOR)]
[assembly: AssemblyProduct("CppExplorer")] [assembly: AssemblyProduct(CppExplorer.NAME)]
[assembly: AssemblyCopyright("By Sinai")] [assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]

View File

@ -2,7 +2,12 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using Il2CppSystem.Collections;
using Il2CppSystem.Reflection;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -234,39 +239,49 @@ namespace Explorer
} }
GUILayout.Label(value.ToString(), null); GUILayout.Label(value.ToString(), null);
} }
else if (valueType.IsArray || ReflectionWindow.IsList(valueType)) else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
{ {
object[] m_array; System.Collections.IEnumerable enumerable;
if (valueType.IsArray)
if (value is System.Collections.IEnumerable isEnumerable)
{ {
m_array = (value as Array).Cast<object>().ToArray(); enumerable = isEnumerable;
} }
else else
{ {
m_array = (value as IEnumerable).Cast<object>().ToArray(); 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();
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>[" + m_array.Length + "] " + valueType + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) })) string btnLabel = "<color=yellow>[" + count + "] " + valueType + "</color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{ {
WindowManager.InspectObject(value, out bool _); WindowManager.InspectObject(value, out bool _);
} }
GUI.skin.button.alignment = TextAnchor.MiddleCenter; GUI.skin.button.alignment = TextAnchor.MiddleCenter;
for (int i = 0; i < m_array.Length; i++) var enumerator = enumerable.GetEnumerator();
if (enumerator != null)
{ {
var obj = m_array[i]; int i = 0;
while (enumerator.MoveNext())
{
var obj = enumerator.Current;
// collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(190); GUILayout.Space(190);
if (i > CppExplorer.ArrayLimit) if (i > CppExplorer.ArrayLimit)
{ {
GUILayout.Label($"<i><color=red>{m_array.Length - CppExplorer.ArrayLimit} results omitted, array is too long!</color></i>", null); GUILayout.Label($"<i><color=red>{count - CppExplorer.ArrayLimit} results omitted, array is too long!</color></i>", null);
break; break;
} }
@ -277,7 +292,26 @@ namespace Explorer
else else
{ {
var type = obj.GetType(); var type = obj.GetType();
DrawMember(ref obj, type.ToString(), i.ToString(), rect, setTarget, setAction, 25, true); var lbl = i + ": <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++;
} }
} }
} }
@ -289,6 +323,30 @@ namespace Explorer
{ {
label = (value as Object).name; label = (value as Object).name;
} }
else if (value is Vector4 vec4)
{
label = vec4.ToString();
}
else if (value is Vector3 vec3)
{
label = vec3.ToString();
}
else if (value is Vector2 vec2)
{
label = vec2.ToString();
}
else if (value is Rect rec)
{
label = rec.ToString();
}
else if (value is Matrix4x4 matrix)
{
label = matrix.ToString();
}
else if (value is Color col)
{
label = col.ToString();
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) })) if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))