Compare commits

..

29 Commits

Author SHA1 Message Date
6920ca1129 Update README.md 2020-10-01 20:26:25 +10:00
748e0cabcb 1.8.21
* Fixed a bug when editing a Text Field and the input string is `null`. Only affected Il2Cpp games, appeared in 1.8.0.
* Added a menu page for editing the Explorer Settings in-game, called `Options`.
* Added a new setting for default Items per Page Limit (for all "Pages" in Explorer).
2020-10-01 20:20:52 +10:00
b4b5f1ec93 1.8.2
* Added support for games which use the new InputSystem module and have disabled LegacyInputModule
2020-10-01 18:57:28 +10:00
5afaf85859 Update README.md 2020-10-01 17:16:00 +10:00
b65e417ecb Update GUIUnstrip.cs 2020-09-30 01:59:35 +10:00
23723a4ffd Some more unstrip fixes, and a few cleanups 2020-09-30 01:52:49 +10:00
dab7ecd441 Cleanups and refactorings, and some small UI fixes 2020-09-29 05:40:06 +10:00
f1406d016f Update README.md 2020-09-28 16:19:23 +10:00
99e801b3bd Update README.md 2020-09-28 02:11:35 +10:00
20133e123c Update README.md 2020-09-28 02:04:03 +10:00
142a2a4750 Update README.md 2020-09-27 23:04:42 +10:00
629403a74d 1.8.0 cleanup 2020-09-27 22:52:08 +10:00
873d0f277d Update README.md 2020-09-27 22:15:54 +10:00
99719fafaf Update README.md 2020-09-27 22:07:30 +10:00
b550356f14 1.8.0, merging Mono and Il2Cpp builds, adding BepInEx support
* Project renamed to Explorer to reflect the new scope
* Merged Mono and Il2Cpp builds
* Merged BepInEx and MelonLoader builds
* Some minor changes to accommodate for this
* The release DLL and the config file now use "Explorer" in place of "CppExplorer" for file and folder names
2020-09-27 22:04:23 +10:00
8c6202c194 Allow for inherited flags attributes 2020-09-23 19:42:37 +10:00
f203ae37fc 1.7.5
* Added support for Enums with [Flags] attribute (can set each flag individually)
* Added support for easier bitwise operations on ints (or any primitive assignable to int), and viewing the int as binary. This is intended for things like `Camera.cullingMask`, etc.
* Fixed an issue with Enums that contain duplicate values, for example `CameraClearFlags` (has duplicate values for 2).
2020-09-23 19:19:29 +10:00
2006a9ea76 Faster non-generic Il2Cpp casting 2020-09-21 22:45:33 +10:00
c9bc450d09 Update README.md 2020-09-21 17:48:13 +10:00
a1198f3a92 Update CacheColor.cs 2020-09-21 05:59:01 +10:00
04248a89ce Fix for cases when structs return null (due to null declaring instance) 2020-09-20 20:26:05 +10:00
3639824df3 Cleanup 2020-09-19 01:55:27 +10:00
939861b5f0 Cleanup 2020-09-19 01:44:38 +10:00
ad5fc04a3b Fix methods with multiple generic constraints 2020-09-19 01:27:33 +10:00
c39e097378 Add support for methods with ref/in/out args 2020-09-19 00:14:04 +10:00
129a7e3765 Improved interaction with generic methods and some minor UI fixes 2020-09-18 23:10:46 +10:00
643bb4519c Remove and sort usings 2020-09-18 18:38:11 +10:00
b154cbf39d Add support for generic methods, improved non-generic dictionary output 2020-09-18 18:03:17 +10:00
db91968519 Cleanup and improve syntax highlighting
* Static class members are now displayed in Italics and in a darker color, making them easier to distinguish.
* Cleaned up some issues related to syntax highlighting and refactored it into a global class.
* Methods and properties no longer display their arguments as part of the member name, they are only displayed when "Evaluate" is pressed.
2020-09-16 20:03:57 +10:00
61 changed files with 3216 additions and 1736 deletions

View File

@ -1,23 +1,22 @@
# CppExplorer [![Version](https://img.shields.io/badge/MelonLoader-0.2.7.1-green.svg)](https://github.com/HerpDerpinstine/MelonLoader)
<p align="center"> <p align="center">
<img align="center" src="icon.png"> <img align="center" src="icon.png">
</p> </p>
<p align="center"> <p align="center">
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> Unity games, using <a href="https://github.com/HerpDerpinstine/MelonLoader">MelonLoader</a>.<br><br> An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, using <a href="https://github.com/HerpDerpinstine/MelonLoader">MelonLoader</a> and <a href="https://github.com/BepInEx/BepInEx">BepInEx</a>.<br><br>
<a href="../../releases/latest"> <a href="../../releases/latest">
<img src="https://img.shields.io/github/release/sinai-dev/CppExplorer.svg" /> <img src="https://img.shields.io/github/release/sinai-dev/Explorer.svg" />
</a> </a>
<img src="https://img.shields.io/github/downloads/sinai-dev/CppExplorer/total.svg" /> <img src="https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg" />
</p>
<p align="center">
<a href="https://github.com/sinai-dev/MonoExplorer">Looking for a Mono version?</a>
</p> </p>
- [Known issues](#known-issues) <p align="center">
<img src="https://raw.githubusercontent.com/sinai-dev/Explorer/master/overview.png">
</p>
- [Current status](#current-status)
- [How to install](#how-to-install) - [How to install](#how-to-install)
- [How to use](#how-to-use) - [How to use](#how-to-use)
- [Mod Config](#mod-config) - [Mod Config](#mod-config)
@ -26,21 +25,34 @@
- [Building](#building) - [Building](#building)
- [Credits](#credits) - [Credits](#credits)
## Known issues ## Current status
As of version 1.7+, CppExplorer has reached a fairly stable state for most Il2Cpp games.
| Mod Loader | Il2Cpp | Mono | Mono (.NET 3.5) |
| ----------- | ------ | ---- | ----|
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.MelonLoader.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.MelonLoader.Mono.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.MelonLoader.Mono.NET35.zip) |
| [BepInEx](https://github.com/BepInEx/BepInEx) | ❔ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.NET35.zip) |
<b>IL2CPP Issues:</b>
* .NET 3.5 is not currently supported (Unity 5.6.1 and older), this might change in the future. * .NET 3.5 is not currently supported (Unity 5.6.1 and older), this might change in the future.
* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full MelonLoader log please). * Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full MelonLoader log please).
* Reflection may fail with certain types, see [here](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details. * Reflection may fail with certain types, see [here](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details.
* Scrolling with mouse wheel in the CppExplorer menu may not work on all games at the moment. * Scrolling with mouse wheel in the Explorer menu may not work on all games at the moment.
## How to install ## How to install
### MelonLoader
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game. Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
1. Download <b>CppExplorer.zip</b> from [Releases](https://github.com/sinai-dev/CppExplorer/releases). 1. Download the relevant <b>Explorer_MelonLoader_.zip</b> from above.
2. Unzip the file into the `Mods` folder in your game's installation directory, created by MelonLoader. 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. 3. Make sure it's not in a sub-folder, `Explorer.dll` and `mcs.dll` should be directly in the `Mods\` folder.
### BepInEx
Requires [BepInEx](https://github.com/BepInEx/BepInEx) to be installed for your game.
1. Download the relevant <b>Explorer_BepInEx_.zip</b> from above.
2. Unzip the file into the `BepInEx\plugins\` folder in your game's installation directory, created by BepInEx.
3. Make sure it's not in a sub-folder, `Explorer.dll` and `mcs.dll` should be directly in the `plugins\` folder.
## How to use ## How to use
@ -50,24 +62,23 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
### Mod Config ### Mod Config
There is a simple Mod Config for the CppExplorer, which is generated the first time you run it. There is a simple Mod Config for the Explorer. You can access the settings via the "Options" page of the main menu.
This config is generated to `Mods\CppExplorer\config.xml`. Edit the config while the game is closed if you wish to change it. `Main Menu Toggle` (KeyCode)
* Sets the keybinding for the Main Menu toggle (show/hide all Explorer windows)
`Main_Menu_Toggle` (KeyCode)
* Sets the keybinding for the Main Menu toggle (show/hide all CppExplorer windows)
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes. * See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
* Default: `F7` * Default: `F7`
`Default_Window_Size` (Vector2) `Default Window Size` (Vector2)
* Sets the default width and height for all CppExplorer windows when created. * Sets the default width and height for all Explorer windows when created.
* `x` is width, `y` is height. * `x` is width, `y` is height.
* Default: `<x>550</x> <y>700</y>` * Default: `<x>550</x> <y>700</y>`
## Features `Default Items per Page` (Int)
[![](overview.png)](overview.png) * Sets the default items per page when viewing lists or search results.
* Default: `20`
<i>An overview of the different CppExplorer menus.</i> ## Features
### Scene Explorer ### Scene Explorer
@ -76,7 +87,7 @@ This config is generated to `Mods\CppExplorer\config.xml`. Edit the config while
### Inspectors ### Inspectors
CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>. Explorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
<b>Tips:</b> <b>Tips:</b>
* When in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix. * When in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix.
@ -99,19 +110,19 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
* Filter by name, type, etc. * Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too. * For GameObjects and Transforms you can filter which scene they are found in too.
### C# REPL console ### C# console
* A simple C# REPL console, allows you to execute a method body on the fly. * A simple C# console, allows you to execute a method body on the fly.
### Inspect-under-mouse ### Inspect-under-mouse
* Press Shift+RMB (Right Mouse Button) while the CppExplorer menu is open to begin Inspect-Under-Mouse. * Press Shift+RMB (Right Mouse Button) while the Explorer menu is open to begin Inspect-Under-Mouse.
* Hover over your desired object, if you see the name appear then you can click on it to inspect it. * Hover over your desired object, if you see the name appear then you can click on it to inspect it.
* Only objects with Colliders are supported. * Only objects with Colliders are supported.
### Mouse Control ### Mouse Control
CppExplorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind CppExplorer, this is possible but it requires specific patches for that game. Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind Explorer, this is possible but it requires specific patches for that game.
* For VRChat, use [VRCExplorerMouseControl](https://github.com/sinai-dev/VRCExplorerMouseControl) * For VRChat, use [VRCExplorerMouseControl](https://github.com/sinai-dev/VRCExplorerMouseControl)
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl) * For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
@ -120,17 +131,17 @@ CppExplorer can force the mouse to be visible and unlocked when the menu is open
For example: For example:
```csharp ```csharp
using Explorer; using Explorer;
using Harmony; using Harmony; // or 'using HarmonyLib;' for BepInEx
// ... // ...
[HarmonyPatch(typeof(MyGame.MenuClass), nameof(MyGame.MenuClass.CursorUpdate)] [HarmonyPatch(typeof(MyGame.MenuClass), nameof(MyGame.MenuClass.CursorUpdate)]
public class MenuClass_CursorUpdate public class MenuClass_CursorUpdate
{ {
[HarmonyPrefix] [HarmonyPrefix]
public static bool Prefix() public static bool Prefix()
{ {
// prevent method running if menu open, let it run if not. // prevent method running if menu open, let it run if not.
return !CppExplorer.ShowMenu; return !ExplorerCore.ShowMenu;
} }
} }
``` ```
@ -139,11 +150,11 @@ public class MenuClass_CursorUpdate
If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc. If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc.
1. Install MelonLoader for your game. 1. Install MelonLoader for your game.
2. Open the `src\CppExplorer.csproj` file in a text editor. 2. Open the `src\Explorer.csproj` file in a text editor.
3. Scroll down until you see the `<ItemGroup>` containing the References. 3. Set the relevant `GameFolder` value(s) for the version(s) you want to build, eg. set `MLCppGameFolder` if you want to build for a MelonLoader Il2Cpp game.
4. Fix all of the paths in the `..\Steam\` directory for your game (use the full path if you need to). 4. Open the `src\Explorer.sln` project.
5. Open the `src\CppExplorer.sln` project and build it. 5. Select `Solution 'Explorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> to the version you want to build, then build it.
6. The dll is built to the `Release\` folder in the root of the repository. 5. The DLLs are built to the `Release\` folder in the root of the repository.
## Credits ## Credits

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 23 KiB

BIN
lib/UnityEngine.dll Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 KiB

After

Width:  |  Height:  |  Size: 479 KiB

View File

@ -0,0 +1,169 @@
using System;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
public static class CacheFactory
{
public static CacheObjectBase GetTypeAndCacheObject(object obj)
=> GetTypeAndCacheObject(obj, null, null);
public static CacheObjectBase GetTypeAndCacheObject(MemberInfo memberInfo, object declarer)
=> GetTypeAndCacheObject(null, memberInfo, declarer);
public static CacheObjectBase GetTypeAndCacheObject(object obj, MemberInfo memberInfo, object declarer)
{
Type type = null;
if (memberInfo != null)
{
if (memberInfo is FieldInfo fi)
{
type = fi.FieldType;
}
else if (memberInfo is PropertyInfo pi)
{
type = pi.PropertyType;
}
else if (memberInfo is MethodInfo mi)
{
type = mi.ReturnType;
}
}
else if (obj != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
if (type == null)
{
return null;
}
return GetCacheObject(obj, memberInfo, declarer, type);
}
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
=> GetCacheObject(obj, null, null, valueType);
private static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObjectBase cached;
var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo;
// Check if can process args
if ((pi != null && !CanProcessArgs(pi.GetIndexParameters()))
|| (mi != null && !CanProcessArgs(mi.GetParameters())))
{
return null;
}
if (mi != null)
{
cached = new CacheMethod();
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
cached = new CacheGameObject();
}
else if (valueType.IsPrimitive || valueType == typeof(string))
{
cached = new CachePrimitive();
}
else if (valueType.IsEnum)
{
if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
{
cached = new CacheEnumFlags();
}
else
{
cached = new CacheEnum();
}
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{
cached = new CacheVector();
}
else if (valueType == typeof(Quaternion))
{
cached = new CacheQuaternion();
}
else if (valueType == typeof(Color))
{
cached = new CacheColor();
}
else if (valueType == typeof(Rect))
{
cached = new CacheRect();
}
// must check this before IsEnumerable
else if (ReflectionHelpers.IsDictionary(valueType))
{
cached = new CacheDictionary();
}
else if (ReflectionHelpers.IsEnumerable(valueType))
{
cached = new CacheList();
}
else
{
cached = new CacheOther();
}
cached.Value = obj;
cached.ValueType = valueType;
if (memberInfo != null)
{
cached.MemInfo = memberInfo;
cached.DeclaringType = memberInfo.DeclaringType;
cached.DeclaringInstance = declaringInstance;
}
if (pi != null)
{
cached.m_arguments = pi.GetIndexParameters();
}
else if (mi != null)
{
cached.m_arguments = mi.GetParameters();
}
cached.m_argumentInput = new string[cached.m_arguments.Length];
cached.UpdateValue();
cached.Init();
return cached;
}
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
{
pType = pType.GetElementType();
}
if (pType.IsPrimitive || pType == typeof(string))
{
continue;
}
else
{
return false;
}
}
return true;
}
}
}

View File

@ -2,10 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -13,14 +9,14 @@ namespace Explorer
public abstract class CacheObjectBase public abstract class CacheObjectBase
{ {
public object Value; public object Value;
public string ValueTypeName;
public Type ValueType; public Type ValueType;
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; } public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; } public object DeclaringInstance { get; set; }
public bool HasParameters => m_arguments != null && m_arguments.Length > 0; public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public bool m_evaluated = false; public bool m_evaluated = false;
public bool m_isEvaluating; public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0]; public ParameterInfo[] m_arguments = new ParameterInfo[0];
@ -31,257 +27,12 @@ namespace Explorer
public string RichTextName => m_richTextName ?? GetRichTextName(); public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName; private string m_richTextName;
public bool CanWrite public bool CanWrite => m_canWrite ?? GetCanWrite();
{ private bool? m_canWrite;
get
{
if (MemInfo is FieldInfo fi)
return !(fi.IsLiteral && !fi.IsInitOnly);
else if (MemInfo is PropertyInfo pi)
return pi.CanWrite;
else
return false;
}
}
public virtual void Init() { } public virtual void Init() { }
public abstract void DrawValue(Rect window, float width); public abstract void DrawValue(Rect window, float width);
/// <summary>
/// Get CacheObject from only an object instance
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
public static CacheObjectBase GetCacheObject(object obj)
{
return GetCacheObject(obj, null, null);
}
/// <summary>
/// Get CacheObject from an object instance and provide the value type
/// Calls GetCacheObjectImpl directly</summary>
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
{
return GetCacheObjectImpl(obj, null, null, valueType);
}
/// <summary>
/// Get CacheObject from only a MemberInfo and declaring instance
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (null, memberInfo, declaringInstance)</summary>
public static CacheObjectBase GetCacheObject(MemberInfo memberInfo, object declaringInstance)
{
return GetCacheObject(null, memberInfo, declaringInstance);
}
/// <summary>
/// Get CacheObject from either an object or MemberInfo, and don't provide the type.
/// This gets the type and then calls GetCacheObjectImpl</summary>
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
{
Type type = null;
if (memberInfo != null)
{
if (memberInfo is FieldInfo fi)
{
type = fi.FieldType;
}
else if (memberInfo is PropertyInfo pi)
{
type = pi.PropertyType;
}
else if (memberInfo is MethodInfo mi)
{
type = mi.ReturnType;
}
}
else if (obj != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
if (type == null)
{
return null;
}
return GetCacheObjectImpl(obj, memberInfo, declaringInstance, type);
}
/// <summary>
/// Actual GetCacheObject implementation (private)
/// </summary>
private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObjectBase holder;
var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo;
// if PropertyInfo, check if can process args
if (pi != null && !CanProcessArgs(pi.GetIndexParameters()))
{
return null;
}
// This is pretty ugly, could probably make a cleaner implementation.
// However, the only cleaner ways I can think of are slower and probably not worth it.
// Note: the order is somewhat important.
if (mi != null)
{
if (CacheMethod.CanEvaluate(mi))
{
holder = new CacheMethod();
}
else
{
return null;
}
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
holder = new CacheGameObject();
}
else if (valueType.IsPrimitive || valueType == typeof(string))
{
holder = new CachePrimitive();
}
else if (valueType.IsEnum)
{
holder = new CacheEnum();
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{
holder = new CacheVector();
}
else if (valueType == typeof(Quaternion))
{
holder = new CacheQuaternion();
}
else if (valueType == typeof(Color))
{
holder = new CacheColor();
}
else if (valueType == typeof(Rect))
{
holder = new CacheRect();
}
// must check this before IsEnumerable
else if (ReflectionHelpers.IsDictionary(valueType))
{
holder = new CacheDictionary();
}
else if (ReflectionHelpers.IsEnumerable(valueType))
{
holder = new CacheList();
}
else
{
holder = new CacheOther();
}
holder.Value = obj;
holder.ValueType = valueType;
holder.ValueTypeName = valueType.FullName;
if (memberInfo != null)
{
holder.MemInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance;
}
if (pi != null)
{
holder.m_arguments = pi.GetIndexParameters();
}
else if (mi != null)
{
holder.m_arguments = mi.GetParameters();
}
holder.m_argumentInput = new string[holder.m_arguments.Length];
holder.UpdateValue();
holder.Init();
return holder;
}
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
{
return false;
}
}
return true;
}
public float CalcWhitespace(Rect window)
{
if (!(this is IExpandHeight)) return 0f;
float whitespace = (this as IExpandHeight).WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
return whitespace;
}
public object[] ParseArguments()
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (!string.IsNullOrEmpty(input))
{
// strings can obviously just be used directly
if (type == typeof(string))
{
parsedArgs.Add(input);
continue;
}
else
{
// try to invoke the parse method and use that.
try
{
parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) })
.Invoke(null, new object[] { input }));
continue;
}
catch
{
MelonLogger.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'.");
}
}
}
// Didn't use input, see if there is a default value.
if (m_arguments[i].HasDefaultValue)
{
parsedArgs.Add(m_arguments[i].DefaultValue);
continue;
}
// Try add a null arg I guess
parsedArgs.Add(null);
}
return parsedArgs.ToArray();
}
public virtual void UpdateValue() public virtual void UpdateValue()
{ {
@ -308,14 +59,7 @@ namespace Explorer
var pi = MemInfo as PropertyInfo; var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance; var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
if (HasParameters) Value = pi.GetValue(target, ParseArguments());
{
Value = pi.GetValue(target, ParseArguments());
}
else
{
Value = pi.GetValue(target, null);
}
} }
ReflectionException = null; ReflectionException = null;
@ -347,21 +91,87 @@ namespace Explorer
} }
else else
{ {
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value); pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, null);
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}"); ExplorerCore.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
} }
} }
public object[] ParseArguments()
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type.IsByRef)
{
type = type.GetElementType();
}
if (!string.IsNullOrEmpty(input))
{
if (type == typeof(string))
{
parsedArgs.Add(input);
continue;
}
else
{
try
{
var arg = type.GetMethod("Parse", new Type[] { typeof(string) })
.Invoke(null, new object[] { input });
parsedArgs.Add(arg);
continue;
}
catch
{
ExplorerCore.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'.");
}
}
}
// No input, see if there is a default value.
if (HasDefaultValue(m_arguments[i]))
{
parsedArgs.Add(m_arguments[i].DefaultValue);
continue;
}
// Try add a null arg I guess
parsedArgs.Add(null);
}
return parsedArgs.ToArray();
}
public static bool HasDefaultValue(ParameterInfo arg) => arg.DefaultValue != DBNull.Value;
// ========= Gui Draw ========== // ========= Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f; public const float MAX_LABEL_WIDTH = 400f;
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>"; public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
public float CalcWhitespace(Rect window)
{
if (!(this is IExpandHeight)) return 0f;
float whitespace = (this as IExpandHeight).WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
return whitespace;
}
public static void ClampLabelWidth(Rect window, ref float labelWidth) public static void ClampLabelWidth(Rect window, ref float labelWidth)
{ {
float min = window.width * 0.37f; float min = window.width * 0.37f;
@ -390,42 +200,89 @@ namespace Explorer
if (HasParameters) if (HasParameters)
{ {
GUILayout.BeginVertical(null); GUILayout.BeginVertical(new GUILayoutOption[0]);
if (m_isEvaluating) if (m_isEvaluating)
{ {
for (int i = 0; i < m_arguments.Length; i++) if (cm != null && cm.GenericArgs.Length > 0)
{ {
var name = m_arguments[i].Name; GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", new GUILayoutOption[0]);
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
var label = "<color=#2df7b2>" + type + "</color> <color=#a6e9e9>" + name + "</color>"; for (int i = 0; i < cm.GenericArgs.Length; i++)
if (m_arguments[i].HasDefaultValue)
{ {
label = $"<i>[{label} = {m_arguments[i].DefaultValue}]</i>"; string types = "";
if (cm.GenericConstraints[i].Length > 0)
{
foreach (var constraint in cm.GenericConstraints[i])
{
if (types != "") types += ", ";
string type;
if (constraint == null)
type = "Any";
else
type = constraint.ToString();
types += $"<color={UIStyles.Syntax.Class_Instance}>{type}</color>";
}
}
else
{
types = $"<color={UIStyles.Syntax.Class_Instance}>Any</color>";
}
var input = cm.GenericArgInput[i];
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(
$"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>",
new GUILayoutOption[] { GUILayout.Width(15) }
);
cm.GenericArgInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(types, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
} }
GUILayout.BeginHorizontal(null);
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) });
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUILayout.Label(label, null);
GUILayout.EndHorizontal();
} }
GUILayout.BeginHorizontal(null); if (m_arguments.Length > 0)
{
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < m_arguments.Length; i++)
{
var name = m_arguments[i].Name;
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
if (HasDefaultValue(m_arguments[i]))
{
label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
m_argumentInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(label, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{ {
if (cm != null) if (cm != null)
{
cm.Evaluate(); cm.Evaluate();
}
else else
{
UpdateValue(); UpdateValue();
}
} }
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) })) if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{ {
@ -435,7 +292,12 @@ namespace Explorer
} }
else else
{ {
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) })) var lbl = $"Evaluate (";
int len = m_arguments.Length;
if (cm != null) len += cm.GenericArgs.Length;
lbl += len + " params)";
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) }))
{ {
m_isEvaluating = true; m_isEvaluating = true;
} }
@ -443,37 +305,35 @@ namespace Explorer
GUILayout.EndVertical(); GUILayout.EndVertical();
// new line and space
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth); GUIUnstrip.Space(labelWidth);
} }
else if (cm != null) else if (cm != null)
{ {
//GUILayout.BeginHorizontal(null);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{ {
cm.Evaluate(); cm.Evaluate();
} }
// new line and space
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth); GUIUnstrip.Space(labelWidth);
} }
string typeName = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (!string.IsNullOrEmpty(ReflectionException)) if (!string.IsNullOrEmpty(ReflectionException))
{ {
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null); GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", new GUILayoutOption[0]);
} }
else if ((HasParameters || this is CacheMethod) && !m_evaluated) else if ((HasParameters || this is CacheMethod) && !m_evaluated)
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeName})", new GUILayoutOption[0]);
} }
else if (Value == null && !(this is CacheMethod)) else if (Value == null && !(this is CacheMethod))
{ {
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null); GUILayout.Label($"<i>null ({typeName})</i>", new GUILayoutOption[0]);
} }
else else
{ {
@ -481,33 +341,87 @@ namespace Explorer
} }
} }
private bool GetCanWrite()
{
if (MemInfo is FieldInfo fi)
m_canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
else if (MemInfo is PropertyInfo pi)
m_canWrite = pi.CanWrite;
else
m_canWrite = false;
return (bool)m_canWrite;
}
private string GetRichTextName() private string GetRichTextName()
{ {
string memberColor = ""; string memberColor = "";
switch (MemInfo.MemberType) bool isStatic = false;
{
case MemberTypes.Field:
memberColor = "#c266ff"; break;
case MemberTypes.Property:
memberColor = "#72a6a6"; break;
case MemberTypes.Method:
memberColor = "#ff8000"; break;
};
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>"; if (MemInfo is FieldInfo fi)
if (m_arguments.Length > 0 || this is CacheMethod)
{ {
m_richTextName += "("; if (fi.IsStatic)
var _params = "";
foreach (var param in m_arguments)
{ {
if (_params != "") _params += ", "; isStatic = true;
memberColor = UIStyles.Syntax.Field_Static;
_params += $"<color=#2df7b2>{param.ParameterType.Name}</color> <color=#a6e9e9>{param.Name}</color>";
} }
m_richTextName += _params; else
m_richTextName += ")"; memberColor = UIStyles.Syntax.Field_Instance;
}
else if (MemInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Method_Static;
}
else
memberColor = UIStyles.Syntax.Method_Instance;
}
else if (MemInfo is PropertyInfo pi)
{
if (pi.GetAccessors()[0].IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Prop_Static;
}
else
memberColor = UIStyles.Syntax.Prop_Instance;
}
string classColor;
if (MemInfo.DeclaringType.IsValueType)
{
classColor = UIStyles.Syntax.StructGreen;
}
else if (MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed)
{
classColor = UIStyles.Syntax.Class_Static;
}
else
{
classColor = UIStyles.Syntax.Class_Instance;
}
m_richTextName = $"<color={classColor}>{MemInfo.DeclaringType.Name}</color>.";
if (isStatic) m_richTextName += "<i>";
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
if (isStatic) m_richTextName += "</i>";
// generic method args
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
{
m_richTextName += "<";
var args = "";
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
if (args != "") args += ", ";
args += $"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
}
m_richTextName += args;
m_richTextName += ">";
} }
return m_richTextName; return m_richTextName;

View File

@ -1,10 +1,4 @@
using System; namespace Explorer
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Explorer
{ {
interface IExpandHeight interface IExpandHeight
{ {

View File

@ -1,13 +1,10 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine; using UnityEngine;
using System.Reflection; #if CPP
using UnhollowerBaseLib; using UnhollowerBaseLib;
#endif
namespace Explorer namespace Explorer
{ {
@ -56,8 +53,8 @@ namespace Explorer
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type. // note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
// get keys and values // get keys and values
var keys = ValueType.GetProperty("Keys") .GetValue(Value); var keys = ValueType.GetProperty("Keys") .GetValue(Value, null);
var values = ValueType.GetProperty("Values").GetValue(Value); var values = ValueType.GetProperty("Values").GetValue(Value, null);
// create lists to hold them // create lists to hold them
var keyList = new List<object>(); var keyList = new List<object>();
@ -92,7 +89,7 @@ namespace Explorer
// iterate // iterate
while ((bool)moveNext.Invoke(enumerator, null)) while ((bool)moveNext.Invoke(enumerator, null))
{ {
list.Add(current.GetValue(enumerator)); list.Add(current.GetValue(enumerator, null));
} }
} }
@ -133,14 +130,16 @@ namespace Explorer
var keys = new List<CacheObjectBase>(); var keys = new List<CacheObjectBase>();
foreach (var key in IDict.Keys) foreach (var key in IDict.Keys)
{ {
var cache = GetCacheObject(key, TypeOfKeys); Type t = ReflectionHelpers.GetActualType(key) ?? TypeOfKeys;
var cache = CacheFactory.GetCacheObject(key, t);
keys.Add(cache); keys.Add(cache);
} }
var values = new List<CacheObjectBase>(); var values = new List<CacheObjectBase>();
foreach (var val in IDict.Values) foreach (var val in IDict.Values)
{ {
var cache = GetCacheObject(val, TypeOfValues); Type t = ReflectionHelpers.GetActualType(val) ?? TypeOfValues;
var cache = CacheFactory.GetCacheObject(val, t);
values.Add(cache); values.Add(cache);
} }
@ -155,6 +154,7 @@ namespace Explorer
return true; return true;
} }
#if CPP
try try
{ {
return Check(TypeOfKeys) && Check(TypeOfValues); return Check(TypeOfKeys) && Check(TypeOfValues);
@ -173,6 +173,9 @@ namespace Explorer
{ {
return false; return false;
} }
#else
return false;
#endif
} }
// ============= GUI Draw ============= // ============= GUI Draw =============
@ -181,7 +184,7 @@ namespace Explorer
{ {
if (m_cachedKeys == null || m_cachedValues == null) if (m_cachedKeys == null || m_cachedValues == null)
{ {
GUILayout.Label("Cached keys or values is null!", null); GUILayout.Label("Cached keys or values is null!", new GUILayoutOption[0]);
return; return;
} }
@ -223,7 +226,7 @@ namespace Explorer
if (count > Pages.ItemsPerPage) if (count > Pages.ItemsPerPage)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
@ -253,19 +256,20 @@ namespace Explorer
//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(new GUILayoutOption[0]);
//GUIUnstrip.Space(whitespace); //GUIUnstrip.Space(whitespace);
if (key == null || val == null) if (key == null || val == null)
{ {
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null); GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", new GUILayoutOption[0]);
} }
else else
{ {
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
key.DrawValue(window, (window.width / 2) - 80f); key.DrawValue(window, (window.width / 2) - 80f);

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,9 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -104,6 +102,7 @@ namespace Explorer
{ {
if (Value == null) return null; if (Value == null) return null;
#if CPP
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>)) if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
{ {
return (IEnumerable)CppListToArrayMethod?.Invoke(Value, new object[0]); return (IEnumerable)CppListToArrayMethod?.Invoke(Value, new object[0]);
@ -116,8 +115,12 @@ namespace Explorer
{ {
return CppIListToMono(); return CppIListToMono();
} }
#else
return Value as IEnumerable;
#endif
} }
#if CPP
private IEnumerable CppHashSetToMono() private IEnumerable CppHashSetToMono()
{ {
var set = new HashSet<object>(); var set = new HashSet<object>();
@ -159,10 +162,11 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message); ExplorerCore.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
return null; return null;
} }
} }
#endif
private Type GetEntryType() private Type GetEntryType()
{ {
@ -227,6 +231,7 @@ namespace Explorer
if (obj != null && ReflectionHelpers.GetActualType(obj) is Type t) if (obj != null && ReflectionHelpers.GetActualType(obj) is Type t)
{ {
#if CPP
if (obj is Il2CppSystem.Object iObj) if (obj is Il2CppSystem.Object iObj)
{ {
try try
@ -239,8 +244,9 @@ namespace Explorer
} }
catch { } catch { }
} }
#endif
if (GetCacheObject(obj, t) is CacheObjectBase cached) if (CacheFactory.GetCacheObject(obj, t) is CacheObjectBase cached)
{ {
list.Add(cached); list.Add(cached);
} }
@ -264,7 +270,7 @@ namespace Explorer
{ {
if (m_cachedEntries == null) if (m_cachedEntries == null)
{ {
GUILayout.Label("m_cachedEntries is null!", null); GUILayout.Label("m_cachedEntries is null!", new GUILayoutOption[0]);
return; return;
} }
@ -291,7 +297,7 @@ namespace Explorer
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = $"[{count}] <color=#2df7b2>{EntryType.FullName}</color>"; string btnLabel = $"[{count}] <color=#2df7b2>{EntryType.FullName}</color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(negativeWhitespace) })) if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }
@ -306,7 +312,7 @@ namespace Explorer
if (count > Pages.ItemsPerPage) if (count > Pages.ItemsPerPage)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
@ -335,19 +341,20 @@ namespace Explorer
//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(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
if (entry == null || entry.Value == null) if (entry == null || entry.Value == null)
{ {
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null); GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", new GUILayoutOption[0]);
} }
else else
{ {
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
entry.DrawValue(window, window.width - (whitespace + 85)); entry.DrawValue(window, window.width - (whitespace + 85));
} }

View File

@ -1,11 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using MelonLoader;
namespace Explorer namespace Explorer
{ {
@ -13,16 +10,24 @@ namespace Explorer
{ {
private CacheObjectBase m_cachedReturnValue; private CacheObjectBase m_cachedReturnValue;
public static bool CanEvaluate(MethodInfo mi) public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0;
{
// TODO generic args
if (mi.GetGenericArguments().Length > 0)
{
return false;
}
// primitive and string args supported public Type[] GenericArgs { get; private set; }
return CanProcessArgs(mi.GetParameters()); public Type[][] GenericConstraints { get; private set; }
public string[] GenericArgInput = new string[0];
public override void Init()
{
var mi = (MemInfo as MethodInfo);
GenericArgs = mi.GetGenericArguments();
GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints())
.ToArray();
GenericArgInput = new string[GenericArgs.Length];
ValueType = mi.ReturnType;
} }
public override void UpdateValue() public override void UpdateValue()
@ -32,32 +37,34 @@ namespace Explorer
public void Evaluate() public void Evaluate()
{ {
m_isEvaluating = false; MethodInfo mi;
if (GenericArgs.Length > 0)
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{ {
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]); mi = MakeGenericMethodFromInput();
m_evaluated = true; if (mi == null) return;
} }
else else
{ {
try mi = MemInfo as MethodInfo;
{ }
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
m_evaluated = true; object ret = null;
}
catch (Exception e) try
{ {
MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}"); ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
} m_evaluated = true;
m_isEvaluating = false;
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}");
ReflectionException = ReflectionHelpers.ExceptionToString(e);
} }
if (ret != null) if (ret != null)
{ {
m_cachedReturnValue = GetCacheObject(ret); m_cachedReturnValue = CacheFactory.GetTypeAndCacheObject(ret);
m_cachedReturnValue.UpdateValue(); m_cachedReturnValue.UpdateValue();
} }
else else
@ -66,10 +73,54 @@ namespace Explorer
} }
} }
private MethodInfo MakeGenericMethodFromInput()
{
var mi = MemInfo as MethodInfo;
var list = new List<Type>();
for (int i = 0; i < GenericArgs.Length; i++)
{
var input = GenericArgInput[i];
if (ReflectionHelpers.GetTypeByName(input) is Type t)
{
if (GenericConstraints[i].Length == 0)
{
list.Add(t);
}
else
{
foreach (var constraint in GenericConstraints[i].Where(x => x != null))
{
if (!constraint.IsAssignableFrom(t))
{
ExplorerCore.LogWarning($"Generic argument #{i}, '{input}' is not assignable from the constraint '{constraint}'!");
return null;
}
}
list.Add(t);
}
}
else
{
ExplorerCore.LogWarning($"Generic argument #{i}, could not get any type by the name of '{input}'!" +
$" Make sure you use the full name, including the NameSpace.");
return null;
}
}
// make into a generic with type list
mi = mi.MakeGenericMethod(list.ToArray());
return mi;
}
// ==== GUI DRAW ==== // ==== GUI DRAW ====
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
string typeLabel = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (m_evaluated) if (m_evaluated)
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
@ -78,12 +129,12 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label($"null (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"null ({typeLabel})", new GUILayoutOption[0]);
} }
} }
else else
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeLabel})", new GUILayoutOption[0]);
} }
} }
} }

View File

@ -1,9 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
@ -11,55 +6,75 @@ namespace Explorer
{ {
public class CacheOther : CacheObjectBase public class CacheOther : CacheObjectBase
{ {
public string ButtonLabel => m_btnLabel ?? GetButtonLabel();
private string m_btnLabel;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
private MethodInfo m_toStringMethod; private MethodInfo m_toStringMethod;
public MethodInfo ToStringMethod public override void UpdateValue()
{ {
get base.UpdateValue();
{
if (m_toStringMethod == null)
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke GetButtonLabel();
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
}
return m_toStringMethod;
}
} }
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
if (!label.Contains(ValueTypeName))
{
label += $" (<color=#2df7b2>{ValueTypeName}</color>)";
}
else
{
label = label.Replace(ValueTypeName, $"<color=#2df7b2>{ValueTypeName}</color>");
}
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
{
label = unityObj.name + " | " + label;
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width - 15) })) if (GUILayout.Button(ButtonLabel, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }
GUI.skin.button.alignment = TextAnchor.MiddleCenter; GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
private MethodInfo GetToStringMethod()
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
return m_toStringMethod;
}
private string GetButtonLabel()
{
if (Value == null) return null;
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
var classColor = ValueType.IsAbstract && ValueType.IsSealed
? UIStyles.Syntax.Class_Static
: UIStyles.Syntax.Class_Instance;
string typeLabel = $"<color={classColor}>{ValueType.FullName}</color>";
if (Value is UnityEngine.Object)
{
label = label.Replace($"({ValueType.FullName})", $"({typeLabel})");
}
else
{
if (!label.Contains(ValueType.FullName))
{
label += $" ({typeLabel})";
}
else
{
label = label.Replace(ValueType.FullName, typeLabel);
}
}
return m_btnLabel = label;
}
} }
} }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -21,6 +20,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var color = (Color)Value; var color = (Color)Value;
r = color.r.ToString(); r = color.r.ToString();
@ -51,7 +52,7 @@ namespace Explorer
//var c = (Color)Value; //var c = (Color)Value;
//GUI.color = c; //GUI.color = c;
GUILayout.Label($"<color=#2df7b2>Color:</color> {((Color)Value).ToString()}", null); GUILayout.Label($"<color=#2df7b2>Color:</color> {((Color)Value).ToString()}", new GUILayoutOption[0]);
//GUI.color = Color.white; //GUI.color = Color.white;
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
@ -60,32 +61,32 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("R:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("R:", new GUILayoutOption[] { GUILayout.Width(30) });
r = GUILayout.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) }); r = GUIUnstrip.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("G:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("G:", new GUILayoutOption[] { GUILayout.Width(30) });
g = GUILayout.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) }); g = GUIUnstrip.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("B:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("B:", new GUILayoutOption[] { GUILayout.Width(30) });
b = GUILayout.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) }); b = GUIUnstrip.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("A:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("A:", new GUILayoutOption[] { GUILayout.Width(30) });
a = GUILayout.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) }); a = GUIUnstrip.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
@ -93,7 +94,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
} }
} }
@ -104,7 +105,7 @@ namespace Explorer
&& float.TryParse(b, out float fB) && float.TryParse(b, out float fB)
&& float.TryParse(a, out float fA)) && float.TryParse(a, out float fA))
{ {
Value = new Color(fR, fB, fG, fA); Value = new Color(fR, fG, fB, fA);
SetValue(); SetValue();
} }
} }

View File

@ -2,32 +2,37 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheEnum : CacheObjectBase public class CacheEnum : CacheObjectBase
{ {
public Type EnumType; // public Type EnumType;
public string[] EnumNames; public string[] EnumNames = new string[0];
public override void Init() public override void Init()
{ {
try if (ValueType == null && Value != null)
{ {
EnumType = Value.GetType(); ValueType = Value.GetType();
}
catch
{
EnumType = (MemInfo as FieldInfo)?.FieldType ?? (MemInfo as PropertyInfo).PropertyType;
} }
if (EnumType != null) if (ValueType != null)
{ {
EnumNames = Enum.GetNames(EnumType); // using GetValues not GetNames, to catch instances of weird enums (eg CameraClearFlags)
var values = Enum.GetValues(ValueType);
var list = new List<string>();
foreach (var value in values)
{
var v = value.ToString();
if (list.Contains(v)) continue;
list.Add(v);
}
EnumNames = list.ToArray();
} }
else else
{ {
@ -41,28 +46,28 @@ namespace Explorer
{ {
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
SetEnum(ref Value, -1); SetEnum(-1);
SetValue(); SetValue();
} }
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) })) if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
SetEnum(ref Value, 1); SetEnum(1);
SetValue(); SetValue();
} }
} }
GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", null); GUILayout.Label(Value.ToString() + $"<color={UIStyles.Syntax.StructGreen}><i> ({ValueType})</i></color>", new GUILayoutOption[0]);
} }
public void SetEnum(ref object value, int change) public void SetEnum(int change)
{ {
var names = EnumNames.ToList(); var names = EnumNames.ToList();
int newindex = names.IndexOf(value.ToString()) + change; int newindex = names.IndexOf(Value.ToString()) + change;
if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count)) if (newindex >= 0 && newindex < names.Count)
{ {
value = Enum.Parse(EnumType, names[newindex]); Value = Enum.Parse(ValueType, EnumNames[newindex]);
} }
} }
} }

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Explorer
{
public class CacheEnumFlags : CacheEnum, IExpandHeight
{
public bool[] m_enabledFlags = new bool[0];
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public override void Init()
{
base.Init();
UpdateValue();
}
public override void UpdateValue()
{
base.UpdateValue();
if (Value == null) return;
try
{
var enabledNames = Value.ToString().Split(',').Select(it => it.Trim());
m_enabledFlags = new bool[EnumNames.Length];
for (int i = 0; i < EnumNames.Length; i++)
{
m_enabledFlags[i] = enabledNames.Contains(EnumNames[i]);
}
}
catch (Exception e)
{
ExplorerCore.Log(e.ToString());
}
}
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
{
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
}
GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", new GUILayoutOption[0]);
if (IsExpanded)
{
GUILayout.EndHorizontal();
var whitespace = CalcWhitespace(window);
for (int i = 0; i < EnumNames.Length; i++)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
m_enabledFlags[i] = GUILayout.Toggle(m_enabledFlags[i], EnumNames[i], new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetFlagsFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
}
}
public void SetFlagsFromInput()
{
string val = "";
for (int i = 0; i < EnumNames.Length; i++)
{
if (m_enabledFlags[i])
{
if (val != "") val += ", ";
val += EnumNames[i];
}
}
Value = Enum.Parse(ValueType, val);
SetValue();
}
}
}

View File

@ -1,21 +1,28 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnityEngine; using UnityEngine;
#if CPP
using UnhollowerRuntimeLib;
#endif
namespace Explorer namespace Explorer
{ {
public class CachePrimitive : CacheObjectBase public class CachePrimitive : CacheObjectBase
{ {
private string m_valueToString;
private bool m_isBool; private bool m_isBool;
private bool m_isString; private bool m_isString;
private string m_valueToString;
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) })); public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod; private MethodInfo m_parseMethod;
private bool m_canBitwiseOperate;
private bool m_inBitwiseMode;
private string m_bitwiseOperatorInput = "0";
private string m_binaryInput;
public override void Init() public override void Init()
{ {
if (ValueType == null) if (ValueType == null)
@ -37,13 +44,28 @@ namespace Explorer
{ {
m_isBool = true; m_isBool = true;
} }
m_canBitwiseOperate = typeof(int).IsAssignableFrom(ValueType);
UpdateValue();
} }
public override void UpdateValue() public override void UpdateValue()
{ {
base.UpdateValue(); base.UpdateValue();
RefreshToString();
}
private void RefreshToString()
{
m_valueToString = Value?.ToString(); m_valueToString = Value?.ToString();
if (m_canBitwiseOperate)
{
var _int = (int)Value;
m_binaryInput = Convert.ToString(_int, toBase: 2);
}
} }
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
@ -55,72 +77,175 @@ namespace Explorer
if (CanWrite) if (CanWrite)
{ {
b = GUILayout.Toggle(b, label, null); b = GUILayout.Toggle(b, label, new GUILayoutOption[0]);
if (b != (bool)Value) if (b != (bool)Value)
{ {
SetValueFromInput(b.ToString()); Value = b;
SetValue();
} }
} }
else else
{ {
GUILayout.Label(label, null); GUILayout.Label(label, new GUILayoutOption[0]);
} }
return;
} }
else
// all other non-bool values use TextField
GUILayout.BeginVertical(new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
m_valueToString = GUIUnstrip.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.ExpandWidth(true) });
if (CanWrite)
{ {
// using ValueType.Name instead of ValueTypeName, because we only want the short name. if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
int dynSize = 25 + (m_valueToString.Length * 15);
var maxwidth = window.width - 310f;
if (CanWrite) maxwidth -= 60;
if (dynSize > maxwidth)
{ {
m_valueToString = GUILayout.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(maxwidth) }); SetValueFromInput();
} }
else
{
m_valueToString = GUILayout.TextField(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(dynSize) });
}
if (CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
SetValueFromInput(m_valueToString);
}
}
GUIUnstrip.Space(10);
} }
if (m_canBitwiseOperate)
{
m_inBitwiseMode = GUILayout.Toggle(m_inBitwiseMode, "Bitwise?", new GUILayoutOption[0]);
}
GUIUnstrip.Space(10);
GUILayout.EndHorizontal();
if (m_inBitwiseMode)
{
DrawBitwise();
}
GUILayout.EndVertical();
} }
public void SetValueFromInput(string valueString) private void DrawBitwise()
{
if (CanWrite)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("RHS:", new GUILayoutOption[] { GUILayout.Width(35) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
if (GUILayout.Button("~", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = ~bit;
RefreshToString();
}
}
if (GUILayout.Button("<<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value << bit;
RefreshToString();
}
}
if (GUILayout.Button(">>", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value >> bit;
RefreshToString();
}
}
if (GUILayout.Button("|", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value | bit;
RefreshToString();
}
}
if (GUILayout.Button("&", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value & bit;
RefreshToString();
}
}
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value ^ bit;
RefreshToString();
}
}
m_bitwiseOperatorInput = GUIUnstrip.TextField(m_bitwiseOperatorInput, new GUILayoutOption[] { GUILayout.Width(55) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan>Binary:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
m_binaryInput = GUIUnstrip.TextField(m_binaryInput, new GUILayoutOption[0]);
if (CanWrite)
{
if (GUILayout.Button("Apply", new GUILayoutOption[0]))
{
SetValueFromBinaryInput();
}
}
GUILayout.EndHorizontal();
}
public void SetValueFromInput()
{ {
if (MemInfo == null) if (MemInfo == null)
{ {
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!"); ExplorerCore.Log("Trying to SetValue but the MemberInfo is null!");
return; return;
} }
if (m_isString) if (m_isString)
{ {
Value = valueString; Value = m_valueToString;
} }
else else
{ {
try try
{ {
Value = ParseMethod.Invoke(null, new object[] { valueString }); Value = ParseMethod.Invoke(null, new object[] { m_valueToString });
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception parsing value: " + e.GetType() + ", " + e.Message); ExplorerCore.Log("Exception parsing value: " + e.GetType() + ", " + e.Message);
} }
} }
SetValue(); SetValue();
RefreshToString();
}
private void SetValueFromBinaryInput()
{
try
{
var method = typeof(Convert).GetMethod($"To{ValueType.Name}", new Type[] { typeof(string), typeof(int) });
Value = method.Invoke(null, new object[] { m_binaryInput, 2 });
SetValue();
RefreshToString();
}
catch (Exception e)
{
ExplorerCore.Log("Exception setting value: " + e.GetType() + ", " + e.Message);
}
} }
} }
} }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -20,6 +19,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var euler = ((Quaternion)Value).eulerAngles; var euler = ((Quaternion)Value).eulerAngles;
x = euler.x.ToString(); x = euler.x.ToString();
@ -47,7 +48,7 @@ namespace Explorer
} }
} }
GUILayout.Label($"<color=#2df7b2>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null); GUILayout.Label($"<color=#2df7b2>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", new GUILayoutOption[0]);
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
@ -55,26 +56,26 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); x = GUIUnstrip.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); y = GUIUnstrip.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) });
z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) }); z = GUIUnstrip.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
@ -82,7 +83,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
} }
} }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -21,6 +20,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var rect = (Rect)Value; var rect = (Rect)Value;
x = rect.x.ToString(); x = rect.x.ToString();
@ -49,7 +50,7 @@ namespace Explorer
} }
} }
GUILayout.Label($"<color=#2df7b2>Rect</color>: {((Rect)Value).ToString()}", null); GUILayout.Label($"<color=#2df7b2>Rect</color>: {((Rect)Value).ToString()}", new GUILayoutOption[0]);
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
@ -57,32 +58,32 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); x = GUIUnstrip.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); y = GUIUnstrip.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) });
w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) }); w = GUIUnstrip.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("H:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("H:", new GUILayoutOption[] { GUILayout.Width(30) });
h = GUILayout.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) }); h = GUIUnstrip.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
@ -90,7 +91,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
} }
} }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
@ -24,20 +23,26 @@ namespace Explorer
public override void Init() public override void Init()
{ {
if (Value is Vector2) if (ValueType == null && Value != null)
{
ValueType = Value.GetType();
}
if (ValueType == typeof(Vector2))
{ {
VectorSize = 2; VectorSize = 2;
m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]);
} }
else if (Value is Vector3) else if (ValueType == typeof(Vector3))
{ {
VectorSize = 3; VectorSize = 3;
m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]);
} }
else else
{ {
VectorSize = 4; VectorSize = 4;
m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]);
} }
m_toStringMethod = Value.GetType().GetMethod("ToString", new Type[0]);
} }
public override void UpdateValue() public override void UpdateValue()
@ -84,7 +89,7 @@ namespace Explorer
} }
} }
GUILayout.Label($"<color=#2df7b2>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null); GUILayout.Label($"<color=#2df7b2>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", new GUILayoutOption[0]);
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
@ -93,39 +98,39 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
// always draw x and y // always draw x and y
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); x = GUIUnstrip.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); y = GUIUnstrip.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (VectorSize > 2) if (VectorSize > 2)
{ {
// draw z // draw z
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) });
z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) }); z = GUIUnstrip.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
if (VectorSize > 3) if (VectorSize > 3)
{ {
// draw w // draw w
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) });
w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) }); w = GUIUnstrip.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
@ -133,7 +138,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
} }
} }

View File

@ -1,9 +1,4 @@
using System; using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using UnityEngine; using UnityEngine;
@ -13,13 +8,14 @@ namespace Explorer
{ {
[XmlIgnore] public static readonly XmlSerializer Serializer = new XmlSerializer(typeof(ModConfig)); [XmlIgnore] public static readonly XmlSerializer Serializer = new XmlSerializer(typeof(ModConfig));
[XmlIgnore] private const string EXPLORER_FOLDER = @"Mods\CppExplorer"; [XmlIgnore] private const string EXPLORER_FOLDER = @"Mods\Explorer";
[XmlIgnore] private const string SETTINGS_PATH = EXPLORER_FOLDER + @"\config.xml"; [XmlIgnore] private const string SETTINGS_PATH = EXPLORER_FOLDER + @"\config.xml";
[XmlIgnore] public static ModConfig Instance; [XmlIgnore] public static ModConfig Instance;
public KeyCode Main_Menu_Toggle = KeyCode.F7; public KeyCode Main_Menu_Toggle = KeyCode.F7;
public Vector2 Default_Window_Size = new Vector2(550, 700); public Vector2 Default_Window_Size = new Vector2(550, 700);
public int Default_Page_Limit = 20;
public static void OnLoad() public static void OnLoad()
{ {
@ -35,9 +31,9 @@ namespace Explorer
} }
// returns true if settings successfully loaded // returns true if settings successfully loaded
public static bool LoadSettings(bool checkExist = true) public static bool LoadSettings()
{ {
if (checkExist && !File.Exists(SETTINGS_PATH)) if (!File.Exists(SETTINGS_PATH))
return false; return false;
try try
@ -55,9 +51,9 @@ namespace Explorer
return Instance != null; return Instance != null;
} }
public static void SaveSettings(bool checkExist = true) public static void SaveSettings()
{ {
if (checkExist && File.Exists(SETTINGS_PATH)) if (File.Exists(SETTINGS_PATH))
File.Delete(SETTINGS_PATH); File.Delete(SETTINGS_PATH);
using (var file = File.Create(SETTINGS_PATH)) using (var file = File.Create(SETTINGS_PATH))

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>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<AssemblyName>CppExplorer</AssemblyName>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants />
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<!-- Replace the '..\Steam\..` references with ones from your game (make sure to use the 'MelonLoader\' folder) -->
<Reference Include="Il2Cppmscorlib">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="mcs">
<HintPath>..\lib\mcs.dll</HintPath>
<Private>True</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="UnityEngine">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedObjects\IExpandHeight.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" />
<Compile Include="CachedObjects\Object\CacheGameObject.cs" />
<Compile Include="CachedObjects\Object\CacheList.cs" />
<Compile Include="CachedObjects\Struct\CachePrimitive.cs" />
<Compile Include="CachedObjects\Other\CacheOther.cs" />
<Compile Include="CachedObjects\Other\CacheMethod.cs" />
<Compile Include="CachedObjects\Struct\CacheQuaternion.cs" />
<Compile Include="CachedObjects\Struct\CacheVector.cs" />
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
<Compile Include="Config\ModConfig.cs" />
<Compile Include="CppExplorer.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" />
<Compile Include="Menu\CursorControl.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\LayoutUtilityUnstrip.cs" />
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
<Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\PageHelper.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Menu\UIHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="Menu\InspectUnderMouse.cs" />
<Compile Include="CachedObjects\CacheObjectBase.cs" />
<Compile Include="UnstripFixes\SliderHandlerUnstrip.cs" />
<Compile Include="UnstripFixes\UnstripExtensions.cs" />
<Compile Include="Menu\ResizeDrag.cs" />
<Compile Include="Menu\Windows\TabViewWindow.cs" />
<Compile Include="Menu\Windows\UIWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ConsolePage.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPL.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="Menu\MainMenu\Pages\WindowPage.cs" />
<Compile Include="Menu\Windows\WindowManager.cs" />
<Compile Include="Menu\MainMenu\MainMenu.cs" />
<Compile Include="Menu\Windows\GameObjectWindow.cs" />
<Compile Include="Menu\Windows\ReflectionWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ScenePage.cs" />
<Compile Include="Menu\MainMenu\Pages\SearchPage.cs" />
<Compile Include="Menu\UIStyles.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -1,22 +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
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD5C0A5D-03F1-4CC3-8B4D-E10834908C5A}
EndGlobalSection
EndGlobal

278
src/Explorer.csproj Normal file
View File

@ -0,0 +1,278 @@
<?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)' == '' ">Release_ML_Cpp</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid>
<OutputType>Library</OutputType>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<OutputPath>..\Release\Explorer.MelonLoader.Il2Cpp\</OutputPath>
<DefineConstants>CPP,ML</DefineConstants>
<IsCpp>true</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<RootNamespace>Explorer</RootNamespace>
<AssemblyName>Explorer</AssemblyName>
<!-- Set this to the MelonLoader Il2Cpp Game folder, without the ending '\' character. -->
<MLCppGameFolder>D:\Steam\steamapps\common\Hellpoint</MLCppGameFolder>
<!--<MLCppGameFolder>D:\source\Unity Projects\Test\_BUILD</MLCppGameFolder>-->
<!-- Set this to the MelonLoader Mono Game folder, without the ending '\' character. -->
<MLMonoGameFolder>D:\Steam\steamapps\common\Outward</MLMonoGameFolder>
<!-- Set this to the BepInEx Il2Cpp Game folder, without the ending '\' character. -->
<BIECppGameFolder>D:\Steam\steamapps\common\Outward - Il2Cpp</BIECppGameFolder>
<!-- Set this to the BepInEx Mono Game folder, without the ending '\' character. -->
<BIEMonoGameFolder>D:\Steam\steamapps\common\Outward</BIEMonoGameFolder>
<!-- <BIEMonoGameFolder>D:\source\Unity Projects\! My Unity Games\NewInputSystemTest\_BUILD</BIEMonoGameFolder> -->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Cpp|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.MelonLoader.Il2Cpp\</OutputPath>
<DefineConstants>CPP,ML</DefineConstants>
<IsCpp>true</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.MelonLoader.Mono\</OutputPath>
<DefineConstants>MONO,ML</DefineConstants>
<Prefer32Bit>false</Prefer32Bit>
<IsCpp>false</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono_NET35|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.MelonLoader.Mono.NET35\</OutputPath>
<DefineConstants>MONO,ML,NET35</DefineConstants>
<IsCpp>false</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>true</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Cpp|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.BepInEx.Il2Cpp\</OutputPath>
<DefineConstants>CPP,BIE</DefineConstants>
<IsCpp>true</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.BepInEx.Mono\</OutputPath>
<DefineConstants>MONO,BIE</DefineConstants>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono_NET35|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.BepInEx.Mono.NET35\</OutputPath>
<DefineConstants>MONO,BIE,NET35</DefineConstants>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>true</IsNet35>
</PropertyGroup>
<ItemGroup>
<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" />
<!-- MCS ref -->
<Reference Include="mcs" Condition="'$(IsNet35)'=='false'">
<HintPath>..\lib\mcs.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="mcs" Condition="'$(IsNet35)'=='true'">
<HintPath>..\lib\mcs.NET35.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<!-- Universal Mono UnityEngine.dll ref (v5.3) -->
<ItemGroup Condition="'$(IsCpp)'=='false'">
<Reference Include="UnityEngine">
<HintPath>..\lib\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- MelonLoader Mono ref -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'">
<Reference Include="MelonLoader.ModHandler">
<HintPath>$(MLMonoGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- BepInEx Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'">
<Reference Include="BepInEx">
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\BepInEx.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- MelonLoader Il2Cpp refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'">
<Reference Include="MelonLoader.ModHandler">
<HintPath>$(MLCppGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnhollowerBaseLib">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- BepInEx Il2Cpp refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|true'">
<Reference Include="BepInEx">
<HintPath>$(BIECppGameFolder)\BepInEx\core\BepInEx.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>$(BIECppGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="BepInEx.IL2CPP">
<HintPath>$(BIECppGameFolder)\BepInEx\core\BepInEx.IL2CPP.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnhollowerBaseLib">
<HintPath>$(BIECppGameFolder)\BepInEx\core\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedObjects\CacheFactory.cs" />
<Compile Include="CachedObjects\IExpandHeight.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" />
<Compile Include="CachedObjects\Object\CacheGameObject.cs" />
<Compile Include="CachedObjects\Object\CacheList.cs" />
<Compile Include="CachedObjects\Struct\CacheEnumFlags.cs" />
<Compile Include="CachedObjects\Struct\CachePrimitive.cs" />
<Compile Include="CachedObjects\Other\CacheOther.cs" />
<Compile Include="CachedObjects\Other\CacheMethod.cs" />
<Compile Include="CachedObjects\Struct\CacheQuaternion.cs" />
<Compile Include="CachedObjects\Struct\CacheVector.cs" />
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
<Compile Include="Config\ModConfig.cs" />
<Compile Include="ExplorerCore.cs" />
<Compile Include="ExplorerBepInPlugin.cs" />
<Compile Include="ExplorerMelonMod.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" />
<Compile Include="Menu\CursorControl.cs" />
<Compile Include="Menu\MainMenu\Pages\OptionsPage.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\Internal_LayoutUtility.cs" />
<Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\PageHelper.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Menu\UIHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="Menu\InspectUnderMouse.cs" />
<Compile Include="CachedObjects\CacheObjectBase.cs" />
<Compile Include="UnstripFixes\Internal_ScrollViewState.cs" />
<Compile Include="UnstripFixes\Internal_SliderHandler.cs" />
<Compile Include="Menu\ResizeDrag.cs" />
<Compile Include="Menu\Windows\TabViewWindow.cs" />
<Compile Include="Menu\Windows\UIWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ConsolePage.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPL.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="Menu\MainMenu\Pages\WindowPage.cs" />
<Compile Include="Menu\Windows\WindowManager.cs" />
<Compile Include="Menu\MainMenu\MainMenu.cs" />
<Compile Include="Menu\Windows\GameObjectWindow.cs" />
<Compile Include="Menu\Windows\ReflectionWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ScenePage.cs" />
<Compile Include="Menu\MainMenu\Pages\SearchPage.cs" />
<Compile Include="Menu\UIStyles.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UnstripFixes\Internal.cs" />
<Compile Include="UnstripFixes\Internal_SliderState.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

37
src/Explorer.sln Normal file
View File

@ -0,0 +1,37 @@

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}") = "Explorer", "Explorer.csproj", "{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release_BIE_Cpp|Any CPU = Release_BIE_Cpp|Any CPU
Release_BIE_Mono_NET35|Any CPU = Release_BIE_Mono_NET35|Any CPU
Release_BIE_Mono|Any CPU = Release_BIE_Mono|Any CPU
Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU
Release_ML_Mono_NET35|Any CPU = Release_ML_Mono_NET35|Any CPU
Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release_BIE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.Build.0 = Release_BIE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono_NET35|Any CPU.ActiveCfg = Release_BIE_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono_NET35|Any CPU.Build.0 = Release_BIE_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono|Any CPU.ActiveCfg = Release_BIE_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono|Any CPU.Build.0 = Release_BIE_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.ActiveCfg = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.Build.0 = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono_NET35|Any CPU.ActiveCfg = Release_ML_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono_NET35|Any CPU.Build.0 = Release_ML_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.ActiveCfg = Release_ML_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.Build.0 = Release_ML_Mono|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD5C0A5D-03F1-4CC3-8B4D-E10834908C5A}
EndGlobalSection
EndGlobal

122
src/ExplorerBepInPlugin.cs Normal file
View File

@ -0,0 +1,122 @@
#if BIE
using System;
using System.IO;
using System.Reflection;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
#if CPP
using UnhollowerRuntimeLib;
using BepInEx.IL2CPP;
#endif
namespace Explorer
{
[BepInPlugin(ExplorerCore.GUID, ExplorerCore.NAME, ExplorerCore.VERSION)]
#if CPP
public class ExplorerBepInPlugin : BasePlugin
#else
public class ExplorerBepInPlugin : BaseUnityPlugin
#endif
{
public static ExplorerBepInPlugin Instance;
public static ManualLogSource Logging =>
#if CPP
Instance?.Log;
#else
Instance?.Logger;
#endif
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
#if CPP
// temporary for Il2Cpp until scene change delegate works
private static string lastSceneName;
#endif
// Init
#if CPP
public override void Load()
{
#else
internal void Awake()
{
#endif
Instance = this;
#if CPP
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
var obj = new GameObject(
"ExplorerBehaviour",
new Il2CppSystem.Type[]
{
Il2CppType.Of<ExplorerBehaviour>()
}
);
GameObject.DontDestroyOnLoad(obj);
#else
SceneManager.activeSceneChanged += DoSceneChange;
#endif
LoadMCS();
new ExplorerCore();
//HarmonyInstance.PatchAll();
}
void LoadMCS()
{
#if NET35
var path = @"BepInEx\plugins\mcs.NET35.dll";
#else
var path = @"BepInEx\plugins\mcs.dll";
#endif
Assembly.Load(File.ReadAllBytes(path));
ExplorerCore.Log("Loaded mcs!");
}
internal static void DoSceneChange(Scene arg0, Scene arg1)
{
ExplorerCore.OnSceneChange();
}
#if CPP // BepInEx Il2Cpp mod class doesn't have monobehaviour methods yet, so wrap them in a dummy.
public class ExplorerBehaviour : MonoBehaviour
{
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
internal void Awake()
{
Logging.LogMessage("ExplorerBehaviour.Awake");
}
#endif
internal void Update()
{
ExplorerCore.Update();
#if CPP
var scene = SceneManager.GetActiveScene();
if (scene.name != lastSceneName)
{
lastSceneName = scene.name;
DoSceneChange(scene, scene);
}
#endif
}
internal void OnGUI()
{
ExplorerCore.OnGUI();
}
#if CPP
}
#endif
}
}
#endif

View File

@ -1,23 +1,43 @@
using System; using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CppExplorer : MelonMod public class ExplorerCore
{ {
public const string NAME = "CppExplorer"; public const string NAME = "Explorer (" + PLATFORM + ", " + MODLOADER + ")";
public const string VERSION = "1.7.31"; public const string VERSION = "1.8.21";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.cppexplorer"; public const string GUID = "com.sinai.explorer";
public static CppExplorer Instance { get; private set; } public const string PLATFORM =
#if CPP
"Il2Cpp";
#else
"Mono";
#endif
public const string MODLOADER =
#if ML
"MelonLoader";
#else
"BepInEx";
#endif
public static ExplorerCore Instance { get; private set; }
public ExplorerCore()
{
Instance = this;
ModConfig.OnLoad();
new MainMenu();
new WindowManager();
InputHelper.Init();
CursorControl.Init();
Log($"{NAME} {VERSION} initialized.");
}
public static bool ShowMenu public static bool ShowMenu
{ {
@ -32,29 +52,7 @@ namespace Explorer
CursorControl.UpdateCursorControl(); CursorControl.UpdateCursorControl();
} }
public override void OnApplicationStart() public static void Update()
{
Instance = this;
ModConfig.OnLoad();
InputHelper.Init();
new MainMenu();
new WindowManager();
CursorControl.Init();
MelonLogger.Log($"CppExplorer {VERSION} initialized.");
}
public override void OnLevelWasLoaded(int level)
{
ScenePage.Instance?.OnSceneChange();
SearchPage.Instance?.OnSceneChange();
}
public override void OnUpdate()
{ {
if (InputHelper.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle)) if (InputHelper.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
{ {
@ -71,7 +69,7 @@ namespace Explorer
} }
} }
public override void OnGUI() public static void OnGUI()
{ {
if (!ShowMenu) return; if (!ShowMenu) return;
@ -84,5 +82,38 @@ namespace Explorer
GUI.skin = origSkin; GUI.skin = origSkin;
} }
public static void OnSceneChange()
{
ScenePage.Instance?.OnSceneChange();
SearchPage.Instance?.OnSceneChange();
}
public static void Log(string message)
{
#if ML
MelonLoader.MelonLogger.Log(message);
#else
ExplorerBepInPlugin.Logging?.LogMessage(message);
#endif
}
public static void LogWarning(string message)
{
#if ML
MelonLoader.MelonLogger.LogWarning(message);
#else
ExplorerBepInPlugin.Logging?.LogWarning(message);
#endif
}
public static void LogError(string message)
{
#if ML
MelonLoader.MelonLogger.LogError(message);
#else
ExplorerBepInPlugin.Logging?.LogError(message);
#endif
}
} }
} }

37
src/ExplorerMelonMod.cs Normal file
View File

@ -0,0 +1,37 @@
#if ML
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader;
namespace Explorer
{
public class ExplorerMelonMod : MelonMod
{
public static ExplorerMelonMod Instance;
public override void OnApplicationStart()
{
Instance = this;
new ExplorerCore();
}
public override void OnLevelWasLoaded(int level)
{
ExplorerCore.OnSceneChange();
}
public override void OnUpdate()
{
ExplorerCore.Update();
}
public override void OnGUI()
{
ExplorerCore.OnGUI();
}
}
}
#endif

View File

@ -1,26 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
namespace Explorer namespace Explorer
{ {
public static class ReflectionExtensions public static class ReflectionExtensions
{ {
/// <summary> #if CPP
/// Extension to allow for easy, non-generic Il2Cpp casting.
/// The extension is on System.Object, but only Il2Cpp objects would be a valid target.
/// </summary>
public static object Il2CppCast(this object obj, Type castTo) public static object Il2CppCast(this object obj, Type castTo)
{ {
return ReflectionHelpers.Il2CppCast(obj, castTo); return ReflectionHelpers.Il2CppCast(obj, castTo);
} }
#endif
/// <summary>
/// Extension to safely try to get all Types from an Assembly, with a fallback for ReflectionTypeLoadException.
/// </summary>
public static IEnumerable<Type> TryGetTypes(this Assembly asm) public static IEnumerable<Type> TryGetTypes(this Assembly asm)
{ {
try try
@ -29,7 +22,14 @@ namespace Explorer
} }
catch (ReflectionTypeLoadException e) catch (ReflectionTypeLoadException e)
{ {
return e.Types.Where(t => t != null); try
{
return asm.GetExportedTypes();
}
catch
{
return e.Types.Where(t => t != null);
}
} }
catch catch
{ {

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,28 +1,45 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using MelonLoader;
namespace Explorer namespace Explorer
{ {
/// <summary> /// <summary>
/// Version-agnostic UnityEngine Input module using Reflection. /// Version-agnostic Input module using Reflection.
/// </summary> /// </summary>
public static class InputHelper public static class InputHelper
{ {
// If Input module failed to load at all // If Input module failed to load at all
public static bool NO_INPUT; public static bool NO_INPUT;
// Base UnityEngine.Input class // If using new InputSystem module
private static Type Input => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input")); public static bool USING_NEW_INPUT;
// Cached Types
private static Type TInput => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
private static Type _input; private static Type _input;
// Cached member infos private static Type TKeyboard => _keyboardSys ?? (_keyboardSys = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
private static Type _keyboardSys;
private static Type TMouse => _mouseSys ?? (_mouseSys = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Mouse"));
private static Type _mouseSys;
private static Type TKey => _key ?? (_key = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Key"));
private static Type _key;
// Cached member infos (new system)
private static PropertyInfo _keyboardCurrent;
private static PropertyInfo _kbItemProp;
private static PropertyInfo _isPressed;
private static PropertyInfo _wasPressedThisFrame;
private static PropertyInfo _mouseCurrent;
private static PropertyInfo _leftButton;
private static PropertyInfo _rightButton;
private static PropertyInfo _position;
private static MethodInfo _readValueMethod;
// Cached member infos (legacy)
private static PropertyInfo _mousePosition; private static PropertyInfo _mousePosition;
private static MethodInfo _getKey; private static MethodInfo _getKey;
private static MethodInfo _getKeyDown; private static MethodInfo _getKeyDown;
@ -31,49 +48,153 @@ namespace Explorer
public static void Init() public static void Init()
{ {
if (Input == null && !TryManuallyLoadInput()) if (TKeyboard != null || TryManuallyLoadNewInput())
{ {
NO_INPUT = true; InitNewInput();
return; return;
} }
// Cache reflection now that we know Input is loaded if (TInput != null || TryManuallyLoadLegacyInput())
{
InitLegacyInput();
return;
}
_mousePosition = Input.GetProperty("mousePosition"); ExplorerCore.LogWarning("Could not find any Input module!");
NO_INPUT = true;
_getKey = Input.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
_getKeyDown = Input.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
_getMouseButton = Input.GetMethod("GetMouseButton", new Type[] { typeof(int) });
_getMouseButtonDown = Input.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
} }
#pragma warning disable IDE1006 // Camel-case property (Unity style) private static void InitNewInput()
public static Vector3 mousePosition {
ExplorerCore.Log("Initializing new InputSystem support...");
USING_NEW_INPUT = true;
_keyboardCurrent = TKeyboard.GetProperty("current");
_kbItemProp = TKeyboard.GetProperty("Item", new Type[] { TKey });
var btnControl = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl");
_isPressed = btnControl.GetProperty("isPressed");
_wasPressedThisFrame = btnControl.GetProperty("wasPressedThisFrame");
_mouseCurrent = TMouse.GetProperty("current");
_leftButton = TMouse.GetProperty("leftButton");
_rightButton = TMouse.GetProperty("rightButton");
_position = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Pointer")
.GetProperty("position");
_readValueMethod = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
.MakeGenericType(typeof(Vector2))
.GetMethod("ReadValue");
}
private static void InitLegacyInput()
{
ExplorerCore.Log("Initializing Legacy Input support...");
_mousePosition = TInput.GetProperty("mousePosition");
_getKey = TInput.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
_getKeyDown = TInput.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
_getMouseButton = TInput.GetMethod("GetMouseButton", new Type[] { typeof(int) });
_getMouseButtonDown = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
}
private static bool TryManuallyLoadNewInput()
{
if (ReflectionHelpers.LoadModule("Unity.InputSystem") && TKeyboard != null)
{
ExplorerCore.Log("Loaded new InputSystem module!");
return true;
}
else
{
return false;
}
}
private static bool TryManuallyLoadLegacyInput()
{
if ((ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") || ReflectionHelpers.LoadModule("UnityEngine.CoreModule"))
&& TInput != null)
{
ExplorerCore.Log("Loaded legacy InputModule!");
return true;
}
else
{
return false;
}
}
public static Vector3 MousePosition
{ {
get get
{ {
if (NO_INPUT) return Vector3.zero; if (NO_INPUT) return Vector3.zero;
return (Vector3)_mousePosition.GetValue(null);
if (USING_NEW_INPUT)
{
var mouse = _mouseCurrent.GetValue(null, null);
var pos = _position.GetValue(mouse, null);
return (Vector2)_readValueMethod.Invoke(pos, new object[0]);
}
return (Vector3)_mousePosition.GetValue(null, null);
} }
} }
#pragma warning restore IDE1006
public static bool GetKeyDown(KeyCode key) public static bool GetKeyDown(KeyCode key)
{ {
if (NO_INPUT) return false; if (NO_INPUT) return false;
if (USING_NEW_INPUT)
{
var parsed = Enum.Parse(TKey, key.ToString());
var currentKB = _keyboardCurrent.GetValue(null, null);
var actualKey = _kbItemProp.GetValue(currentKB, new object[] { parsed });
return (bool)_wasPressedThisFrame.GetValue(actualKey, null);
}
return (bool)_getKeyDown.Invoke(null, new object[] { key }); return (bool)_getKeyDown.Invoke(null, new object[] { key });
} }
public static bool GetKey(KeyCode key) public static bool GetKey(KeyCode key)
{ {
if (NO_INPUT) return false; if (NO_INPUT) return false;
if (USING_NEW_INPUT)
{
var parsed = Enum.Parse(TKey, key.ToString());
var currentKB = _keyboardCurrent.GetValue(null, null);
var actualKey = _kbItemProp.GetValue(currentKB, new object[] { parsed });
return (bool)_isPressed.GetValue(actualKey, null);
}
return (bool)_getKey.Invoke(null, new object[] { key }); return (bool)_getKey.Invoke(null, new object[] { key });
} }
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param> /// <param name="btn">0/1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButtonDown(int btn) public static bool GetMouseButtonDown(int btn)
{ {
if (NO_INPUT) return false; if (NO_INPUT) return false;
if (USING_NEW_INPUT)
{
var mouse = _mouseCurrent.GetValue(null, null);
PropertyInfo btnProp;
if (btn < 2) btnProp = _leftButton;
else btnProp = _rightButton;
var actualBtn = btnProp.GetValue(mouse, null);
return (bool)_wasPressedThisFrame.GetValue(actualBtn, null);
}
return (bool)_getMouseButtonDown.Invoke(null, new object[] { btn }); return (bool)_getMouseButtonDown.Invoke(null, new object[] { btn });
} }
@ -81,24 +202,21 @@ namespace Explorer
public static bool GetMouseButton(int btn) public static bool GetMouseButton(int btn)
{ {
if (NO_INPUT) return false; if (NO_INPUT) return false;
if (USING_NEW_INPUT)
{
var mouse = _mouseCurrent.GetValue(null, null);
PropertyInfo btnProp;
if (btn < 2) btnProp = _leftButton;
else btnProp = _rightButton;
var actualBtn = btnProp.GetValue(mouse, null);
return (bool)_isPressed.GetValue(actualBtn, null);
}
return (bool)_getMouseButton.Invoke(null, new object[] { btn }); return (bool)_getMouseButton.Invoke(null, new object[] { btn });
} }
private static bool TryManuallyLoadInput()
{
MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
if ((ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule.dll") || ReflectionHelpers.LoadModule("UnityEngine.CoreModule.dll"))
&& Input != null)
{
MelonLogger.Log("Ok!");
return true;
}
else
{
MelonLogger.Log("Could not load Input module!");
return false;
}
}
} }
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -26,7 +21,7 @@ namespace Explorer
CalculateMaxOffset(); CalculateMaxOffset();
} }
} }
private int m_itemsPerPage = 20; private int m_itemsPerPage = ModConfig.Instance.Default_Page_Limit;
public int ItemCount public int ItemCount
{ {
@ -99,7 +94,7 @@ namespace Explorer
{ {
GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) });
var limit = this.ItemsPerPage.ToString(); var limit = this.ItemsPerPage.ToString();
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) }); limit = GUIUnstrip.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) });
if (limit != ItemsPerPage.ToString() && int.TryParse(limit, out int i)) if (limit != ItemsPerPage.ToString() && int.TryParse(limit, out int i))
{ {
ItemsPerPage = i; ItemsPerPage = i;

View File

@ -1,15 +1,16 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.IO; using System.IO;
using MelonLoader; using System.Reflection;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
#if CPP
using ILType = Il2CppSystem.Type; using ILType = Il2CppSystem.Type;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
#endif
namespace Explorer namespace Explorer
{ {
@ -17,31 +18,43 @@ namespace Explorer
{ {
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
public static ILType GameObjectType => Il2CppType.Of<GameObject>(); #if CPP
public static ILType TransformType => Il2CppType.Of<Transform>(); public static ILType GameObjectType => Il2CppType.Of<GameObject>();
public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>(); public static ILType TransformType => Il2CppType.Of<Transform>();
public static ILType ComponentType => Il2CppType.Of<Component>(); public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static ILType BehaviourType => Il2CppType.Of<Behaviour>(); public static ILType ComponentType => Il2CppType.Of<Component>();
public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast"); private static readonly MethodInfo tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
private static readonly Dictionary<Type, MethodInfo> cachedTryCastMethods = new Dictionary<Type, MethodInfo>();
public static object Il2CppCast(object obj, Type castTo) public static object Il2CppCast(object obj, Type castTo)
{ {
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj; if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
return m_tryCastMethodInfo if (!cachedTryCastMethods.ContainsKey(castTo))
.MakeGenericMethod(castTo) {
.Invoke(obj, null); cachedTryCastMethods.Add(castTo, tryCastMethodInfo.MakeGenericMethod(castTo));
}
return cachedTryCastMethods[castTo].Invoke(obj, null);
} }
#else
public static Type GameObjectType => typeof(GameObject);
public static Type TransformType => typeof(Transform);
public static Type ObjectType => typeof(UnityEngine.Object);
public static Type ComponentType => typeof(Component);
public static Type BehaviourType => typeof(Behaviour);
#endif
public static bool IsEnumerable(Type t) public static bool IsEnumerable(Type t)
{ {
return typeof(IEnumerable).IsAssignableFrom(t) || IsCppEnumerable(t); if (typeof(IEnumerable).IsAssignableFrom(t))
} {
return true;
}
// Checks for Il2Cpp List or HashSet. #if CPP
public static bool IsCppEnumerable(Type t)
{
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{ {
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g) return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
@ -52,6 +65,9 @@ namespace Explorer
{ {
return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t); return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
} }
#else
return false;
#endif
} }
public static bool IsDictionary(Type t) public static bool IsDictionary(Type t)
@ -61,6 +77,7 @@ namespace Explorer
return true; return true;
} }
#if CPP
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{ {
return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g) return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g)
@ -71,6 +88,9 @@ namespace Explorer
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t) return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t)
|| typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t); || typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t);
} }
#else
return false;
#endif
} }
public static Type GetTypeByName(string fullName) public static Type GetTypeByName(string fullName)
@ -93,6 +113,7 @@ namespace Explorer
{ {
if (obj == null) return null; if (obj == null) return null;
#if CPP
// Need to use GetIl2CppType for Il2CppSystem Objects // Need to use GetIl2CppType for Il2CppSystem Objects
if (obj is Il2CppSystem.Object ilObject) if (obj is Il2CppSystem.Object ilObject)
{ {
@ -104,6 +125,7 @@ namespace Explorer
return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType(); return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType();
} }
#endif
// It's a normal object, this is fine // It's a normal object, this is fine
return obj.GetType(); return obj.GetType();
@ -126,7 +148,12 @@ namespace Explorer
public static bool LoadModule(string module) public static bool LoadModule(string module)
{ {
var path = $@"MelonLoader\Managed\{module}"; #if CPP
#if ML
var path = $@"MelonLoader\Managed\{module}.dll";
#else
var path = $@"BepInEx\unhollowed\{module}.dll";
#endif
if (!File.Exists(path)) return false; if (!File.Exists(path)) return false;
try try
@ -136,13 +163,15 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log(e.GetType() + ", " + e.Message); ExplorerCore.Log(e.GetType() + ", " + e.Message);
return false;
} }
#endif
return false;
} }
public static string ExceptionToString(Exception e) public static string ExceptionToString(Exception e)
{ {
#if CPP
if (IsFailedGeneric(e)) if (IsFailedGeneric(e))
{ {
return "Unable to initialize this type."; return "Unable to initialize this type.";
@ -151,10 +180,11 @@ namespace Explorer
{ {
return "Garbage collected in Il2Cpp."; return "Garbage collected in Il2Cpp.";
} }
#endif
return e.GetType() + ", " + e.Message; return e.GetType() + ", " + e.Message;
} }
#if CPP
public static bool IsFailedGeneric(Exception e) public static bool IsFailedGeneric(Exception e)
{ {
return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException)); return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException));
@ -181,5 +211,6 @@ namespace Explorer
else else
return false; return false;
} }
#endif
} }
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,12 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
#if ML
using Harmony; using Harmony;
using MelonLoader; #else
using System.Reflection; using HarmonyLib;
#endif
namespace Explorer namespace Explorer
{ {
@ -22,7 +20,7 @@ namespace Explorer
private static bool m_lastVisibleState; private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false; private static bool m_currentlySettingCursor = false;
public static bool ShouldForceMouse => CppExplorer.ShowMenu && ForceUnlockMouse; public static bool ShouldForceMouse => ExplorerCore.ShowMenu && ForceUnlockMouse;
private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor")); private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static Type m_cursorType; private static Type m_cursorType;
@ -34,11 +32,11 @@ namespace Explorer
// Check if Cursor class is loaded // Check if Cursor class is loaded
if (CursorType == null) if (CursorType == null)
{ {
MelonLogger.Log("Trying to manually load Cursor module..."); ExplorerCore.Log("Trying to manually load Cursor module...");
if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule") && CursorType != null) if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule") && CursorType != null)
{ {
MelonLogger.Log("Ok!"); ExplorerCore.Log("Ok!");
} }
else else
{ {
@ -59,12 +57,12 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}"); ExplorerCore.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}");
} }
// Enable ShowMenu and ForceUnlockMouse // Enable ShowMenu and ForceUnlockMouse
// (set m_showMenu directly to not call UpdateCursorState twice) // (set m_showMenu directly to not call UpdateCursorState twice)
CppExplorer.m_showMenu = true; ExplorerCore.m_showMenu = true;
ForceUnlockMouse = true; ForceUnlockMouse = true;
} }
@ -72,7 +70,16 @@ namespace Explorer
{ {
try try
{ {
var harmony = CppExplorer.Instance.harmonyInstance; // var harmony = ExplorerCore.Instance.harmonyInstance;
var harmony =
#if ML
ExplorerMelonMod.Instance.harmonyInstance;
#else
ExplorerBepInPlugin.HarmonyInstance;
#endif
;
var prop = typeof(Cursor).GetProperty(property); var prop = typeof(Cursor).GetProperty(property);
if (setter) if (setter)
@ -88,7 +95,7 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}"); ExplorerCore.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}");
} }
} }
@ -126,7 +133,7 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}"); ExplorerCore.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}");
} }
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -15,7 +10,7 @@ namespace Explorer
public static void Update() public static void Update()
{ {
if (CppExplorer.ShowMenu) if (ExplorerCore.ShowMenu)
{ {
if (InputHelper.GetKey(KeyCode.LeftShift) && InputHelper.GetMouseButtonDown(1)) if (InputHelper.GetKey(KeyCode.LeftShift) && InputHelper.GetMouseButtonDown(1))
{ {
@ -38,7 +33,7 @@ namespace Explorer
if (!UnityHelpers.MainCamera) if (!UnityHelpers.MainCamera)
return; return;
var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputHelper.mousePosition); var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputHelper.MousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f)) if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{ {
@ -66,7 +61,7 @@ namespace Explorer
{ {
if (m_objUnderMouseName != "") if (m_objUnderMouseName != "")
{ {
var pos = InputHelper.mousePosition; var pos = InputHelper.MousePosition;
var rect = new Rect( var rect = new Rect(
pos.x - (Screen.width / 2), // x pos.x - (Screen.width / 2), // x
Screen.height - pos.y - 50, // y Screen.height - pos.y - 50, // y

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using MelonLoader;
namespace Explorer namespace Explorer
{ {
@ -18,16 +17,20 @@ namespace Explorer
Pages.Add(new ScenePage()); Pages.Add(new ScenePage());
Pages.Add(new SearchPage()); Pages.Add(new SearchPage());
Pages.Add(new ConsolePage()); Pages.Add(new ConsolePage());
Pages.Add(new OptionsPage());
for (int i = 0; i < Pages.Count; i++) for (int i = 0; i < Pages.Count; i++)
{ {
var page = Pages[i]; var page = Pages[i];
page.Init(); page.Init();
// If page failed to init, it will remove itself from the list. Lower the iterate counter.
if (!Pages.Contains(page)) i--;
} }
} }
public const int MainWindowID = 5000; public const int MainWindowID = 5000;
public static Rect MainRect = new Rect(5,5, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y); public static Rect MainRect = new Rect(5, 5, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y);
public static readonly List<WindowPage> Pages = new List<WindowPage>(); public static readonly List<WindowPage> Pages = new List<WindowPage>();
private static int m_currentPage = 0; private static int m_currentPage = 0;
@ -36,11 +39,11 @@ namespace Explorer
{ {
if (index < 0 || Pages.Count <= index) if (index < 0 || Pages.Count <= index)
{ {
MelonLogger.Log("cannot set page " + index); ExplorerCore.Log("cannot set page " + index);
return; return;
} }
m_currentPage = index; m_currentPage = index;
GUI.BringWindowToFront(MainWindowID); GUIUnstrip.BringWindowToFront(MainWindowID);
GUI.FocusWindow(MainWindowID); GUI.FocusWindow(MainWindowID);
} }
@ -51,16 +54,16 @@ namespace Explorer
public void OnGUI() public void OnGUI()
{ {
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME); MainRect = GUIUnstrip.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, ExplorerCore.NAME);
} }
private void MainWindow(int id) private void MainWindow(int id)
{ {
GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20)); GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20));
if (GUI.Button(new Rect(MainRect.width - 90, 2, 80, 20), $"Hide ({ModConfig.Instance.Main_Menu_Toggle})")) if (GUIUnstrip.Button(new Rect(MainRect.width - 90, 2, 80, 20), $"Hide ({ModConfig.Instance.Main_Menu_Toggle})"))
{ {
CppExplorer.ShowMenu = false; ExplorerCore.ShowMenu = false;
return; return;
} }
@ -83,7 +86,7 @@ namespace Explorer
private void MainHeader() private void MainHeader()
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
for (int i = 0; i < Pages.Count; i++) for (int i = 0; i < Pages.Count; i++)
{ {
if (m_currentPage == i) if (m_currentPage == i)
@ -91,22 +94,22 @@ namespace Explorer
else else
GUI.color = Color.white; GUI.color = Color.white;
if (GUILayout.Button(Pages[i].Name, null)) if (GUILayout.Button(Pages[i].Name, new GUILayoutOption[0]))
{ {
m_currentPage = i; m_currentPage = i;
} }
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.color = Color.white; GUI.color = Color.white;
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null); InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", new GUILayoutOption[0]);
bool mouseState = CursorControl.ForceUnlockMouse; bool mouseState = CursorControl.ForceUnlockMouse;
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null); bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", new GUILayoutOption[0]);
if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse; if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse;
WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", null); WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
//GUIUnstrip.Space(10); //GUIUnstrip.Space(10);

View File

@ -1,9 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Mono.CSharp; using Mono.CSharp;
using UnityEngine; using UnityEngine;
using Attribute = System.Attribute; using Attribute = System.Attribute;

View File

@ -1,15 +1,14 @@
using System.Collections; using System;
//using Il2CppSystem;
using MelonLoader;
using UnityEngine; using UnityEngine;
using System;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Explorer namespace Explorer
{ {
public class ReplHelper : MonoBehaviour public class ReplHelper : MonoBehaviour
{ {
#if CPP
public ReplHelper(IntPtr intPtr) : base(intPtr) { } public ReplHelper(IntPtr intPtr) : base(intPtr) { }
#endif
public T Find<T>() where T : Object public T Find<T>() where T : Object
{ {

View File

@ -1,14 +1,10 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; 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 System.IO;
using MelonLoader; using System.Reflection;
using System.Text;
using Mono.CSharp;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -34,20 +30,28 @@ namespace Explorer
"System.Collections", "System.Collections",
"System.Collections.Generic", "System.Collections.Generic",
"System.Reflection", "System.Reflection",
#if ML
"MelonLoader" "MelonLoader"
#endif
}; };
public override void Init() public override void Init()
{ {
#if CPP
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<ReplHelper>(); UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<ReplHelper>();
#endif
try try
{ {
MethodInput = @"// This is a basic C# REPL console. MethodInput = @"// This is a basic C# console.
// Some common using directives are added by default, you can add more below. // Some common using directives are added by default, you can add more below.
// If you want to return some output, MelonLogger.Log() it. // If you want to return some output, Debug.Log() or MelonLogger.Log() it.
"
MelonLogger.Log(""hello world"");"; #if ML
+ @"MelonLogger.Log(""hello world"");";
#else
+ @"Debug.Log(""hello world"");";
#endif
;
ResetConsole(); ResetConsole();
@ -58,7 +62,7 @@ MelonLogger.Log(""hello world"");";
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}"); ExplorerCore.Log($"Error setting up console!\r\nMessage: {e.Message}");
MainMenu.SetCurrentPage(0); MainMenu.SetCurrentPage(0);
MainMenu.Pages.Remove(this); MainMenu.Pages.Remove(this);
} }
@ -113,7 +117,7 @@ MelonLogger.Log(""hello world"");";
{ {
if (!suppressWarning) if (!suppressWarning)
{ {
MelonLogger.LogWarning(e.GetType() + ", " + e.Message); ExplorerCore.LogWarning(e.GetType() + ", " + e.Message);
} }
} }
@ -123,19 +127,19 @@ MelonLogger.Log(""hello world"");";
public override void DrawWindow() public override void DrawWindow()
{ {
GUILayout.Label("<b><size=15><color=cyan>C# REPL Console</color></size></b>", null); GUILayout.Label("<b><size=15><color=cyan>C# Console</color></size></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.Label("Enter code here as though it is a method body:", null); GUILayout.Label("Enter code here as though it is a method body:", new GUILayoutOption[0]);
inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) }); inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) });
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) }); MethodInput = GUIUnstrip.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) });
GUIUnstrip.EndScrollView(); GUIUnstrip.EndScrollView();
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null)) if (GUILayout.Button("<color=cyan><b>Execute</b></color>", new GUILayoutOption[0]))
{ {
try try
{ {
@ -147,21 +151,21 @@ MelonLogger.Log(""hello world"");";
if (result != null && !Equals(result, VoidType.Value)) if (result != null && !Equals(result, VoidType.Value))
{ {
MelonLogger.Log("[Console Output]\r\n" + result.ToString()); ExplorerCore.Log("[Console Output]\r\n" + result.ToString());
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace); ExplorerCore.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
} }
} }
GUILayout.Label("<b>Using directives:</b>", null); GUILayout.Label("<b>Using directives:</b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) }); GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) });
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) }); UsingInput = GUIUnstrip.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("<b><color=lime>Add</color></b>", new GUILayoutOption[] { GUILayout.Width(120) })) if (GUILayout.Button("<b><color=lime>Add</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{ {
AddUsing(UsingInput); AddUsing(UsingInput);
@ -174,7 +178,7 @@ MelonLogger.Log(""hello world"");";
foreach (var asm in UsingDirectives) foreach (var asm in UsingDirectives)
{ {
GUILayout.Label(AsmToUsing(asm, true), null); GUILayout.Label(AsmToUsing(asm, true), new GUILayoutOption[0]);
} }
} }

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.Tests;
using UnityEngine;
namespace Explorer
{
public class OptionsPage : WindowPage
{
public override string Name => "Options";
public string toggleKeyInputString = "";
public Vector2 defaultSizeInputVector;
public int defaultPageLimit;
private CacheObjectBase toggleKeyInput;
private CacheObjectBase defaultSizeInput;
private CacheObjectBase defaultPageLimitInput;
public override void Init()
{
toggleKeyInputString = ModConfig.Instance.Main_Menu_Toggle.ToString();
toggleKeyInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("toggleKeyInputString"), this);
defaultSizeInputVector = ModConfig.Instance.Default_Window_Size;
defaultSizeInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultSizeInputVector"), this);
defaultPageLimit = ModConfig.Instance.Default_Page_Limit;
defaultPageLimitInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultPageLimit"), this);
}
public override void Update() { }
public override void DrawWindow()
{
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<color=orange><size=16><b>Settings</b></size></color>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Menu Toggle Key:", new GUILayoutOption[] { GUILayout.Width(215f) });
toggleKeyInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultSizeInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultPageLimitInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
if (GUILayout.Button("<color=lime><b>Apply and Save</b></color>", new GUILayoutOption[0]))
{
ApplyAndSave();
}
GUILayout.EndVertical();
//GUIUnstrip.Space(10f);
//GUI.skin.label.alignment = TextAnchor.MiddleCenter;
//GUILayout.Label("<color=orange><size=16><b>Other</b></size></color>", new GUILayoutOption[0]);
//GUI.skin.label.alignment = TextAnchor.MiddleLeft;
//GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
//if (GUILayout.Button("Inspect Test Class", new GUILayoutOption[0]))
//{
// WindowManager.InspectObject(TestClass.Instance, out bool _);
//}
//GUILayout.EndVertical();
}
private void ApplyAndSave()
{
if (Enum.Parse(typeof(KeyCode), toggleKeyInputString) is KeyCode key)
{
ModConfig.Instance.Main_Menu_Toggle = key;
}
else
{
ExplorerCore.LogWarning($"Could not parse '{toggleKeyInputString}' to KeyCode!");
}
ModConfig.Instance.Default_Window_Size = defaultSizeInputVector;
ModConfig.Instance.Default_Page_Limit = defaultPageLimit;
ModConfig.SaveSettings();
}
}
}

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using MelonLoader;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
@ -12,7 +10,7 @@ namespace Explorer
{ {
public static ScenePage Instance; public static ScenePage Instance;
public override string Name { get => "Scene Explorer"; } public override string Name { get => "Scenes"; }
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
@ -88,7 +86,11 @@ namespace Explorer
foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType)) foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType))
{ {
#if CPP
var go = obj.TryCast<GameObject>(); var go = obj.TryCast<GameObject>();
#else
var go = obj as GameObject;
#endif
if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene) if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene)
{ {
matches.Add(new GameObjectCache(go)); matches.Add(new GameObjectCache(go));
@ -126,14 +128,22 @@ namespace Explorer
{ {
try try
{ {
var scene = SceneManager.GetSceneByName(m_currentScene); for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
allTransforms.AddRange(scene.GetRootGameObjects() if (scene.name == m_currentScene)
.Select(it => it.transform)); {
allTransforms.AddRange(scene.GetRootGameObjects()
.Select(it => it.transform));
break;
}
}
} }
catch catch
{ {
MelonLogger.Log("Exception getting root scene objects, falling back to backup method..."); ExplorerCore.Log("Exception getting root scene objects, falling back to backup method...");
m_getRootObjectsFailed = true; m_getRootObjectsFailed = true;
allTransforms.AddRange(GetRootObjectsManual_Impl()); allTransforms.AddRange(GetRootObjectsManual_Impl());
@ -175,7 +185,11 @@ namespace Explorer
var list = new List<Transform>(); var list = new List<Transform>();
foreach (var obj in array) foreach (var obj in array)
{ {
#if CPP
var transform = obj.TryCast<Transform>(); var transform = obj.TryCast<Transform>();
#else
var transform = obj as Transform;
#endif
if (transform.parent == null && transform.gameObject.scene.name == m_currentScene) if (transform.parent == null && transform.gameObject.scene.name == m_currentScene)
{ {
list.Add(transform); list.Add(transform);
@ -185,7 +199,7 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception getting root scene objects (manual): " ExplorerCore.Log("Exception getting root scene objects (manual): "
+ e.GetType() + ", " + e.Message + "\r\n" + e.GetType() + ", " + e.Message + "\r\n"
+ e.StackTrace); + e.StackTrace);
return new Transform[0]; return new Transform[0];
@ -200,7 +214,7 @@ namespace Explorer
{ {
DrawHeaderArea(); DrawHeaderArea();
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
DrawPageButtons(); DrawPageButtons();
@ -215,28 +229,31 @@ namespace Explorer
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
catch catch (Exception e)
{ {
// supress if (!e.Message.Contains("in a group with only"))
{
ExplorerCore.Log(e.ToString());
}
} }
} }
private void DrawHeaderArea() private void DrawHeaderArea()
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
// Current Scene label // Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
SceneChangeButtons(); SceneChangeButtons();
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) }); GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// ----- GameObject Search ----- // ----- GameObject Search -----
GUILayout.BeginHorizontal(GUI.skin.box, null); GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, null); m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[0]);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
@ -249,8 +266,14 @@ namespace Explorer
private void SceneChangeButtons() private void SceneChangeButtons()
{ {
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC. var scenes = new List<Scene>();
var scenes = SceneManager.GetAllScenes().ToList(); var names = new List<string>();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
names.Add(scene.name);
scenes.Add(scene);
}
if (scenes.Count > 1) if (scenes.Count > 1)
{ {
@ -265,7 +288,7 @@ namespace Explorer
} }
if (changeWanted != 0) if (changeWanted != 0)
{ {
int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene)); int index = names.IndexOf(m_currentScene);
index += changeWanted; index += changeWanted;
if (index > scenes.Count - 1) if (index > scenes.Count - 1)
{ {
@ -282,7 +305,7 @@ namespace Explorer
private void DrawPageButtons() private void DrawPageButtons()
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
@ -313,7 +336,7 @@ namespace Explorer
{ {
if (m_currentTransform != null) if (m_currentTransform != null)
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{ {
TraverseUp(); TraverseUp();
@ -330,11 +353,11 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label("Scene Root GameObjects:", null); GUILayout.Label("Scene Root GameObjects:", new GUILayoutOption[0]);
if (m_getRootObjectsFailed) if (m_getRootObjectsFailed)
{ {
if (GUILayout.Button("Update Root Object List (auto-update failed!)", null)) if (GUILayout.Button("Update Root Object List (auto-update failed!)", new GUILayoutOption[0]))
{ {
Update_Impl(true); Update_Impl(true);
} }
@ -359,7 +382,7 @@ namespace Explorer
} }
label += "</i></color>"; label += "</i></color>";
GUILayout.Label(label, null); GUILayout.Label(label, new GUILayoutOption[0]);
} }
else else
{ {
@ -382,7 +405,7 @@ namespace Explorer
CancelSearch(); CancelSearch();
} }
GUILayout.Label("Search Results:", null); GUILayout.Label("Search Results:", new GUILayoutOption[0]);
if (m_searchResults.Count > 0) if (m_searchResults.Count > 0)
{ {
@ -404,13 +427,13 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label("<i><color=red>Null or destroyed!</color></i>", null); GUILayout.Label("<i><color=red>Null or destroyed!</color></i>", new GUILayoutOption[0]);
} }
} }
} }
else else
{ {
GUILayout.Label("<color=red><i>No results found!</i></color>", null); GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]);
} }
} }

View File

@ -2,10 +2,8 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using UnityEngine;
using System.Reflection; using System.Reflection;
using MelonLoader; using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -66,12 +64,19 @@ namespace Explorer
{ {
var toCache = obj; var toCache = obj;
#if CPP
if (toCache is Il2CppSystem.Object ilObject) if (toCache is Il2CppSystem.Object ilObject)
{ {
toCache = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Transform>()?.gameObject ?? ilObject; toCache = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Transform>()?.gameObject ?? ilObject;
} }
#else
if (toCache is GameObject || toCache is Transform)
{
toCache = toCache as GameObject ?? (toCache as Transform).gameObject;
}
#endif
var cache = CacheObjectBase.GetCacheObject(toCache); var cache = CacheFactory.GetTypeAndCacheObject(toCache);
m_searchResults.Add(cache); m_searchResults.Add(cache);
} }
@ -84,7 +89,7 @@ namespace Explorer
try try
{ {
// helpers // helpers
GUILayout.BeginHorizontal(GUI.skin.box, null); GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) }); GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) });
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) })) if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
{ {
@ -97,15 +102,15 @@ namespace Explorer
SearchBox(); SearchBox();
// results // results
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", null); GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
int count = m_searchResults.Count; int count = m_searchResults.Count;
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
@ -146,7 +151,7 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label("<color=red><i>No results found!</i></color>", null); GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]);
} }
GUIUnstrip.EndScrollView(); GUIUnstrip.EndScrollView();
@ -160,21 +165,21 @@ namespace Explorer
private void SearchBox() private void SearchBox()
{ {
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
// ----- GameObject Search ----- // ----- GameObject Search -----
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Search</color></b>", null); GUILayout.Label("<b><color=orange>Search</color></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) }); m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
ClassFilterToggle(TypeFilter.Object, "Object"); ClassFilterToggle(TypeFilter.Object, "Object");
@ -184,15 +189,15 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (TypeMode == TypeFilter.Custom) if (TypeMode == TypeFilter.Custom)
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight; GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) }); GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
m_typeInput = GUILayout.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) }); m_typeInput = GUIUnstrip.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60); SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100); SceneFilterToggle(SceneFilter.This, "This Scene", 100);
@ -200,7 +205,7 @@ namespace Explorer
SceneFilterToggle(SceneFilter.None, "No Scene", 80); SceneFilterToggle(SceneFilter.None, "No Scene", 80);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (GUILayout.Button("<b><color=cyan>Search</color></b>", null)) if (GUILayout.Button("<b><color=cyan>Search</color></b>", new GUILayoutOption[0]))
{ {
Search(); Search();
} }
@ -255,15 +260,23 @@ namespace Explorer
private List<object> FindAllObjectsOfType(string searchQuery, string typeName) private List<object> FindAllObjectsOfType(string searchQuery, string typeName)
{ {
#if CPP
Il2CppSystem.Type searchType = null; Il2CppSystem.Type searchType = null;
#else
Type searchType = null;
#endif
if (TypeMode == TypeFilter.Custom) if (TypeMode == TypeFilter.Custom)
{ {
try try
{ {
if (ReflectionHelpers.GetTypeByName(typeName) is Type t) if (ReflectionHelpers.GetTypeByName(typeName) is Type t)
{ {
#if CPP
searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName); searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
#else
searchType = t;
#endif
} }
else else
{ {
@ -272,7 +285,7 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception getting Search Type: " + e.GetType() + ", " + e.Message); ExplorerCore.Log("Exception getting Search Type: " + e.GetType() + ", " + e.Message);
} }
} }
else if (TypeMode == TypeFilter.Object) else if (TypeMode == TypeFilter.Object)
@ -292,7 +305,7 @@ namespace Explorer
{ {
if (searchType != null) if (searchType != null)
{ {
MelonLogger.LogWarning("Your Custom Class Type must inherit from UnityEngine.Object!"); ExplorerCore.LogWarning("Your Custom Class Type must inherit from UnityEngine.Object!");
} }
return new List<object>(); return new List<object>();
} }
@ -301,7 +314,7 @@ namespace Explorer
var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType); var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType);
//MelonLogger.Log("Found count: " + allObjectsOfType.Length); //ExplorerCore.Log("Found count: " + allObjectsOfType.Length);
int i = 0; int i = 0;
foreach (var obj in allObjectsOfType) foreach (var obj in allObjectsOfType)
@ -314,7 +327,11 @@ namespace Explorer
} }
if (searchType.FullName == ReflectionHelpers.ComponentType.FullName if (searchType.FullName == ReflectionHelpers.ComponentType.FullName
#if CPP
&& ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetIl2CppType())) && ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetIl2CppType()))
#else
&& ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetType()))
#endif
{ {
// Transforms shouldn't really be counted as Components, skip them. // Transforms shouldn't really be counted as Components, skip them.
// They're more akin to GameObjects. // They're more akin to GameObjects.
@ -339,15 +356,18 @@ namespace Explorer
public static bool FilterScene(object obj, SceneFilter filter) public static bool FilterScene(object obj, SceneFilter filter)
{ {
GameObject go; GameObject go = null;
#if CPP
if (obj is Il2CppSystem.Object ilObject) if (obj is Il2CppSystem.Object ilObject)
{ {
go = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Component>().gameObject; go = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Component>().gameObject;
} }
else #else
if (obj is GameObject || obj is Component)
{ {
go = (obj as GameObject) ?? (obj as Component).gameObject; go = (obj as GameObject) ?? (obj as Component).gameObject;
} }
#endif
if (!go) if (!go)
{ {
@ -405,7 +425,7 @@ namespace Explorer
if (pi != null) if (pi != null)
{ {
obj = pi.GetValue(null); obj = pi.GetValue(null, null);
} }
else else
{ {

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,17 +1,16 @@
using System; using System;
using System.Collections.Generic; #if CPP
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
#endif
using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class ResizeDrag public class ResizeDrag
{ {
#if CPP
private static bool RESIZE_FAILED = false; private static bool RESIZE_FAILED = false;
#endif
private static readonly GUIContent gcDrag = new GUIContent("<-- Drag to resize -->"); private static readonly GUIContent gcDrag = new GUIContent("<-- Drag to resize -->");
private static bool isResizing = false; private static bool isResizing = false;
@ -20,25 +19,30 @@ namespace Explorer
public static Rect ResizeWindow(Rect _rect, int ID) public static Rect ResizeWindow(Rect _rect, int ID)
{ {
#if CPP
if (!RESIZE_FAILED) if (!RESIZE_FAILED)
{ {
var origRect = _rect; var origRect = _rect;
try try
{ {
GUILayout.BeginHorizontal(GUI.skin.box, null); GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
#if ML
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) }); GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#else
GUILayout.Button(gcDrag.ToString(), new GUILayoutOption[] { GUILayout.Height(15) });
#endif
//var r = GUILayoutUtility.GetLastRect(); //var r = GUILayoutUtility.GetLastRect();
var r = LayoutUtilityUnstrip.GetLastRect(); var r = Internal_LayoutUtility.GetLastRect();
var mousePos = InputHelper.mousePosition; var mousePos = InputHelper.MousePosition;
try try
{ {
var mouse = GUIUtility.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y)); var mouse = GUIUnstrip.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0)) if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
{ {
isResizing = true; isResizing = true;
@ -66,7 +70,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
catch (Il2CppException e) when (e.Message.StartsWith("System.ArgumentException")) catch (Exception e) when (e.Message.StartsWith("System.ArgumentException"))
{ {
// suppress // suppress
return origRect; return origRect;
@ -74,8 +78,8 @@ namespace Explorer
catch (Exception e) catch (Exception e)
{ {
RESIZE_FAILED = true; RESIZE_FAILED = true;
MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message); ExplorerCore.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message);
//MelonLogger.Log(e.StackTrace); //ExplorerCore.Log(e.StackTrace);
return origRect; return origRect;
} }
@ -83,7 +87,7 @@ namespace Explorer
} }
else else
{ {
GUILayout.BeginHorizontal(GUI.skin.box, null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Resize window:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Resize window:", new GUILayoutOption[] { GUILayout.Width(100) });
@ -111,6 +115,43 @@ namespace Explorer
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
} }
#else // mono
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
//var r = GUILayoutUtility.GetLastRect();
var r = GUILayoutUtility.GetLastRect();
var mousePos = InputHelper.MousePosition;
var mouse = GUIUnstrip.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!InputHelper.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();
#endif
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
return _rect; return _rect;
} }
} }

View File

@ -1,10 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -62,13 +56,13 @@ namespace Explorer
if (!obj) if (!obj)
{ {
GUILayout.Label("<i><color=red>null</color></i>", null); GUILayout.Label("<i><color=red>null</color></i>", new GUILayoutOption[0]);
return; return;
} }
// ------ toggle active button ------ // ------ toggle active button ------
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.button.alignment = TextAnchor.UpperLeft; GUI.skin.button.alignment = TextAnchor.UpperLeft;
GUI.color = activeColor; GUI.color = activeColor;
@ -108,7 +102,7 @@ namespace Explorer
public static void SmallInspectButton(object obj) public static void SmallInspectButton(object obj)
{ {
if (GUILayout.Button("Inspect", null)) if (GUILayout.Button("Inspect", new GUILayoutOption[0]))
{ {
WindowManager.InspectObject(obj, out bool _); WindowManager.InspectObject(obj, out bool _);
} }

View File

@ -1,16 +1,29 @@
using System; using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Explorer namespace Explorer
{ {
public class UIStyles public class UIStyles
{ {
public class Syntax
{
public const string Field_Static = "#8d8dc6";
public const string Field_Instance = "#c266ff";
public const string Method_Static = "#b55b02";
public const string Method_Instance = "#ff8000";
public const string Prop_Static = "#588075";
public const string Prop_Instance = "#55a38e";
public const string Class_Static = "#3a8d71";
public const string Class_Instance = "#2df7b2";
public const string Local = "#a6e9e9";
public const string StructGreen = "#92c470";
}
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f); 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 public static GUISkin WindowSkin

View File

@ -1,11 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
#if CPP
using UnhollowerRuntimeLib;
#endif
namespace Explorer namespace Explorer
{ {
@ -63,7 +62,7 @@ namespace Explorer
return true; return true;
} }
MelonLogger.Log("Error: Target is null or not a GameObject/Transform!"); ExplorerCore.Log("Error: Target is null or not a GameObject/Transform!");
DestroyWindow(); DestroyWindow();
return false; return false;
} }
@ -106,7 +105,7 @@ namespace Explorer
{ {
if (Target == null) if (Target == null)
{ {
MelonLogger.Log("Target is null!"); ExplorerCore.Log("Target is null!");
DestroyWindow(); DestroyWindow();
return; return;
} }
@ -114,7 +113,7 @@ namespace Explorer
{ {
if (!uObj) if (!uObj)
{ {
MelonLogger.Log("Target was destroyed!"); ExplorerCore.Log("Target was destroyed!");
DestroyWindow(); DestroyWindow();
return; return;
} }
@ -152,10 +151,13 @@ namespace Explorer
ChildPages.ItemCount = m_children.Length; ChildPages.ItemCount = m_children.Length;
// update components // update components
#if CPP
var compList = new Il2CppSystem.Collections.Generic.List<Component>(); var compList = new Il2CppSystem.Collections.Generic.List<Component>();
TargetGO.GetComponentsInternal(ReflectionHelpers.ComponentType, true, false, true, false, compList); TargetGO.GetComponentsInternal(ReflectionHelpers.ComponentType, true, false, true, false, compList);
m_components = compList.ToArray(); m_components = compList.ToArray();
#else
m_components = TargetGO.GetComponents<Component>();
#endif
CompPages.ItemCount = m_components.Length; CompPages.ItemCount = m_components.Length;
} }
@ -167,7 +169,7 @@ namespace Explorer
private void DestroyOnException(Exception e) private void DestroyOnException(Exception e)
{ {
MelonLogger.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}"); ExplorerCore.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}");
DestroyWindow(); DestroyWindow();
} }
@ -182,8 +184,12 @@ namespace Explorer
} }
} }
#if CPP
private void ReflectObject(Il2CppSystem.Object obj) private void ReflectObject(Il2CppSystem.Object obj)
{ #else
private void ReflectObject(object obj)
#endif
{
var window = WindowManager.InspectObject(obj, out bool created, true); var window = WindowManager.InspectObject(obj, out bool created, true);
if (created) if (created)
@ -217,8 +223,8 @@ namespace Explorer
scroll = GUIUnstrip.BeginScrollView(scroll); scroll = GUIUnstrip.BeginScrollView(scroll);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null); GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", new GUILayoutOption[0]);
if (m_scene == UnityHelpers.ActiveSceneName) if (m_scene == UnityHelpers.ActiveSceneName)
{ {
if (GUILayout.Button("<color=#00FF00>Send to Scene View</color>", new GUILayoutOption[] { GUILayout.Width(150) })) if (GUILayout.Button("<color=#00FF00>Send to Scene View</color>", new GUILayoutOption[] { GUILayout.Width(150) }))
@ -233,7 +239,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = TargetGO.transform.GetGameObjectPath(); string pathlabel = TargetGO.transform.GetGameObjectPath();
if (TargetGO.transform.parent != null) if (TargetGO.transform.parent != null)
@ -243,16 +249,16 @@ namespace Explorer
InspectGameObject(TargetGO.transform.parent); InspectGameObject(TargetGO.transform.parent);
} }
} }
GUILayout.TextArea(pathlabel, null); GUIUnstrip.TextArea(pathlabel, new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUILayout.TextArea(m_name, null); GUIUnstrip.TextArea(m_name, new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// --- Horizontal Columns section --- // --- Horizontal Columns section ---
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
TransformList(rect); TransformList(rect);
@ -283,12 +289,12 @@ namespace Explorer
private void TransformList(Rect m_rect) private void TransformList(Rect m_rect)
{ {
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll); m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll);
GUILayout.Label("<b><size=15>Children</size></b>", null); GUILayout.Label("<b><size=15>Children</size></b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
ChildPages.DrawLimitInputArea(); ChildPages.DrawLimitInputArea();
if (ChildPages.ItemCount > ChildPages.ItemsPerPage) if (ChildPages.ItemCount > ChildPages.ItemsPerPage)
@ -296,7 +302,7 @@ namespace Explorer
ChildPages.CurrentPageLabel(); ChildPages.CurrentPageLabel();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
@ -319,7 +325,7 @@ namespace Explorer
if (!obj) if (!obj)
{ {
GUILayout.Label("null", null); GUILayout.Label("null", new GUILayoutOption[0]);
continue; continue;
} }
@ -328,7 +334,7 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label("<i>None</i>", null); GUILayout.Label("<i>None</i>", new GUILayoutOption[0]);
} }
GUIUnstrip.EndScrollView(); GUIUnstrip.EndScrollView();
@ -337,11 +343,11 @@ namespace Explorer
private void ComponentList(Rect m_rect) private void ComponentList(Rect m_rect)
{ {
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll); m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll);
GUILayout.Label("<b><size=15>Components</size></b>", null); GUILayout.Label("<b><size=15>Components</size></b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
CompPages.DrawLimitInputArea(); CompPages.DrawLimitInputArea();
if (CompPages.ItemCount > CompPages.ItemsPerPage) if (CompPages.ItemCount > CompPages.ItemsPerPage)
@ -349,7 +355,7 @@ namespace Explorer
CompPages.CurrentPageLabel(); CompPages.CurrentPageLabel();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
@ -362,24 +368,29 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(130) }); var width = m_rect.width / 2 - 135f;
if (GUILayout.Button("Add Comp", null)) m_addComponentInput = GUIUnstrip.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
if (GUILayout.Button("Add Comp", new GUILayoutOption[0]))
{ {
if (ReflectionHelpers.GetTypeByName(m_addComponentInput) is Type compType) if (ReflectionHelpers.GetTypeByName(m_addComponentInput) is Type compType)
{ {
if (typeof(Component).IsAssignableFrom(compType)) if (typeof(Component).IsAssignableFrom(compType))
{ {
#if CPP
TargetGO.AddComponent(Il2CppType.From(compType)); TargetGO.AddComponent(Il2CppType.From(compType));
#else
TargetGO.AddComponent(compType);
#endif
} }
else else
{ {
MelonLogger.LogWarning($"Type '{compType.Name}' is not assignable from Component!"); ExplorerCore.LogWarning($"Type '{compType.Name}' is not assignable from Component!");
} }
} }
else else
{ {
MelonLogger.LogWarning($"Could not find a type by the name of '{m_addComponentInput}'!"); ExplorerCore.LogWarning($"Could not find a type by the name of '{m_addComponentInput}'!");
} }
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -400,18 +411,26 @@ namespace Explorer
if (!component) continue; if (!component) continue;
var ilType = component.GetIl2CppType(); var type =
#if CPP
GUILayout.BeginHorizontal(null); component.GetIl2CppType();
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType)) #else
component.GetType();
#endif
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(type))
{ {
#if CPP
BehaviourEnabledBtn(component.TryCast<Behaviour>()); BehaviourEnabledBtn(component.TryCast<Behaviour>());
#else
BehaviourEnabledBtn(component as Behaviour);
#endif
} }
else else
{ {
GUIUnstrip.Space(26); GUIUnstrip.Space(26);
} }
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) })) if (GUILayout.Button("<color=cyan>" + type.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) }))
{ {
ReflectObject(component); ReflectObject(component);
} }
@ -465,7 +484,7 @@ namespace Explorer
{ {
if (m_hideControls) if (m_hideControls)
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) }); GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) });
if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) })) if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) }))
{ {
@ -476,9 +495,9 @@ namespace Explorer
return; return;
} }
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) }); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) }); GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) });
if (GUILayout.Button("v Hide v", new GUILayoutOption[] { GUILayout.Width(75) })) if (GUILayout.Button("v Hide v", new GUILayoutOption[] { GUILayout.Width(75) }))
{ {
@ -486,7 +505,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
bool m_active = TargetGO.activeSelf; bool m_active = TargetGO.activeSelf;
m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>", m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>",
new GUILayoutOption[] { GUILayout.Width(80) }); new GUILayoutOption[] { GUILayout.Width(80) });
@ -511,9 +530,9 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
m_setParentInput = GUILayout.TextField(m_setParentInput, null); m_setParentInput = GUIUnstrip.TextField(m_setParentInput, new GUILayoutOption[0]);
if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
if (GameObject.Find(m_setParentInput) is GameObject newparent) if (GameObject.Find(m_setParentInput) is GameObject newparent)
@ -522,7 +541,7 @@ namespace Explorer
} }
else else
{ {
MelonLogger.LogWarning($"Could not find gameobject '{m_setParentInput}'"); ExplorerCore.LogWarning($"Could not find gameobject '{m_setParentInput}'");
} }
} }
@ -532,14 +551,14 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false); m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false);
m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true); m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true);
m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false); m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("<color=lime>Apply to Transform</color>", null) || m_autoApplyTransform) if (GUILayout.Button("<color=lime>Apply to Transform</color>", new GUILayoutOption[0]) || m_autoApplyTransform)
{ {
if (m_localContext) if (m_localContext)
{ {
@ -558,19 +577,19 @@ namespace Explorer
UpdateFreeze(); UpdateFreeze();
} }
} }
if (GUILayout.Button("<color=lime>Update from Transform</color>", null) || m_autoUpdateTransform) if (GUILayout.Button("<color=lime>Update from Transform</color>", new GUILayoutOption[0]) || m_autoUpdateTransform)
{ {
CacheTransformValues(); CacheTransformValues();
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?"); BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?");
BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?"); BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?");
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
bool b = m_localContext; bool b = m_localContext;
b = GUILayout.Toggle(b, "<color=" + (b ? "lime" : "red") + ">Use local transform values?</color>", null); b = GUILayout.Toggle(b, "<color=" + (b ? "lime" : "orange") + ">Use local transform values?</color>", new GUILayoutOption[0]);
if (b != m_localContext) if (b != m_localContext)
{ {
m_localContext = b; m_localContext = b;
@ -611,10 +630,10 @@ namespace Explorer
private void BoolToggle(ref bool value, string message) private void BoolToggle(ref bool value, string message)
{ {
string lbl = "<color="; string lbl = "<color=";
lbl += value ? "lime" : "red"; lbl += value ? "lime" : "orange";
lbl += $">{message}</color>"; lbl += $">{message}</color>";
value = GUILayout.Toggle(value, lbl, null); value = GUILayout.Toggle(value, lbl, new GUILayoutOption[0]);
} }
public enum TranslateType public enum TranslateType
@ -626,7 +645,7 @@ namespace Explorer
private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime) private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime)
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:", GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:",
new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) }); new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) });
@ -649,7 +668,7 @@ namespace Explorer
Vector3 input = m_cachedInput[(int)mode]; Vector3 input = m_cachedInput[(int)mode];
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight; GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) }); GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
@ -663,7 +682,7 @@ namespace Explorer
GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) });
var amountInput = amount.ToString("F3"); var amountInput = amount.ToString("F3");
amountInput = GUILayout.TextField(amountInput, new GUILayoutOption[] { GUILayout.Width(60) }); amountInput = GUIUnstrip.TextField(amountInput, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(amountInput, out float f)) if (float.TryParse(amountInput, out float f))
{ {
amount = f; amount = f;
@ -678,7 +697,7 @@ namespace Explorer
private void PlusMinusFloat(ref float f, float amount, bool multByTime) private void PlusMinusFloat(ref float f, float amount, bool multByTime)
{ {
string s = f.ToString("F3"); string s = f.ToString("F3");
s = GUILayout.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) }); s = GUIUnstrip.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(s, out float f2)) if (float.TryParse(s, out float f2))
{ {
f = f2; f = f2;

View File

@ -1,12 +1,11 @@
using System; using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer namespace Explorer
{ {
@ -54,6 +53,7 @@ namespace Explorer
CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target)); CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target));
// cache the extra cast-caching // cache the extra cast-caching
#if CPP
if (Target is Il2CppSystem.Object ilObject) if (Target is Il2CppSystem.Object ilObject)
{ {
var unityObj = ilObject.TryCast<UnityEngine.Object>(); var unityObj = ilObject.TryCast<UnityEngine.Object>();
@ -68,6 +68,10 @@ namespace Explorer
} }
} }
} }
#else
m_uObj = Target as UnityEngine.Object;
m_component = Target as Component;
#endif
} }
public override void Update() public override void Update()
@ -136,13 +140,14 @@ namespace Explorer
} }
catch catch
{ {
MelonLogger.Log($"Exception getting members for type: {declaringType.FullName}"); ExplorerCore.Log($"Exception getting members for type: {declaringType.FullName}");
continue; continue;
} }
object target = Target; object target = Target;
string exception = null; string exception = null;
#if CPP
if (target is Il2CppSystem.Object ilObject) if (target is Il2CppSystem.Object ilObject)
{ {
try try
@ -154,6 +159,7 @@ namespace Explorer
exception = ReflectionHelpers.ExceptionToString(e); exception = ReflectionHelpers.ExceptionToString(e);
} }
} }
#endif
foreach (var member in infos) foreach (var member in infos)
{ {
@ -163,15 +169,13 @@ namespace Explorer
continue; continue;
// check blacklisted members // check blacklisted members
var name = member.DeclaringType.Name + "." + member.Name; var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (_typeAndMemberBlacklist.Any(it => it == name)) if (_typeAndMemberBlacklist.Any(it => it == sig))
continue; continue;
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it))) if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue; continue;
// compare signature to already cached members
var signature = $"{member.DeclaringType.Name}.{member.Name}";
if (member is MethodInfo mi) if (member is MethodInfo mi)
{ {
AppendParams(mi.GetParameters()); AppendParams(mi.GetParameters());
@ -183,35 +187,35 @@ namespace Explorer
void AppendParams(ParameterInfo[] _args) void AppendParams(ParameterInfo[] _args)
{ {
signature += " ("; sig += " (";
foreach (var param in _args) foreach (var param in _args)
{ {
signature += $"{param.ParameterType.Name} {param.Name}, "; sig += $"{param.ParameterType.Name} {param.Name}, ";
} }
signature += ")"; sig += ")";
} }
if (cachedSigs.Contains(signature)) if (cachedSigs.Contains(sig))
{ {
continue; continue;
} }
// MelonLogger.Log($"Trying to cache member {signature}..."); // ExplorerCore.Log($"Trying to cache member {signature}...");
try try
{ {
var cached = CacheObjectBase.GetCacheObject(member, target); var cached = CacheFactory.GetTypeAndCacheObject(member, target);
if (cached != null) if (cached != null)
{ {
cachedSigs.Add(signature); cachedSigs.Add(sig);
list.Add(cached); list.Add(cached);
cached.ReflectionException = exception; cached.ReflectionException = exception;
} }
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogWarning($"Exception caching member {signature}!"); ExplorerCore.LogWarning($"Exception caching member {sig}!");
MelonLogger.Log(e.ToString()); ExplorerCore.Log(e.ToString());
} }
} }
} }
@ -235,17 +239,17 @@ namespace Explorer
GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box); GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) }); GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
if (m_uObj) if (m_uObj)
{ {
GUILayout.Label("Name: " + m_uObj.name, null); GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (m_uObj) if (m_uObj)
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) }); GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
UIHelpers.InstantiateButton(m_uObj); UIHelpers.InstantiateButton(m_uObj);
if (m_component && m_component.gameObject is GameObject obj) if (m_component && m_component.gameObject is GameObject obj)
@ -266,12 +270,12 @@ namespace Explorer
UIStyles.HorizontalLine(Color.grey); UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) }); GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
m_search = GUILayout.TextField(m_search, null); m_search = GUIUnstrip.TextField(m_search, new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) }); GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
FilterToggle(MemberTypes.All, "All"); FilterToggle(MemberTypes.All, "All");
FilterToggle(MemberTypes.Property, "Properties"); FilterToggle(MemberTypes.Property, "Properties");
@ -279,7 +283,7 @@ namespace Explorer
FilterToggle(MemberTypes.Method, "Methods"); FilterToggle(MemberTypes.Method, "Methods");
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Values:</b>", new GUILayoutOption[] { GUILayout.Width(75) }); GUILayout.Label("<b>Values:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) })) if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) }))
{ {
@ -297,7 +301,7 @@ namespace Explorer
Pages.ItemCount = m_cachedMembersFiltered.Length; Pages.ItemCount = m_cachedMembersFiltered.Length;
// prev/next page buttons // prev/next page buttons
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
@ -325,7 +329,7 @@ namespace Explorer
UIStyles.HorizontalLine(Color.grey); UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
var members = this.m_cachedMembersFiltered; var members = this.m_cachedMembersFiltered;
int start = Pages.CalculateOffsetIndex(); int start = Pages.CalculateOffsetIndex();
@ -361,16 +365,13 @@ namespace Explorer
GUIUnstrip.EndArea(); GUIUnstrip.EndArea();
} }
} }
catch (Il2CppException e) catch (Exception e) when (e.Message.Contains("in a group with only"))
{ {
if (!e.Message.Contains("in a group with only")) // suppress
{
throw;
}
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogWarning("Exception drawing ReflectionWindow: " + e.GetType() + ", " + e.Message); ExplorerCore.LogWarning("Exception drawing ReflectionWindow: " + e.GetType() + ", " + e.Message);
DestroyWindow(); DestroyWindow();
return; return;
} }

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -56,7 +50,7 @@ namespace Explorer
try try
{ {
GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20)); 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>Close All</color>")) if (GUIUnstrip.Button(new Rect(m_rect.width - 90, 2, 80, 20), "<color=red>Close All</color>"))
{ {
foreach (var window in WindowManager.Windows) foreach (var window in WindowManager.Windows)
{ {
@ -67,8 +61,8 @@ namespace Explorer
GUIUnstrip.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box); GUIUnstrip.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
int tabPerRow = Mathf.FloorToInt((float)((decimal)m_rect.width / 238)); int tabPerRow = Mathf.FloorToInt((float)((decimal)m_rect.width / 238));
int rowCount = 0; int rowCount = 0;
@ -78,7 +72,7 @@ namespace Explorer
{ {
rowCount = 0; rowCount = 0;
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(new GUILayoutOption[0]);
} }
rowCount++; rowCount++;

View File

@ -1,11 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -49,7 +42,11 @@ namespace Explorer
public void OnGUI() public void OnGUI()
{ {
m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title); #if CPP
m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, GUIContent.Temp(Title), GUI.skin.window);
#else
m_rect = GUI.Window(windowID, m_rect, WindowFunction, Title);
#endif
} }
public void Header() public void Header()
@ -57,8 +54,12 @@ namespace Explorer
if (!WindowManager.TabView) if (!WindowManager.TabView)
{ {
GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20)); 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>")) #if CPP
if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), GUIContent.Temp("<color=red><b>X</b></color>"), GUI.skin.button))
#else
if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), "<color=red><b>X</b></color>", GUI.skin.button))
#endif
{ {
DestroyWindow(); DestroyWindow();
return; return;

View File

@ -1,13 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
namespace Explorer namespace Explorer
{ {
@ -93,12 +85,16 @@ namespace Explorer
{ {
forceReflection = true; forceReflection = true;
} }
#if CPP
Il2CppSystem.Object iObj = null; Il2CppSystem.Object iObj = null;
if (obj is Il2CppSystem.Object isObj) if (obj is Il2CppSystem.Object isObj)
{ {
iObj = isObj; iObj = isObj;
} }
#else
var iObj = obj;
#endif
if (!forceReflection) if (!forceReflection)
{ {
@ -106,6 +102,7 @@ namespace Explorer
{ {
bool equals = ReferenceEquals(obj, window.Target); bool equals = ReferenceEquals(obj, window.Target);
#if CPP
if (!equals && iObj is Il2CppSystem.Object iCurrent && window.Target is Il2CppSystem.Object iTarget) if (!equals && iObj is Il2CppSystem.Object iCurrent && window.Target is Il2CppSystem.Object iTarget)
{ {
if (iCurrent.GetIl2CppType().FullName != iTarget.GetIl2CppType().FullName) if (iCurrent.GetIl2CppType().FullName != iTarget.GetIl2CppType().FullName)
@ -118,6 +115,7 @@ namespace Explorer
equals = iCurrent.Pointer == iTarget.Pointer; equals = iCurrent.Pointer == iTarget.Pointer;
} }
#endif
if (equals) if (equals)
{ {
@ -142,7 +140,7 @@ namespace Explorer
{ {
if (!TabView) if (!TabView)
{ {
GUI.BringWindowToFront(window.windowID); GUIUnstrip.BringWindowToFront(window.windowID);
GUI.FocusWindow(window.windowID); GUI.FocusWindow(window.windowID);
} }
else else
@ -173,7 +171,7 @@ namespace Explorer
{ {
get get
{ {
if (!CppExplorer.ShowMenu) if (!ExplorerCore.ShowMenu)
{ {
return false; return false;
} }
@ -191,7 +189,7 @@ namespace Explorer
private static bool RectContainsMouse(Rect rect) private static bool RectContainsMouse(Rect rect)
{ {
var mousePos = InputHelper.mousePosition; var mousePos = InputHelper.MousePosition;
return rect.Contains(new Vector2(mousePos.x, Screen.height - mousePos.y)); return rect.Contains(new Vector2(mousePos.x, Screen.height - mousePos.y));
} }

View File

@ -2,19 +2,22 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Explorer; using Explorer;
#if ML
using MelonLoader; using MelonLoader;
[assembly: MelonInfo(typeof(CppExplorer), CppExplorer.NAME, CppExplorer.VERSION, CppExplorer.AUTHOR)] [assembly: MelonInfo(typeof(ExplorerMelonMod), "Explorer", ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
[assembly: MelonGame(null, null)] [assembly: MelonGame(null, null)]
#endif
// 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.NAME)] [assembly: AssemblyTitle(ExplorerCore.NAME)]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(CppExplorer.AUTHOR)] [assembly: AssemblyCompany(ExplorerCore.AUTHOR)]
[assembly: AssemblyProduct(CppExplorer.NAME)] [assembly: AssemblyProduct(ExplorerCore.NAME)]
[assembly: AssemblyCopyright("")] [assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]

View File

@ -1,27 +1,86 @@
using System; using System.Collections;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine; using UnityEngine;
using System.Reflection;
using System.Collections.Specialized;
// used to test multiple generic constraints
public class TestGeneric : IComparable<string>
{
public TestGeneric() { }
public int CompareTo(string other) => throw new NotImplementedException();
}
[Flags]
public enum TestFlags
{
Red,
Green,
Blue
}
// test non-flags weird enum
public enum WeirdEnum
{
First = 1,
Second,
Third = 2,
Fourth,
Fifth
}
namespace Explorer.Tests namespace Explorer.Tests
{ {
public class TestClass public class TestClass
{ {
public static TestFlags testFlags = TestFlags.Blue | TestFlags.Green;
public static WeirdEnum testWeird = WeirdEnum.First;
public static int testBitmask;
public static TestClass Instance => m_instance ?? (m_instance = new TestClass()); public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
private static TestClass m_instance; private static TestClass m_instance;
#if CPP
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
#endif
public TestClass() public TestClass()
{ {
#if CPP
ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>(); ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
ILHashSetTest.Add("1"); ILHashSetTest.Add("1");
ILHashSetTest.Add("2"); ILHashSetTest.Add("2");
ILHashSetTest.Add("3"); ILHashSetTest.Add("3");
#endif
testBitmask = 1 | 2;
} }
public static int StaticProperty => 5;
public static int StaticField = 5;
public int NonStaticField;
public static string TestGeneric<C, T>(string arg0) where C : Component where T : TestGeneric, IComparable<string>
{
return $"C: '{typeof(C).FullName}', T: '{typeof(T).FullName}', arg0: '{arg0}'";
}
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2)
{
arg2 = "this is arg2";
return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'";
}
//// this type of generic is not supported, due to requiring a non-primitive argument.
//public static T TestDifferentGeneric<T>(T obj) where T : Component
//{
// return obj;
//}
// test a non-generic dictionary // test a non-generic dictionary
public Hashtable TestNonGenericDict() public Hashtable TestNonGenericDict()
@ -55,7 +114,6 @@ namespace Explorer.Tests
"Three" "Three"
}; };
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
// Test indexed parameter // Test indexed parameter

View File

@ -1,497 +1,124 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using System.Reflection; using System.Reflection;
using UnityEngine;
#if CPP
using Explorer.UnstripInternals;
using UnityEngineInternal; using UnityEngineInternal;
using Harmony; using UnhollowerRuntimeLib;
#endif
namespace Explorer namespace Explorer
{ {
public class GUIUnstrip public class GUIUnstrip
{ {
public static int s_ScrollControlId; public static string TextField(string text, GUILayoutOption[] options)
public static bool ScrollFailed = false;
public static bool ManualUnstripFailed = false;
private static GenericStack ScrollStack => m_scrollStack ?? GetScrollStack();
private static PropertyInfo m_scrollViewStatesInfo;
private static GenericStack m_scrollStack;
public static DateTime nextScrollStepTime;
private static GenericStack GetScrollStack()
{ {
if (m_scrollViewStatesInfo == null) #if CPP
{ return Internal.TextField(text, options);
if (typeof(GUI).GetProperty("scrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo scrollStatesInfo) #else
{ return GUILayout.TextField(text, options);
m_scrollViewStatesInfo = scrollStatesInfo; #endif
} }
else if (typeof(GUI).GetProperty("s_ScrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo s_scrollStatesInfo)
{
m_scrollViewStatesInfo = s_scrollStatesInfo;
}
}
if (m_scrollViewStatesInfo?.GetValue(null, null) is GenericStack stack) public static Rect Window(int id, Rect rect, GUI.WindowFunction windowFunc, string title)
{ {
m_scrollStack = stack; #if CPP
} return GUI.Window(id, rect, windowFunc, GUIContent.Temp(title), GUI.skin.window);
else #else
{ return GUI.Window(id, rect, windowFunc, title);
m_scrollStack = new GenericStack(); #endif
} }
return m_scrollStack; public static bool Button(Rect rect, string title)
{
#if CPP
return GUI.Button(rect, GUIContent.Temp(title), GUI.skin.button);
#else
return GUI.Button(rect, title);
#endif
}
public static string TextArea(string text, params GUILayoutOption[] options)
{
#if CPP
return GUILayout.DoTextField(text, -1, true, GUI.skin.textArea, options);
#else
return GUILayout.TextArea(text, options);
#endif
}
public static void BringWindowToFront(int id)
{
#if CPP
Internal.BringWindowToFront(id);
#else
GUI.BringWindowToFront(id);
#endif
}
public static Vector2 ScreenToGUIPoint(Vector2 screenPoint)
{
#if CPP
return Internal.ScreenToGUIPoint(screenPoint);
#else
return GUIUtility.ScreenToGUIPoint(screenPoint);
#endif
} }
public static void Space(float pixels) public static void Space(float pixels)
{ {
GUIUtility.CheckOnGUI(); #if CPP
Internal.Space(pixels);
if (GUILayoutUtility.current.topLevel.isVertical) #else
LayoutUtilityUnstrip.GetRect(0, pixels, GUILayoutUtility.spaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) }); GUILayout.Space(pixels);
else #endif
LayoutUtilityUnstrip.GetRect(pixels, 0, GUILayoutUtility.spaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
if (Event.current.type == EventType.Layout)
{
GUILayoutUtility.current.topLevel.entries[GUILayoutUtility.current.topLevel.entries.Count - 1].consideredForMargin = false;
}
} }
// fix for repeatbutton public static bool RepeatButton(string text, params GUILayoutOption[] options)
static public bool RepeatButton(Texture image, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(image), GUI.skin.button, options); }
static public bool RepeatButton(string text, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(text), GUI.skin.button, options); }
static public bool RepeatButton(GUIContent content, params GUILayoutOption[] options) { return DoRepeatButton(content, GUI.skin.button, options); }
static public bool RepeatButton(Texture image, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(image), style, options); }
static public bool RepeatButton(string text, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(text), style, options); }
// Make a repeating button. The button returns true as long as the user holds down the mouse
static public bool RepeatButton(GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(content, style, options); }
static bool DoRepeatButton(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{ return GUI.RepeatButton(LayoutUtilityUnstrip.GetRect(content, style, options), content, style); }
// Fix for BeginArea
static public void BeginArea(Rect screenRect) { BeginArea(screenRect, GUIContent.none, GUIStyle.none); }
static public void BeginArea(Rect screenRect, string text) { BeginArea(screenRect, GUIContent.Temp(text), GUIStyle.none); }
static public void BeginArea(Rect screenRect, Texture image) { BeginArea(screenRect, GUIContent.Temp(image), GUIStyle.none); }
static public void BeginArea(Rect screenRect, GUIContent content) { BeginArea(screenRect, content, GUIStyle.none); }
static public void BeginArea(Rect screenRect, GUIStyle style) { BeginArea(screenRect, GUIContent.none, style); }
static public void BeginArea(Rect screenRect, string text, GUIStyle style) { BeginArea(screenRect, GUIContent.Temp(text), style); }
static public void BeginArea(Rect screenRect, Texture image, GUIStyle style) { BeginArea(screenRect, GUIContent.Temp(image), style); }
// Begin a GUILayout block of GUI controls in a fixed screen area.
static public void BeginArea(Rect screenRect, GUIContent content, GUIStyle style)
{ {
GUILayoutGroup g = GUILayoutUtility.BeginLayoutArea(style, Il2CppType.Of<GUILayoutGroup>()); #if CPP
if (Event.current.type == EventType.Layout) return Internal.DoRepeatButton(GUIContent.Temp(text), GUI.skin.button, options);
{ #else
g.resetCoords = true; return GUILayout.RepeatButton(text, options);
g.minWidth = g.maxWidth = screenRect.width; #endif
g.minHeight = g.maxHeight = screenRect.height; }
g.rect = Rect.MinMaxRect(screenRect.xMin, screenRect.yMin, g.rect.xMax, g.rect.yMax);
} public static void BeginArea(Rect screenRect, GUIStyle style)
{
GUI.BeginGroup(g.rect, content, style); #if CPP
Internal.BeginArea(screenRect, GUIContent.none, style);
#else
GUILayout.BeginArea(screenRect, style);
#endif
} }
// Close a GUILayout block started with BeginArea
static public void EndArea() static public void EndArea()
{ {
if (Event.current.type == EventType.Used) #if CPP
return; Internal.EndArea();
GUILayoutUtility.current.layoutGroups.Pop(); #else
GUILayoutUtility.current.topLevel = GUILayoutUtility.current.layoutGroups.Peek().TryCast<GUILayoutGroup>(); GUILayout.EndArea();
GUI.EndGroup(); #endif
} }
// Fix for BeginGroup
public static void BeginGroup(Rect position) { BeginGroup(position, GUIContent.none, GUIStyle.none); }
public static void BeginGroup(Rect position, string text) { BeginGroup(position, GUIContent.Temp(text), GUIStyle.none); }
public static void BeginGroup(Rect position, Texture image) { BeginGroup(position, GUIContent.Temp(image), GUIStyle.none); }
public static void BeginGroup(Rect position, GUIContent content) { BeginGroup(position, content, GUIStyle.none); }
public static void BeginGroup(Rect position, GUIStyle style) { BeginGroup(position, GUIContent.none, style); }
public static void BeginGroup(Rect position, string text, GUIStyle style) { BeginGroup(position, GUIContent.Temp(text), style); }
public static void BeginGroup(Rect position, Texture image, GUIStyle style) { BeginGroup(position, GUIContent.Temp(image), style); }
public static void BeginGroup(Rect position, GUIContent content, GUIStyle style) { BeginGroup(position, content, style, Vector2.zero); }
internal static void BeginGroup(Rect position, GUIContent content, GUIStyle style, Vector2 scrollOffset)
{
int id = GUIUtility.GetControlID(GUI.s_BeginGroupHash, FocusType.Passive);
if (content != GUIContent.none || style != GUIStyle.none)
{
switch (Event.current.type)
{
case EventType.Repaint:
style.Draw(position, content, id);
break;
default:
if (position.Contains(Event.current.mousePosition))
GUIUtility.mouseUsed = true;
break;
}
}
GUIClip.Push(position, scrollOffset, Vector2.zero, false);
}
public static void EndGroup()
{
GUIClip.Internal_Pop();
}
// Fix for BeginScrollView.
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options) public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{ {
// First, just try normal way, may not have been stripped or was unstripped successfully. #if CPP
if (!ScrollFailed) return Internal.BeginScrollView(scroll, options);
{ #else
try return GUILayout.BeginScrollView(scroll, options);
{ #endif
return GUILayout.BeginScrollView(scroll, options);
}
catch
{
ScrollFailed = true;
}
}
// Try manual implementation.
if (!ManualUnstripFailed)
{
try
{
return BeginScrollView_ImplLayout(scroll, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options);
}
catch (Exception e)
{
MelonLogger.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
ManualUnstripFailed = true;
}
}
// Sorry! No scrolling for you.
return scroll;
} }
public static void EndScrollView(bool handleScrollWheel = true) public static void EndScrollView(bool handleScrollWheel = true)
{ {
// Only end the scroll view for the relevant BeginScrollView option, if any. #if CPP
Internal.EndScrollView(handleScrollWheel);
if (!ScrollFailed) #else
{ GUILayout.EndScrollView();
GUILayout.EndScrollView(); #endif
}
else if (!ManualUnstripFailed)
{
GUILayoutUtility.EndLayoutGroup();
EndScrollView_Impl(handleScrollWheel);
}
}
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{
var guiscrollGroup = GUILayoutUtility.BeginLayoutGroup(background, null, Il2CppType.Of<GUIScrollGroup>())
.TryCast<GUIScrollGroup>();
EventType type = Event.current.type;
if (type == EventType.Layout)
{
guiscrollGroup.resetCoords = true;
guiscrollGroup.isVertical = true;
guiscrollGroup.stretchWidth = 1;
guiscrollGroup.stretchHeight = 1;
guiscrollGroup.verticalScrollbar = verticalScrollbar;
guiscrollGroup.horizontalScrollbar = horizontalScrollbar;
guiscrollGroup.needsVerticalScrollbar = alwaysShowVertical;
guiscrollGroup.needsHorizontalScrollbar = alwaysShowHorizontal;
guiscrollGroup.ApplyOptions(options);
}
return BeginScrollView_Impl(guiscrollGroup.rect,
scrollPosition,
new Rect(0f, 0f, guiscrollGroup.clientWidth, guiscrollGroup.clientHeight),
alwaysShowHorizontal,
alwaysShowVertical,
horizontalScrollbar,
verticalScrollbar,
background
);
}
private static Vector2 BeginScrollView_Impl(Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal,
bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background)
{
// GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive);
var scrollViewState = GUIUtility.GetStateObject(Il2CppType.Of<ScrollViewState>(), controlID).TryCast<ScrollViewState>();
var scrollExt = ScrollViewStateUnstrip.FromPointer(scrollViewState.Pointer);
if (scrollExt == null) throw new Exception($"Could not get scrollExt for pointer '{scrollViewState.Pointer}'!");
bool apply = scrollExt.apply;
if (apply)
{
scrollPosition = scrollExt.scrollPosition;
scrollExt.apply = false;
}
scrollExt.position = position;
scrollExt.scrollPosition = scrollPosition;
scrollExt.visibleRect = scrollExt.viewRect = viewRect;
var rect = scrollExt.visibleRect;
rect.width = position.width;
rect.height = position.height;
ScrollStack.Push(scrollViewState);
Rect screenRect = new Rect(position.x, position.y, position.width, position.height);
EventType type = Event.current.type;
if (type != EventType.Layout)
{
if (type != EventType.Used)
{
bool flag = alwaysShowVertical;
bool flag2 = alwaysShowHorizontal;
if (flag2 || viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
if (flag || viewRect.height > screenRect.height)
{
rect.width = position.width - verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
screenRect.width -= verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
flag = true;
if (!flag2 && viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
}
if (Event.current.type == EventType.Repaint && background != GUIStyle.none)
{
background.Draw(position, position.Contains(Event.current.mousePosition), false, flag2 && flag, false);
}
if (flag2 && horizontalScrollbar != GUIStyle.none)
{
scrollPosition.x = HorizBar_Impl(
new Rect(
position.x,
position.yMax - horizontalScrollbar.fixedHeight,
screenRect.width,
horizontalScrollbar.fixedHeight),
scrollPosition.x,
Mathf.Min(screenRect.width, viewRect.width),
0f,
viewRect.width,
horizontalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.x = ((horizontalScrollbar == GUIStyle.none) ? Mathf.Clamp(scrollPosition.x, 0f, Mathf.Max(viewRect.width - position.width, 0f)) : 0f);
}
if (flag && verticalScrollbar != GUIStyle.none)
{
scrollPosition.y = VertBar_Impl(
new Rect(
screenRect.xMax + (float)verticalScrollbar.margin.left,
screenRect.y,
verticalScrollbar.fixedWidth,
screenRect.height),
scrollPosition.y,
Mathf.Min(screenRect.height, viewRect.height),
0f,
viewRect.height,
verticalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.y = ((verticalScrollbar == GUIStyle.none) ? Mathf.Clamp(scrollPosition.y, 0f, Mathf.Max(viewRect.height - position.height, 0f)) : 0f);
}
}
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
}
GUIClip.Push(screenRect, new Vector2(Mathf.Round(-scrollPosition.x - viewRect.x), Mathf.Round(-scrollPosition.y - viewRect.y)), Vector2.zero, false);
return scrollPosition;
}
public static float HorizBar_Impl(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
{
return Scroller_Impl(position, value, size, leftValue, rightValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "leftbutton"),
GUI.skin.GetStyle(style.name + "rightbutton"),
true);
}
public static float VertBar_Impl(Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
{
return Scroller_Impl(position, value, size, topValue, bottomValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "upbutton"),
GUI.skin.GetStyle(style.name + "downbutton"),
false);
}
private static void EndScrollView_Impl(bool handleScrollWheel)
{
GUIUtility.CheckOnGUI();
if (ScrollStack.Count <= 0) return;
var state = ScrollStack.Peek().TryCast<ScrollViewState>();
var scrollExt = ScrollViewStateUnstrip.FromPointer(state.Pointer);
if (scrollExt == null) throw new Exception("Could not get scrollExt!");
GUIClip.Pop();
ScrollStack.Pop();
var position = scrollExt.position;
if (handleScrollWheel && Event.current.type == EventType.ScrollWheel && position.Contains(Event.current.mousePosition))
{
var pos = scrollExt.scrollPosition;
pos.x = Mathf.Clamp(scrollExt.scrollPosition.x + Event.current.delta.x * 20f, 0f, scrollExt.viewRect.width - scrollExt.visibleRect.width);
pos.y = Mathf.Clamp(scrollExt.scrollPosition.y + Event.current.delta.y * 20f, 0f, scrollExt.viewRect.height - scrollExt.visibleRect.height);
if (scrollExt.scrollPosition.x < 0f)
{
pos.x = 0f;
}
if (pos.y < 0f)
{
pos.y = 0f;
}
scrollExt.apply = true;
Event.current.Use();
}
}
private static float Scroller_Impl(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, GUIStyle leftButton, GUIStyle rightButton, bool horiz)
{
GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
Rect position2;
Rect rect;
Rect rect2;
if (horiz)
{
position2 = new Rect(position.x + leftButton.fixedWidth, position.y, position.width - leftButton.fixedWidth - rightButton.fixedWidth, position.height);
rect = new Rect(position.x, position.y, leftButton.fixedWidth, position.height);
rect2 = new Rect(position.xMax - rightButton.fixedWidth, position.y, rightButton.fixedWidth, position.height);
}
else
{
position2 = new Rect(position.x, position.y + leftButton.fixedHeight, position.width, position.height - leftButton.fixedHeight - rightButton.fixedHeight);
rect = new Rect(position.x, position.y, position.width, leftButton.fixedHeight);
rect2 = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight);
}
value = Slider_Impl(position2, value, size, leftValue, rightValue, slider, thumb, horiz, controlID);
bool flag = Event.current.type == EventType.MouseUp;
if (ScrollerRepeatButton_Impl(controlID, rect, leftButton))
{
value -= 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (ScrollerRepeatButton_Impl(controlID, rect2, rightButton))
{
value += 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (flag && Event.current.type == EventType.Used)
{
s_ScrollControlId = 0;
}
if (leftValue < rightValue)
{
value = Mathf.Clamp(value, leftValue, rightValue - size);
}
else
{
value = Mathf.Clamp(value, rightValue, leftValue - size);
}
return value;
}
public static float Slider_Impl(Rect position, float value, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
{
if (id == 0)
{
id = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
}
var sliderHandler = new SliderHandlerUnstrip(position, value, size, start, end, slider, thumb, horiz, id);
return sliderHandler.Handle();
}
private static bool ScrollerRepeatButton_Impl(int scrollerID, Rect rect, GUIStyle style)
{
bool result = false;
if (GUI.DoRepeatButton(rect, GUIContent.none, style, FocusType.Passive))
{
bool flag = s_ScrollControlId != scrollerID;
s_ScrollControlId = scrollerID;
if (flag)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
}
else if (DateTime.Now >= nextScrollStepTime)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
}
if (Event.current.type == EventType.Repaint)
{
GUI.InternalRepaintEditorWindow();
}
}
return result;
} }
} }
} }

View File

@ -0,0 +1,680 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngineInternal;
using UnhollowerRuntimeLib;
namespace Explorer.UnstripInternals
{
public class Internal
{
#region Properties
public static int s_ScrollControlId;
public static bool ScrollFailed = false;
public static bool ManualUnstripFailed = false;
public static GenericStack ScrollStack => m_scrollStack ?? GetScrollStack();
public static PropertyInfo m_scrollViewStatesInfo;
public static GenericStack m_scrollStack;
public static Dictionary<int, Il2CppSystem.Object> StateCache => m_stateCacheDict ?? GetStateCacheDict();
public static Dictionary<int, Il2CppSystem.Object> m_stateCacheDict;
public static GUIStyle SpaceStyle => m_spaceStyle ?? GetSpaceStyle();
public static GUIStyle m_spaceStyle;
public static DateTime nextScrollStepTime;
public static MethodInfo ScreenToGuiPointMethod;
public static bool m_screenToGuiAttemped;
public static MethodInfo m_bringWindowToFrontMethod;
public static bool m_bringWindowFrontAttempted;
private static GenericStack GetScrollStack()
{
if (m_scrollViewStatesInfo == null)
{
if (typeof(GUI).GetProperty("scrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo scrollStatesInfo)
{
m_scrollViewStatesInfo = scrollStatesInfo;
}
else if (typeof(GUI).GetProperty("s_ScrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo s_scrollStatesInfo)
{
m_scrollViewStatesInfo = s_scrollStatesInfo;
}
}
if (m_scrollViewStatesInfo?.GetValue(null, null) is GenericStack stack)
{
m_scrollStack = stack;
}
else
{
m_scrollStack = new GenericStack();
}
return m_scrollStack;
}
private static Dictionary<int, Il2CppSystem.Object> GetStateCacheDict()
{
if (m_stateCacheDict == null)
{
try
{
var type = ReflectionHelpers.GetTypeByName("UnityEngine.GUIStateObjects");
m_stateCacheDict = type.GetProperty("s_StateCache")
.GetValue(null, null)
as Dictionary<int, Il2CppSystem.Object>;
if (m_stateCacheDict == null) throw new Exception();
}
catch
{
m_stateCacheDict = new Dictionary<int, Il2CppSystem.Object>();
}
}
return m_stateCacheDict;
}
private static GUIStyle GetSpaceStyle()
{
try
{
m_spaceStyle = typeof(GUILayoutUtility)
.GetProperty("s_SpaceStyle")
.GetValue(null, null)
.Il2CppCast(typeof(GUIStyle))
as GUIStyle;
if (m_spaceStyle == null) throw new Exception();
}
catch { }
if (m_spaceStyle == null)
{
m_spaceStyle = new GUIStyle();
}
m_spaceStyle.stretchWidth = false;
return m_spaceStyle;
}
#endregion
#region GUILayout Methods
public static string TextField(string text, GUILayoutOption[] options)
{
text = text ?? "";
int controlID = GUIUtility.GetControlID(FocusType.Keyboard);
GUIContent guicontent = GUIContent.Temp(text);
bool flag = GUIUtility.keyboardControl != controlID;
if (flag)
{
guicontent = GUIContent.Temp(text);
}
else
{
guicontent = GUIContent.Temp(text);
// guicontent = GUIContent.Temp(text + GUIUtility.compositionString);
}
Rect rect = Internal_LayoutUtility.GetRect(guicontent, GUI.skin.textField, options);
bool flag2 = GUIUtility.keyboardControl == controlID;
if (flag2)
{
guicontent = GUIContent.Temp(text);
}
DoTextField(rect, controlID, guicontent, false, -1, GUI.skin.textField);
return guicontent.text;
}
internal static void DoTextField(Rect position, int id, GUIContent content, bool multiline, int maxLength, GUIStyle style)
{
if (GetStateObject(Il2CppType.Of<TextEditor>(), id).TryCast<TextEditor>() is TextEditor textEditor)
{
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
textEditor.m_Content.text = content.text;
textEditor.SaveBackup();
textEditor.position = position;
textEditor.style = style;
textEditor.multiline = multiline;
textEditor.controlID = id;
textEditor.DetectFocusChange();
GUI.HandleTextFieldEventForDesktop(position, id, content, multiline, maxLength, style, textEditor);
textEditor.UpdateScrollOffsetIfNeeded(Event.current);
}
}
public static bool DoRepeatButton(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
return GUI.DoRepeatButton(Internal_LayoutUtility.GetRect(content, style, options), content, style, FocusType.Passive);
}
public static void Space(float pixels)
{
if (GUILayoutUtility.current.topLevel.isVertical)
Internal_LayoutUtility.GetRect(0, pixels, SpaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) });
else
Internal_LayoutUtility.GetRect(pixels, 0, SpaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
if (Event.current.type == EventType.Layout)
{
GUILayoutUtility.current.topLevel.entries[GUILayoutUtility.current.topLevel.entries.Count - 1].consideredForMargin = false;
}
}
public static Vector2 ScreenToGUIPoint(Vector2 screenPoint)
{
if (!m_screenToGuiAttemped)
{
m_screenToGuiAttemped = true;
ScreenToGuiPointMethod = typeof(GUIUtility).GetMethod("ScreenToGUIPoint");
}
if (ScreenToGuiPointMethod == null)
{
throw new Exception("Couldn't get method 'GUIUtility.ScreenToGUIPoint'!");
}
return (Vector2)ScreenToGuiPointMethod.Invoke(null, new object[] { screenPoint });
}
public static void BringWindowToFront(int id)
{
if (!m_bringWindowFrontAttempted)
{
m_bringWindowFrontAttempted = true;
m_bringWindowToFrontMethod = typeof(GUI).GetMethod("BringWindowToFront");
}
if (m_bringWindowToFrontMethod == null)
{
throw new Exception("Couldn't get method 'GUIUtility.BringWindowToFront'!");
}
m_bringWindowToFrontMethod.Invoke(null, new object[] { id });
}
public static void BeginArea(Rect screenRect, GUIContent content, GUIStyle style)
{
var g = BeginLayoutArea(style, typeof(GUILayoutGroup));
if (Event.current.type == EventType.Layout)
{
g.resetCoords = true;
g.minWidth = g.maxWidth = screenRect.width;
g.minHeight = g.maxHeight = screenRect.height;
g.rect = Rect.MinMaxRect(screenRect.xMin, screenRect.yMin, g.rect.xMax, g.rect.yMax);
}
BeginGroup(g.rect, content, style);
}
internal static GUILayoutGroup BeginLayoutArea(GUIStyle style, Type layoutType)
{
EventType type = Event.current.type;
GUILayoutGroup guilayoutGroup;
if (type != EventType.Used && type != EventType.Layout)
{
guilayoutGroup = GUILayoutUtility.current.windows.GetNext().TryCast<GUILayoutGroup>();
guilayoutGroup.ResetCursor();
}
else
{
guilayoutGroup = (GUILayoutGroup)Activator.CreateInstance(layoutType);
guilayoutGroup.style = style;
GUILayoutUtility.current.windows.Add(guilayoutGroup);
}
GUILayoutUtility.current.layoutGroups.Push(guilayoutGroup);
GUILayoutUtility.current.topLevel = guilayoutGroup;
return guilayoutGroup;
}
public static void BeginGroup(Rect position, GUIContent content, GUIStyle style)
{
BeginGroup(position, content, style, Vector2.zero);
}
internal static void BeginGroup(Rect position, GUIContent content, GUIStyle style, Vector2 scrollOffset)
{
int id = GUIUtility.GetControlID(GUI.s_BeginGroupHash, FocusType.Passive);
if (content != GUIContent.none || style != GUIStyle.none)
{
switch (Event.current.type)
{
case EventType.Repaint:
style.Draw(position, content, id);
break;
default:
if (position.Contains(Event.current.mousePosition))
GUIUtility.mouseUsed = true;
break;
}
}
GUIClip.Push(position, scrollOffset, Vector2.zero, false);
}
public static void EndArea()
{
if (Event.current.type == EventType.Used)
return;
GUILayoutUtility.current.layoutGroups.Pop();
GUILayoutUtility.current.topLevel = GUILayoutUtility.current.layoutGroups.Peek().TryCast<GUILayoutGroup>();
GUI.EndGroup();
}
#endregion
#region Scrolling
private static Il2CppSystem.Object GetStateObject(Il2CppSystem.Type type, int controlID)
{
Il2CppSystem.Object obj;
if (StateCache.ContainsKey(controlID))
{
obj = StateCache[controlID];
}
else
{
obj = Il2CppSystem.Activator.CreateInstance(type);
StateCache.Add(controlID, obj);
}
return obj;
}
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
// First, just try normal way, may not have been stripped or was unstripped successfully.
if (!ScrollFailed)
{
try
{
return GUILayout.BeginScrollView(scroll, options);
}
catch
{
ScrollFailed = true;
}
}
// Try manual implementation.
if (!ManualUnstripFailed)
{
try
{
return BeginScrollView_ImplLayout(scroll,
false,
false,
GUI.skin.horizontalScrollbar,
GUI.skin.verticalScrollbar,
GUI.skin.scrollView,
options);
}
catch (Exception e)
{
ExplorerCore.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
ManualUnstripFailed = true;
}
}
// Sorry! No scrolling for you.
return scroll;
}
internal static void EndScrollView(bool handleScrollWheel)
{
// Only end the scroll view for the relevant BeginScrollView option, if any.
if (!ScrollFailed)
{
GUILayout.EndScrollView();
}
else if (!ManualUnstripFailed)
{
GUILayoutUtility.EndLayoutGroup();
if (ScrollStack.Count <= 0) return;
var state = ScrollStack.Peek().TryCast<ScrollViewState>();
var scrollExt = Internal_ScrollViewState.FromPointer(state.Pointer);
if (scrollExt == null) throw new Exception("Could not get scrollExt!");
GUIClip.Pop();
ScrollStack.Pop();
var position = scrollExt.position;
if (handleScrollWheel && Event.current.type == EventType.ScrollWheel && position.Contains(Event.current.mousePosition))
{
var pos = scrollExt.scrollPosition;
pos.x = Mathf.Clamp(scrollExt.scrollPosition.x + Event.current.delta.x * 20f, 0f, scrollExt.viewRect.width - scrollExt.visibleRect.width);
pos.y = Mathf.Clamp(scrollExt.scrollPosition.y + Event.current.delta.y * 20f, 0f, scrollExt.viewRect.height - scrollExt.visibleRect.height);
if (scrollExt.scrollPosition.x < 0f)
{
pos.x = 0f;
}
if (pos.y < 0f)
{
pos.y = 0f;
}
scrollExt.apply = true;
Event.current.Use();
}
}
}
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{
var guiscrollGroup = GUILayoutUtility.BeginLayoutGroup(background, null, Il2CppType.Of<GUIScrollGroup>())
.TryCast<GUIScrollGroup>();
EventType type = Event.current.type;
if (type == EventType.Layout)
{
guiscrollGroup.resetCoords = true;
guiscrollGroup.isVertical = true;
guiscrollGroup.stretchWidth = 1;
guiscrollGroup.stretchHeight = 1;
guiscrollGroup.verticalScrollbar = verticalScrollbar;
guiscrollGroup.horizontalScrollbar = horizontalScrollbar;
guiscrollGroup.needsVerticalScrollbar = alwaysShowVertical;
guiscrollGroup.needsHorizontalScrollbar = alwaysShowHorizontal;
guiscrollGroup.ApplyOptions(options);
}
return BeginScrollView_Impl(guiscrollGroup.rect,
scrollPosition,
new Rect(0f, 0f, guiscrollGroup.clientWidth, guiscrollGroup.clientHeight),
alwaysShowHorizontal,
alwaysShowVertical,
horizontalScrollbar,
verticalScrollbar,
background
);
}
private static Vector2 BeginScrollView_Impl(Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal,
bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background)
{
// GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive);
var scrollViewState = GetStateObject(Il2CppType.Of<ScrollViewState>(), controlID)
.TryCast<ScrollViewState>();
if (scrollViewState == null)
return scrollPosition;
var scrollExt = Internal_ScrollViewState.FromPointer(scrollViewState.Pointer);
if (scrollExt == null)
return scrollPosition;
bool apply = scrollExt.apply;
if (apply)
{
scrollPosition = scrollExt.scrollPosition;
scrollExt.apply = false;
}
scrollExt.position = position;
scrollExt.scrollPosition = scrollPosition;
scrollExt.visibleRect = scrollExt.viewRect = viewRect;
var rect = scrollExt.visibleRect;
rect.width = position.width;
rect.height = position.height;
ScrollStack.Push(scrollViewState);
Rect screenRect = new Rect(position.x, position.y, position.width, position.height);
EventType type = Event.current.type;
if (type != EventType.Layout)
{
if (type != EventType.Used)
{
bool flag = alwaysShowVertical;
bool flag2 = alwaysShowHorizontal;
if (flag2 || viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
if (flag || viewRect.height > screenRect.height)
{
rect.width = position.width - verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
screenRect.width -= verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
flag = true;
if (!flag2 && viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
}
if (Event.current.type == EventType.Repaint && background != GUIStyle.none)
{
background.Draw(position, position.Contains(Event.current.mousePosition), false, flag2 && flag, false);
}
if (flag2 && horizontalScrollbar != GUIStyle.none)
{
scrollPosition.x = HorizontalScroll(
new Rect(
position.x,
position.yMax - horizontalScrollbar.fixedHeight,
screenRect.width,
horizontalScrollbar.fixedHeight),
scrollPosition.x,
Mathf.Min(screenRect.width, viewRect.width),
0f,
viewRect.width,
horizontalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.x = ((horizontalScrollbar == GUIStyle.none)
? Mathf.Clamp(scrollPosition.x, 0f, Mathf.Max(viewRect.width - position.width, 0f))
: 0f);
}
if (flag && verticalScrollbar != GUIStyle.none)
{
scrollPosition.y = VerticalScroll(
new Rect(
screenRect.xMax + (float)verticalScrollbar.margin.left,
screenRect.y,
verticalScrollbar.fixedWidth,
screenRect.height),
scrollPosition.y,
Mathf.Min(screenRect.height, viewRect.height),
0f,
viewRect.height,
verticalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.y = ((verticalScrollbar == GUIStyle.none)
? Mathf.Clamp(scrollPosition.y, 0f, Mathf.Max(viewRect.height - position.height, 0f))
: 0f);
}
}
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
}
GUIClip.Push(screenRect,
new Vector2(
Mathf.Round(-scrollPosition.x - viewRect.x),
Mathf.Round(-scrollPosition.y - viewRect.y)),
Vector2.zero,
false
);
return scrollPosition;
}
public static float HorizontalScroll(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
{
return Scroller(position, value, size, leftValue, rightValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "leftbutton"),
GUI.skin.GetStyle(style.name + "rightbutton"),
true);
}
public static float VerticalScroll(Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
{
return Scroller(position, value, size, topValue, bottomValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "upbutton"),
GUI.skin.GetStyle(style.name + "downbutton"),
false);
}
private static float Scroller(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle slider,
GUIStyle thumb, GUIStyle leftButton, GUIStyle rightButton, bool horiz)
{
GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
Rect position2;
Rect rect;
Rect rect2;
if (horiz)
{
position2 = new Rect(position.x + leftButton.fixedWidth,
position.y,
position.width - leftButton.fixedWidth - rightButton.fixedWidth,
position.height);
rect = new Rect(position.x, position.y, leftButton.fixedWidth, position.height);
rect2 = new Rect(position.xMax - rightButton.fixedWidth, position.y, rightButton.fixedWidth, position.height);
}
else
{
position2 = new Rect(position.x,
position.y + leftButton.fixedHeight,
position.width,
position.height - leftButton.fixedHeight - rightButton.fixedHeight);
rect = new Rect(position.x, position.y, position.width, leftButton.fixedHeight);
rect2 = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight);
}
value = Slider(position2, value, size, leftValue, rightValue, slider, thumb, horiz, controlID);
bool flag = Event.current.type == EventType.MouseUp;
if (ScrollerRepeatButton(controlID, rect, leftButton))
{
value -= 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (ScrollerRepeatButton(controlID, rect2, rightButton))
{
value += 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (flag && Event.current.type == EventType.Used)
{
s_ScrollControlId = 0;
}
if (leftValue < rightValue)
{
value = Mathf.Clamp(value, leftValue, rightValue - size);
}
else
{
value = Mathf.Clamp(value, rightValue, leftValue - size);
}
return value;
}
public static float Slider(Rect position, float value, float size, float start, float end, GUIStyle slider,
GUIStyle thumb, bool horiz, int id)
{
if (id == 0)
{
id = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
}
var sliderHandler = new Internal_SliderHandler(position, value, size, start, end, slider, thumb, horiz, id);
return sliderHandler.Handle();
}
private static bool ScrollerRepeatButton(int scrollerID, Rect rect, GUIStyle style)
{
bool result = false;
if (GUI.DoRepeatButton(rect, GUIContent.none, style, FocusType.Passive))
{
bool flag = s_ScrollControlId != scrollerID;
s_ScrollControlId = scrollerID;
if (flag)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
}
else if (DateTime.Now >= nextScrollStepTime)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
}
if (Event.current.type == EventType.Repaint)
{
GUI.InternalRepaintEditorWindow();
}
}
return result;
}
#endregion
}
#region Extensions
public static class Extensions
{
public static Rect Unstripped_GetLast(this GUILayoutGroup group)
{
Rect result;
if (group.m_Cursor > 0 && group.m_Cursor <= group.entries.Count)
{
GUILayoutEntry guilayoutEntry = group.entries[group.m_Cursor - 1];
result = guilayoutEntry.rect;
}
else
{
result = GUILayoutEntry.kDummyRect;
}
return result;
}
}
#endregion
}
#endif

View File

@ -1,38 +1,17 @@
using System; #if CPP
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using Explorer.UnstripInternals;
namespace Explorer namespace Explorer
{ {
public class LayoutUtilityUnstrip public class Internal_LayoutUtility
{ {
public static Rect GetRect(float width, float height) { return DoGetRect(width, width, height, height, GUIStyle.none, null); }
public static Rect GetRect(float width, float height, GUIStyle style) { return DoGetRect(width, width, height, height, style, null); }
public static Rect GetRect(float width, float height, params GUILayoutOption[] options) { return DoGetRect(width, width, height, height, GUIStyle.none, options); }
// Reserve layout space for a rectangle with a fixed content area.
public static Rect GetRect(float width, float height, GUIStyle style, params GUILayoutOption[] options) public static Rect GetRect(float width, float height, GUIStyle style, params GUILayoutOption[] options)
{ return DoGetRect(width, width, height, height, style, options); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, null); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, style, null); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, params GUILayoutOption[] options)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, options); }
// Reserve layout space for a flexible rect.
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, params GUILayoutOption[] options)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, style, options); }
static Rect DoGetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, GUILayoutOption[] options)
{ {
switch (Event.current.type) switch (Event.current.type)
{ {
case EventType.Layout: case EventType.Layout:
GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(minWidth, maxWidth, minHeight, maxHeight, style, options)); GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(width, width, height, height, style, options));
return GUILayoutUtility.kDummyRect; return GUILayoutUtility.kDummyRect;
case EventType.Used: case EventType.Used:
return GUILayoutUtility.kDummyRect; return GUILayoutUtility.kDummyRect;
@ -40,14 +19,14 @@ namespace Explorer
return GUILayoutUtility.current.topLevel.GetNext().rect; return GUILayoutUtility.current.topLevel.GetNext().rect;
} }
} }
public static Rect GetRect(GUIContent content, GUIStyle style) { return DoGetRect(content, style, null); }
// Reserve layout space for a rectangle for displaying some contents with a specific style. public static Rect GetRect(GUIContent content, GUIStyle style, params GUILayoutOption[] options)
public static Rect GetRect(GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoGetRect(content, style, options); } {
return DoGetRect(content, style, options);
}
static Rect DoGetRect(GUIContent content, GUIStyle style, GUILayoutOption[] options) static Rect DoGetRect(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{ {
GUIUtility.CheckOnGUI();
switch (Event.current.type) switch (Event.current.type)
{ {
case EventType.Layout: case EventType.Layout:
@ -100,13 +79,14 @@ namespace Explorer
Rect last; Rect last;
if (type != EventType.Layout && type != EventType.Used) if (type != EventType.Layout && type != EventType.Used)
{ {
last = GUILayoutUtility.current.topLevel.GetLastUnstripped(); last = GUILayoutUtility.current.topLevel.Unstripped_GetLast();
} }
else else
{ {
last = GUILayoutUtility.kDummyRect; last = GUILayoutUtility.kDummyRect;
} }
return last; return last;
} }
} }
} }
#endif

View File

@ -0,0 +1,29 @@
#if CPP
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Explorer
{
public class Internal_ScrollViewState
{
public Rect position;
public Rect visibleRect;
public Rect viewRect;
public Vector2 scrollPosition;
public bool apply;
public static Dictionary<IntPtr, Internal_ScrollViewState> Dict = new Dictionary<IntPtr, Internal_ScrollViewState>();
public static Internal_ScrollViewState FromPointer(IntPtr ptr)
{
if (!Dict.ContainsKey(ptr))
{
Dict.Add(ptr, new Internal_ScrollViewState());
}
return Dict[ptr];
}
}
}
#endif

View File

@ -1,15 +1,54 @@
using System; #if CPP
using System.Collections.Generic; using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine;
using Explorer.UnstripInternals;
using Il2CppSystem.Reflection;
namespace Explorer namespace Explorer
{ {
public struct SliderHandlerUnstrip public struct Internal_SliderHandler
{ {
public static int ScrollTroughSide
{
get
{
if (!m_getScrollTroughSideFailed)
{
try
{
return GUI.scrollTroughSide;
}
catch
{
m_getScrollTroughSideFailed = true;
}
}
return m_manualScrollTrough;
}
set
{
if (!m_setScrollTroughSideFailed)
{
try
{
GUI.scrollTroughSide = value;
return;
}
catch
{
m_setScrollTroughSideFailed = true;
}
}
m_manualScrollTrough = value;
}
}
private static bool m_getScrollTroughSideFailed;
private static bool m_setScrollTroughSideFailed;
private static int m_manualScrollTrough;
private readonly Rect position; private readonly Rect position;
private readonly float currentValue; private readonly float currentValue;
private readonly float size; private readonly float size;
@ -20,7 +59,8 @@ namespace Explorer
private readonly bool horiz; private readonly bool horiz;
private readonly int id; private readonly int id;
public SliderHandlerUnstrip(Rect position, float currentValue, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id) public Internal_SliderHandler(Rect position, float currentValue, float size, float start,
float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
{ {
this.position = position; this.position = position;
this.currentValue = currentValue; this.currentValue = currentValue;
@ -67,7 +107,7 @@ namespace Explorer
} }
else else
{ {
GUI.scrollTroughSide = 0; ScrollTroughSide = 0;
GUIUtility.hotControl = this.id; GUIUtility.hotControl = this.id;
this.CurrentEvent().Use(); this.CurrentEvent().Use();
if (this.ThumbSelectionRect().Contains(this.CurrentEvent().mousePosition)) if (this.ThumbSelectionRect().Contains(this.CurrentEvent().mousePosition))
@ -80,9 +120,10 @@ namespace Explorer
GUI.changed = true; GUI.changed = true;
if (this.SupportsPageMovements()) if (this.SupportsPageMovements())
{ {
this.SliderState().isDragging = false; var ext = Internal_SliderState.FromPointer(GetSliderState().Pointer);
GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0); ext.isDragging = false;
GUI.scrollTroughSide = this.CurrentScrollTroughSide(); Internal.nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
ScrollTroughSide = this.CurrentScrollTroughSide();
result = this.PageMovementValue(); result = this.PageMovementValue();
} }
else else
@ -105,8 +146,8 @@ namespace Explorer
} }
else else
{ {
SliderState sliderState = this.SliderState(); var ext = Internal_SliderState.FromPointer(GetSliderState().Pointer);
if (!sliderState.isDragging) if (!ext.isDragging)
{ {
result = this.currentValue; result = this.currentValue;
} }
@ -114,8 +155,8 @@ namespace Explorer
{ {
GUI.changed = true; GUI.changed = true;
this.CurrentEvent().Use(); this.CurrentEvent().Use();
float num = this.MousePosition() - sliderState.dragStartPos; float num = this.MousePosition() - ext.dragStartPos;
float value = sliderState.dragStartValue + num / this.ValuesPerPixel(); float value = ext.dragStartValue + num / this.ValuesPerPixel();
result = this.Clamp(value); result = this.Clamp(value);
} }
} }
@ -146,7 +187,7 @@ namespace Explorer
} }
else if (this.ThumbRect().Contains(this.CurrentEvent().mousePosition)) else if (this.ThumbRect().Contains(this.CurrentEvent().mousePosition))
{ {
if (GUI.scrollTroughSide != 0) if (ScrollTroughSide != 0)
{ {
GUIUtility.hotControl = 0; GUIUtility.hotControl = 0;
} }
@ -155,20 +196,20 @@ namespace Explorer
else else
{ {
GUI.InternalRepaintEditorWindow(); GUI.InternalRepaintEditorWindow();
if (DateTime.Now < GUIUnstrip.nextScrollStepTime) if (DateTime.Now < Internal.nextScrollStepTime)
{ {
result = this.currentValue; result = this.currentValue;
} }
else if (this.CurrentScrollTroughSide() != GUI.scrollTroughSide) else if (this.CurrentScrollTroughSide() != ScrollTroughSide)
{ {
result = this.currentValue; result = this.currentValue;
} }
else else
{ {
GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0); Internal.nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
if (this.SupportsPageMovements()) if (this.SupportsPageMovements())
{ {
this.SliderState().isDragging = false; Internal_SliderState.FromPointer(GetSliderState().Pointer).isDragging = false;
GUI.changed = true; GUI.changed = true;
result = this.PageMovementValue(); result = this.PageMovementValue();
} }
@ -264,15 +305,15 @@ namespace Explorer
private void StartDraggingWithValue(float dragStartValue) private void StartDraggingWithValue(float dragStartValue)
{ {
SliderState sliderState = this.SliderState(); var ext = Internal_SliderState.FromPointer(GetSliderState().Pointer);
sliderState.dragStartPos = this.MousePosition(); ext.dragStartPos = this.MousePosition();
sliderState.dragStartValue = dragStartValue; ext.dragStartValue = dragStartValue;
sliderState.isDragging = true; ext.isDragging = true;
} }
private SliderState SliderState() private SliderState GetSliderState()
{ {
return (SliderState)GUIUtility.GetStateObject(Il2CppType.Of<SliderState>(), this.id).TryCast<SliderState>(); return GUIUtility.GetStateObject(Il2CppType.Of<SliderState>(), this.id).TryCast<SliderState>();
} }
private Rect ThumbRect() private Rect ThumbRect()
@ -369,3 +410,4 @@ namespace Explorer
} }
} }
#endif

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Explorer
{
public class Internal_SliderState
{
public float dragStartPos;
public float dragStartValue;
public bool isDragging;
public static Dictionary<IntPtr, Internal_SliderState> Dict = new Dictionary<IntPtr, Internal_SliderState>();
public static Internal_SliderState FromPointer(IntPtr ptr)
{
if (!Dict.ContainsKey(ptr))
{
Dict.Add(ptr, new Internal_SliderState());
}
return Dict[ptr];
}
}
}

View File

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Harmony;
using MelonLoader;
using UnityEngine;
namespace Explorer
{
public class ScrollViewStateUnstrip
{
public Rect position;
public Rect visibleRect;
public Rect viewRect;
public Vector2 scrollPosition;
public bool apply;
public static Dictionary<IntPtr, ScrollViewStateUnstrip> Dict = new Dictionary<IntPtr, ScrollViewStateUnstrip>();
public static ScrollViewStateUnstrip FromPointer(IntPtr ptr)
{
if (!Dict.ContainsKey(ptr))
{
Dict.Add(ptr, new ScrollViewStateUnstrip());
}
return Dict[ptr];
}
}
}

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer
{
public static class UnstripExtensions
{
public static Rect GetLastUnstripped(this GUILayoutGroup group)
{
Rect result;
if (group.m_Cursor > 0 && group.m_Cursor <= group.entries.Count)
{
GUILayoutEntry guilayoutEntry = group.entries[group.m_Cursor - 1];
result = guilayoutEntry.rect;
}
else
{
result = GUILayoutEntry.kDummyRect;
}
return result;
}
}
}