Compare commits

...

45 Commits

Author SHA1 Message Date
995e2a3e93 3.1.11: fix potential crash on scene reload 2021-03-03 22:09:14 +11:00
2c95fec646 3.1.10: Add "Hide on startup" config option 2021-02-27 17:04:47 +11:00
69912d7ea4 Prevent GC Mark Overflow on C# Console copy+paste 2021-02-26 17:54:00 +11:00
9c5596ace4 Update lib versions 2021-02-26 17:52:21 +11:00
d4dac58fc8 Fix for deobfuscated unhollowed types not being properly resolved 2021-02-20 19:39:19 +11:00
77b97cbe17 Update README.md 2021-02-03 18:31:29 +11:00
c6f0f34ac0 Update README.md 2021-01-28 02:09:40 +11:00
d1f4f74d32 some ui cleanups (minor) 2021-01-22 21:56:00 +11:00
f13068bf01 Update README.md 2021-01-20 23:05:04 +11:00
dfc288a101 Update README.md 2021-01-20 19:10:13 +11:00
544009dc21 3.1.7
* Added standalone release build (thanks @Alloc86)
* Improved formatting for ToString methods which accept an IFormatProvider
* When editing a struct, the reference to the parent member will now be updated if you modify the struct values.
2021-01-20 17:22:36 +11:00
fdfaaadd89 3.1.6 - don't bother setting pixelPerfect on canvas 2021-01-14 17:46:32 +11:00
58d60a10d4 Update ForceUnlockCursor.cs 2021-01-04 01:23:20 +11:00
0432c6d56c 3.1.5
* Integrate PR from js6pak
2021-01-03 19:27:02 +11:00
8c34aa2be5 Merge pull request #29 from js6pak/embedded-assetbundle
Load assetbundle from EmbeddedResource
2021-01-03 19:13:15 +11:00
4a1c54fac1 Load assetbundle from EmbeddedResource 2021-01-02 19:38:01 +01:00
190467fa5c Update README.md 2020-12-31 18:34:26 +11:00
44f54d9190 3.1.4 2020-12-31 18:32:52 +11:00
3b4ea31b50 Fix Texture2D saver in Mono and for non-readable textures 2020-12-24 18:10:17 +11:00
ad7b05f721 Just disable EventSystem component and not the entire gameobject 2020-12-21 16:33:34 +11:00
852ca8e9eb New attempt at fixing conflicting EventSystem problems in IL2CPP 2020-12-16 14:28:54 +11:00
7386eca0c2 Update UIManager.cs 2020-12-15 19:33:04 +11:00
97325a5f3a Fix an issue causing duplicated clicks in some IL2CPP games, fix setting Component.enabled in IL2CPP 2020-12-15 19:32:50 +11:00
82e52de557 Cleanup and fix Singleton search slightly 2020-12-14 19:26:59 +11:00
28181e2266 Restoring Texture viewer/saver, and Static/Singleton class searching 2020-12-14 18:35:43 +11:00
6dfa4806ce 3.0.8
Reverting to the previous World-Raycast method as it gave more accurate/expected results
2020-12-12 23:24:44 +11:00
27f6a6ca52 Update README.md 2020-12-10 03:21:22 +11:00
cd7b260ea7 Fix in issue where the Behaviour Enabled toggle doesn't work in IL2CPP 2020-12-08 19:42:44 +11:00
ba986274be Bump version 2020-12-07 22:22:25 +11:00
d181c0bee9 Improved Enumerable and Dictionary enumeration in IL2CPP 2020-12-07 22:22:03 +11:00
a9faec8cf9 Some cleanups 2020-12-03 22:12:30 +11:00
a6bf91b7af accidentally replaced the original asset bundle 2020-11-25 16:47:13 +11:00
e7c5170232 3.0.5 2020-11-25 16:41:42 +11:00
be635e46a0 Swapping to INI instead of XML config, including an AssetBundle for old (5.6.1) Unity versions. 2020-11-25 16:40:36 +11:00
687f56eac9 Update README.md 2020-11-23 21:16:29 +11:00
1dfcdb2dca Update build instructions, remove unnecessary references from CS project file 2020-11-23 20:28:22 +11:00
0025c83930 Fix a preprocessor directive 2020-11-23 20:17:54 +11:00
cfa4b12039 3.0.4
see release notes
2020-11-23 18:23:25 +11:00
c7ccdf387c update version 2020-11-22 18:23:07 +11:00
bb46d77a02 3.0.3
* Fixed not being able to set values on Enums
* [MONO] Fixed an issue where GameObjects in no scene (a Resource/Asset) would display nothing for their scene name, instead of "None (Resource/Asset)".
* Some UI layout cleanups and fixes, the Child/Component lists on the GameObject inspector should now expand to fill available height.
2020-11-22 18:22:57 +11:00
c38155ab04 Fix for InputSystem in 3.0.0 (temp fix for il2cpp) 2020-11-20 17:12:40 +11:00
97dbecaa2a Made instance inspector helper (owner gameobject/name), force loading unhollowed Assembly-CSharp on game start 2020-11-19 16:47:18 +11:00
e77e4cce07 fix release links 2020-11-19 01:41:11 +11:00
dcf0bdce48 disabling EventSystem.current patch for bepinex il2cpp as its causing a crash in some games 2020-11-19 00:55:17 +11:00
2a3df5de9d cleanup CacheMembers a bit 2020-11-17 23:26:22 +11:00
54 changed files with 1931 additions and 1074 deletions

View File

@ -17,7 +17,6 @@
- [Features](#features)
- [How to install](#how-to-install)
- [Mod Config](#mod-config)
- [Mouse Control](#mouse-control)
- [Building](#building)
- [Credits](#credits)
@ -25,11 +24,9 @@
| Mod Loader | IL2CPP | Mono |
| ----------- | ------ | ---- |
| [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) |
| [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) |
<b>IL2CPP Issues:</b>
* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full debug log please).
| [BepInEx](https://github.com/BepInEx/BepInEx) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Mono.zip) |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) |
| Standalone | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
## Features
@ -52,19 +49,25 @@
0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game.
1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
2. Take the `UnityExplorer.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
3. Take the `UnityExplorer\` folder (with `explorerui.bundle`) and put it in `[GameFolder]\Mods\`, so it looks like `[GameFolder]\Mods\UnityExplorer\explorerui.bundle`.
4. In IL2CPP, it is highly recommended to get the base Unity libs for the game's Unity version and put them in the `BepInEx\unhollowed\base\` folder.
2. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
3. In IL2CPP, it is highly recommended to get the base Unity libs for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
### MelonLoader
0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) for your game.
1. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
2. Take the contents of the release and put it in the `[GameFolder]\Mods\` folder. It should look like `[GameFolder]\Mods\UnityExplorer.dll` and `[GameFolder]\Mods\UnityExplorer\explorerui.bundle`.
2. Take the contents of the release and put it in the `[GameFolder]\Mods\` folder. It should look like `[GameFolder]\Mods\UnityExplorer.ML.___.dll`
### Standalone
0. Load the DLL from your mod or inject it.
1. Create an instance of Unity Explorer with `new ExplorerCore();`
2. You will need to call ExplorerCore.Update() (static method) from your Update method.
3. Subscribe to the `ExplorerCore.OnLog__` methods for logging.
## Mod Config
You can access the settings via the "Options" page of the main menu, or directly from the config at `Mods\UnityExplorer\config.xml` (generated after first launch).
You can access the settings via the "Options" page of the main menu, or directly from the config at `Mods\UnityExplorer\config.ini` (generated after first launch).
`Main Menu Toggle` (KeyCode)
* Default: `F7`
@ -80,7 +83,7 @@ You can access the settings via the "Options" page of the main menu, or directly
* <b>Requires a restart to take effect</b>, apart from Reflection Inspector tabs.
`Default Output Path` (string)
* Default: `Mods\Explorer`
* Default: `Mods\UnityExplorer`
* Where output is generated to, by default (for Texture PNG saving, etc).
* Currently this is not actually used for anything, but it will be soon.
@ -92,10 +95,10 @@ You can access the settings via the "Options" page of the main menu, or directly
If you'd like to build this yourself, you will need to have installed BepInEx and/or MelonLoader for at least one Unity game. If you want to build all 4 versions, you will need at least one IL2CPP and one Mono game, with BepInEx and MelonLoader installed for both.
1. Install MelonLoader or BepInEx for your game.
2. Open the `src\Explorer.csproj` file in a text editor.
3. Set the relevant `GameFolder` values for the versions you want to build, eg. set `MLCppGameFolder` if you want to build for a MelonLoader IL2CPP game.
4. Open the `src\Explorer.sln` project.
1. Install BepInEx or MelonLoader for your game, or use the standalone build.
2. Open the `src\UnityExplorer.csproj` file in a text editor.
3. For IL2CPP builds, make sure you set `BIECppGameFolder` (for BepInEx) and/or `MLCppGameFolder` (for MelonLoader) so the project can locate the necessary references.
4. Open the `src\UnityExplorer.sln` project.
5. Select `Solution 'UnityExplorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> property to the version you want to build, then build it.
5. The DLLs are built to the `Release\` folder in the root of the repository.
6. If ILRepack fails or is missing, use the NuGet package manager to re-install `ILRepack.Lib.MSBuild.Task`, then re-build.

Binary file not shown.

Binary file not shown.

BIN
lib/INIFileParser.dll Normal file

Binary file not shown.

Binary file not shown.

View File

@ -32,7 +32,7 @@ namespace UnityExplorer.CSConsole
private Text inputHighlightText;
private readonly CSharpLexer highlightLexer;
private readonly StringBuilder sbHighlight;
//private readonly StringBuilder sbHighlight;
internal int m_lastCaretPos;
internal int m_fixCaretPos;
@ -68,7 +68,6 @@ The following helper methods are available:
public CodeEditor()
{
sbHighlight = new StringBuilder();
highlightLexer = new CSharpLexer();
ConstructUI();
@ -76,8 +75,24 @@ The following helper methods are available:
InputField.onValueChanged.AddListener((string s) => { OnInputChanged(s); });
}
internal static bool IsUserCopyPasting()
{
return (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
&& InputManager.GetKeyDown(KeyCode.V);
}
public void Update()
{
if (s_copyPasteBuffer != null)
{
if (!IsUserCopyPasting())
{
OnInputChanged(s_copyPasteBuffer);
s_copyPasteBuffer = null;
}
}
if (EnableCtrlRShortcut)
{
if ((InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
@ -149,11 +164,18 @@ The following helper methods are available:
AutoCompleter.ClearAutocompletes();
}
public void OnInputChanged(string newInput, bool forceUpdate = false)
{
string newText = newInput;
internal static string s_copyPasteBuffer;
UpdateIndent(newInput);
public void OnInputChanged(string newText, bool forceUpdate = false)
{
if (IsUserCopyPasting())
{
//Console.WriteLine("Copy+Paste detected!");
s_copyPasteBuffer = newText;
return;
}
UpdateIndent(newText);
if (!forceUpdate && string.IsNullOrEmpty(newText))
inputHighlightText.text = string.Empty;
@ -203,35 +225,29 @@ The following helper methods are available:
{
int offset = 0;
sbHighlight.Length = 0;
//Console.WriteLine("Highlighting input text:\r\n" + inputText);
string ret = "";
foreach (LexerMatchInfo match in highlightLexer.GetMatches(inputText))
{
for (int i = offset; i < match.startIndex; i++)
{
sbHighlight.Append(inputText[i]);
}
ret += inputText[i];
sbHighlight.Append($"{match.htmlColor}");
ret += $"{match.htmlColor}";
for (int i = match.startIndex; i < match.endIndex; i++)
{
sbHighlight.Append(inputText[i]);
}
ret += inputText[i];
sbHighlight.Append(CLOSE_COLOR_TAG);
ret += CLOSE_COLOR_TAG;
offset = match.endIndex;
}
for (int i = offset; i < inputText.Length; i++)
{
sbHighlight.Append(inputText[i]);
}
ret += inputText[i];
inputText = sbHighlight.ToString();
return inputText;
return ret;
}
private void AutoIndentCaret()

View File

@ -1,26 +1,31 @@
using System;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
using IniParser;
using IniParser.Parser;
namespace UnityExplorer.Config
{
public class ModConfig
{
[XmlIgnore] public static readonly XmlSerializer Serializer = new XmlSerializer(typeof(ModConfig));
public static ModConfig Instance;
//[XmlIgnore] private const string EXPLORER_FOLDER = @"Mods\UnityExplorer";
[XmlIgnore] private const string SETTINGS_PATH = ExplorerCore.EXPLORER_FOLDER + @"\config.xml";
internal static readonly IniDataParser _parser = new IniDataParser();
internal static readonly string INI_PATH = Path.Combine(ExplorerCore.EXPLORER_FOLDER, "config.ini");
[XmlIgnore] public static ModConfig Instance;
static ModConfig()
{
_parser.Configuration.CommentString = "#";
}
// Actual configs
public KeyCode Main_Menu_Toggle = KeyCode.F7;
public bool Force_Unlock_Mouse = true;
public int Default_Page_Limit = 25;
public string Default_Output_Path = ExplorerCore.EXPLORER_FOLDER;
public string Default_Output_Path = ExplorerCore.EXPLORER_FOLDER + @"\Output";
public bool Log_Unity_Debug = false;
public bool Save_Logs_To_Disk = true;
public bool Hide_On_Startup = false;
//public bool Save_Logs_To_Disk = true;
public static event Action OnConfigChanged;
@ -31,38 +36,70 @@ namespace UnityExplorer.Config
public static void OnLoad()
{
Instance = new ModConfig();
if (LoadSettings())
return;
Instance = new ModConfig();
SaveSettings();
}
public static bool LoadSettings()
{
if (!File.Exists(SETTINGS_PATH))
if (!File.Exists(INI_PATH))
return false;
try
string ini = File.ReadAllText(INI_PATH);
var data = _parser.Parse(ini);
foreach (var config in data.Sections["Config"])
{
using (var file = File.OpenRead(SETTINGS_PATH))
Instance = (ModConfig)Serializer.Deserialize(file);
}
catch
{
return false;
switch (config.KeyName)
{
case nameof(Main_Menu_Toggle):
Instance.Main_Menu_Toggle = (KeyCode)Enum.Parse(typeof(KeyCode), config.Value);
break;
case nameof(Force_Unlock_Mouse):
Instance.Force_Unlock_Mouse = bool.Parse(config.Value);
break;
case nameof(Default_Page_Limit):
Instance.Default_Page_Limit = int.Parse(config.Value);
break;
case nameof(Log_Unity_Debug):
Instance.Log_Unity_Debug = bool.Parse(config.Value);
break;
case nameof(Default_Output_Path):
Instance.Default_Output_Path = config.Value;
break;
case nameof(Hide_On_Startup):
Instance.Hide_On_Startup = bool.Parse(config.Value);
break;
//case nameof(Save_Logs_To_Disk):
// Instance.Save_Logs_To_Disk = bool.Parse(config.Value);
// break;
}
}
return Instance != null;
return true;
}
public static void SaveSettings()
{
if (File.Exists(SETTINGS_PATH))
File.Delete(SETTINGS_PATH);
var data = new IniParser.Model.IniData();
using (var file = File.Create(SETTINGS_PATH))
Serializer.Serialize(file, Instance);
data.Sections.AddSection("Config");
var sec = data.Sections["Config"];
sec.AddKey(nameof(Main_Menu_Toggle), Instance.Main_Menu_Toggle.ToString());
sec.AddKey(nameof(Force_Unlock_Mouse), Instance.Force_Unlock_Mouse.ToString());
sec.AddKey(nameof(Default_Page_Limit), Instance.Default_Page_Limit.ToString());
sec.AddKey(nameof(Log_Unity_Debug), Instance.Log_Unity_Debug.ToString());
sec.AddKey(nameof(Default_Output_Path), Instance.Default_Output_Path);
sec.AddKey(nameof(Hide_On_Startup), Instance.Hide_On_Startup.ToString());
//sec.AddKey("Save_Logs_To_Disk", Instance.Save_Logs_To_Disk.ToString());
File.WriteAllText(INI_PATH, data.ToString());
}
}
}

View File

@ -1,23 +1,45 @@
using System;
using System.IO;
using System.Reflection;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityExplorer.Config;
using UnityExplorer.Helpers;
using UnityExplorer.Input;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Modules;
using UnityEngine;
using UnityExplorer.Inspectors;
using System.IO;
using UnityExplorer.Unstrip;
using UnityEngine.SceneManagement;
namespace UnityExplorer
{
public class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "3.0.1";
public const string VERSION = "3.1.11";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";
public const string EXPLORER_FOLDER = @"Mods\UnityExplorer";
#if ML
public static string EXPLORER_FOLDER = Path.Combine("Mods", NAME);
#elif BIE
public static string EXPLORER_FOLDER = Path.Combine(BepInEx.Paths.ConfigPath, NAME);
#elif STANDALONE
public static string EXPLORER_FOLDER
{
get
{
if (s_explorerFolder == null)
{
s_explorerFolder = (new Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath;
s_explorerFolder = Uri.UnescapeDataString(s_explorerFolder);
s_explorerFolder = Path.GetDirectoryName(s_explorerFolder);
}
return s_explorerFolder;
}
}
private static string s_explorerFolder;
#endif
public static ExplorerCore Instance { get; private set; }
@ -41,6 +63,10 @@ namespace UnityExplorer
Instance = this;
#if CPP
ReflectionHelpers.TryLoadGameModules();
#endif
if (!Directory.Exists(EXPLORER_FOLDER))
Directory.CreateDirectory(EXPLORER_FOLDER);
@ -84,6 +110,11 @@ namespace UnityExplorer
{
UIManager.Init();
Log("Initialized UnityExplorer UI.");
if (ModConfig.Instance.Hide_On_Startup)
ShowMenu = false;
// InspectorManager.Instance.Inspect(Tests.TestClass.Instance);
}
catch (Exception e)
{
@ -154,6 +185,12 @@ namespace UnityExplorer
}
}
#if STANDALONE
public static Action<string> OnLogMessage;
public static Action<string> OnLogWarning;
public static Action<string> OnLogError;
#endif
public static void Log(object message, bool unity = false)
{
DebugConsole.Log(message?.ToString());
@ -163,8 +200,10 @@ namespace UnityExplorer
#if ML
MelonLoader.MelonLogger.Log(message?.ToString());
#else
#elif BIE
ExplorerBepInPlugin.Logging?.LogMessage(message?.ToString());
#elif STANDALONE
OnLogMessage?.Invoke(message?.ToString());
#endif
}
@ -177,8 +216,10 @@ namespace UnityExplorer
#if ML
MelonLoader.MelonLogger.LogWarning(message?.ToString());
#else
ExplorerBepInPlugin.Logging?.LogWarning(message?.ToString());
#elif BIE
ExplorerBepInPlugin.Logging?.LogWarning(message?.ToString());
#elif STANDALONE
OnLogWarning?.Invoke(message?.ToString());
#endif
}
@ -191,8 +232,10 @@ namespace UnityExplorer
#if ML
MelonLoader.MelonLogger.LogError(message?.ToString());
#else
ExplorerBepInPlugin.Logging?.LogError(message?.ToString());
#elif BIE
ExplorerBepInPlugin.Logging?.LogError(message?.ToString());
#elif STANDALONE
OnLogError?.Invoke(message?.ToString());
#endif
}

11
src/ExplorerStandalone.cs Normal file
View File

@ -0,0 +1,11 @@
#if STANDALONE
using HarmonyLib;
namespace UnityExplorer
{
public class ExplorerStandalone
{
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
}
}
#endif

View File

@ -52,7 +52,7 @@ namespace UnityExplorer.Helpers
return list.ToArray();
}
public static Type GetActualType(object obj)
public static Type GetActualType(this object obj)
{
if (obj == null)
return null;
@ -101,25 +101,52 @@ namespace UnityExplorer.Helpers
return Il2CppToMonoType[cppType];
var getType = Type.GetType(cppType.AssemblyQualifiedName);
Il2CppToMonoType.Add(cppType, getType);
return getType;
if (getType != null)
{
Il2CppToMonoType.Add(cppType, getType);
return getType;
}
else
{
string baseName = cppType.FullName;
string baseAssembly = cppType.Assembly.GetName().name;
Type unhollowedType = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == baseAssembly)?.GetTypes().FirstOrDefault(t =>
t.CustomAttributes.Any(ca =>
ca.AttributeType.Name == "ObfuscatedNameAttribute" && (string)ca.ConstructorArguments[0].Value == baseName));
Il2CppToMonoType.Add(cppType, unhollowedType);
return unhollowedType;
}
}
private static readonly Dictionary<Type, IntPtr> ClassPointers = new Dictionary<Type, IntPtr>();
private static readonly Dictionary<Type, IntPtr> CppClassPointers = new Dictionary<Type, IntPtr>();
/// <summary>
/// Attempt to cast the object to its underlying type.
/// </summary>
/// <param name="obj">The object you want to cast.</param>
/// <returns>The object, as the underlying type if successful or the input value if not.</returns>
public static object Il2CppCast(this object obj) => Il2CppCast(obj, GetActualType(obj));
/// <summary>
/// Attempt to cast the object to the provided type.
/// </summary>
/// <param name="obj">The object you want to cast.</param>
/// <param name="castTo">The Type you want to cast to.</param>
/// <returns>The object, as the type (or a normal C# object) if successful or the input value if not.</returns>
public static object Il2CppCast(this object obj, Type castTo)
{
if (!(obj is Il2CppSystem.Object ilObj))
{
return obj;
}
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
return obj;
IntPtr classPtr = il2cpp_object_get_class(ilObj.Pointer);
IntPtr castFromPtr = il2cpp_object_get_class(ilObj.Pointer);
if (!il2cpp_class_is_assignable_from(castToPtr, classPtr))
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
return obj;
if (RuntimeSpecificsStore.IsInjected(castToPtr))
@ -128,24 +155,54 @@ namespace UnityExplorer.Helpers
return Activator.CreateInstance(castTo, ilObj.Pointer);
}
public static bool Il2CppTypeNotNull(Type type)
internal static readonly Dictionary<Type, MethodInfo> s_unboxMethods = new Dictionary<Type, MethodInfo>();
/// <summary>
/// Attempt to unbox the object to the underlying struct type.
/// </summary>
/// <param name="obj">The object which is a struct underneath.</param>
/// <returns>The struct if successful, otherwise null.</returns>
public static object Unbox(this object obj) => Unbox(obj, GetActualType(obj));
/// <summary>
/// Attempt to unbox the object to the struct type.
/// </summary>
/// <param name="obj">The object which is a struct underneath.</param>
/// <param name="type">The type of the struct you want to unbox to.</param>
/// <returns>The struct if successful, otherwise null.</returns>
public static object Unbox(this object obj, Type type)
{
return Il2CppTypeNotNull(type, out _);
if (!type.IsValueType)
return null;
if (!(obj is Il2CppSystem.Object))
return obj;
if (!s_unboxMethods.ContainsKey(type))
{
s_unboxMethods.Add(type, typeof(Il2CppObjectBase)
.GetMethod("Unbox")
.MakeGenericMethod(type));
}
return s_unboxMethods[type].Invoke(obj, new object[0]);
}
public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _);
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
{
if (!ClassPointers.ContainsKey(type))
if (!CppClassPointers.ContainsKey(type))
{
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { type })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
ClassPointers.Add(type, il2cppPtr);
CppClassPointers.Add(type, il2cppPtr);
}
else
il2cppPtr = ClassPointers[type];
il2cppPtr = CppClassPointers[type];
return il2cppPtr != IntPtr.Zero;
}
@ -181,76 +238,95 @@ namespace UnityExplorer.Helpers
}
}
#if CPP
internal static void TryLoadGameModules()
{
LoadModule("Assembly-CSharp");
LoadModule("Assembly-CSharp-firstpass");
}
public static bool LoadModule(string module)
{
#if CPP
#if ML
string path = $@"MelonLoader\Managed\{module}.dll";
var path = $@"MelonLoader\Managed\{module}.dll";
#else
var path = $@"BepInEx\unhollowed\{module}.dll";
#endif
if (!File.Exists(path))
{
return LoadModuleInternal(path);
}
internal static bool LoadModuleInternal(string fullPath)
{
if (!File.Exists(fullPath))
return false;
}
try
{
Assembly.Load(File.ReadAllBytes(path));
Assembly.Load(File.ReadAllBytes(fullPath));
return true;
}
catch (Exception e)
{
ExplorerCore.Log(e.GetType() + ", " + e.Message);
Console.WriteLine(e.GetType() + ", " + e.Message);
}
#endif
return false;
}
#else
public static bool LoadModule(string module) => true;
#endif
#if CPP
internal static IntPtr s_cppEnumerableClassPtr;
#endif
public static bool IsEnumerable(Type t)
{
if (typeof(IEnumerable).IsAssignableFrom(t))
{
return true;
#if CPP
try
{
if (s_cppEnumerableClassPtr == IntPtr.Zero)
Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr);
if (s_cppEnumerableClassPtr != IntPtr.Zero
&& Il2CppTypeNotNull(t, out IntPtr classPtr)
&& il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, classPtr))
{
return true;
}
}
catch { }
#endif
return false;
}
#if CPP
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g);
}
else
{
return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
}
#else
return false;
internal static IntPtr s_cppDictionaryClassPtr;
#endif
}
public static bool IsDictionary(Type t)
{
if (typeof(IDictionary).IsAssignableFrom(t))
{
return true;
}
#if CPP
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
try
{
return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g);
if (s_cppDictionaryClassPtr == IntPtr.Zero)
if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr))
return false;
if (Il2CppTypeNotNull(t, out IntPtr classPtr))
{
if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr))
return true;
}
}
else
{
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t)
|| typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t);
}
#else
return false;
catch { }
#endif
return false;
}
public static string ExceptionToString(Exception e, bool innerMost = false)

View File

@ -10,12 +10,21 @@ namespace UnityExplorer.Helpers
{
public static class Texture2DHelpers
{
#if CPP // If Mono
#else
#if MONO
private static bool isNewEncodeMethod = false;
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
private static MethodInfo m_encodeToPNGMethod;
public static byte[] EncodeToPNGSafe(this Texture2D tex)
{
var method = EncodeToPNGMethod;
if (method.IsStatic)
return (byte[])method.Invoke(null, new object[] { tex });
else
return (byte[])method.Invoke(tex, new object[0]);
}
private static MethodInfo GetEncodeToPNGMethod()
{
if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
@ -35,7 +44,6 @@ namespace UnityExplorer.Helpers
}
#endif
public static bool IsReadable(this Texture2D tex)
{
try
@ -53,7 +61,7 @@ namespace UnityExplorer.Helpers
}
}
public static Texture2D Copy(Texture2D orig, Rect rect) //, bool isDTXnmNormal = false)
public static Texture2D Copy(Texture2D orig, Rect rect)
{
Color[] pixels;
@ -62,12 +70,21 @@ namespace UnityExplorer.Helpers
pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
// use full constructor for better compatibility
#if CPP
var _newTex = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
#else
var _newTex = new Texture2D((int)rect.width, (int)rect.height);
#endif
_newTex.SetPixels(pixels);
return _newTex;
}
#if CPP
internal delegate void d_Blit2(IntPtr source, IntPtr dest);
#endif
public static Texture2D ForceReadTexture(Texture2D tex)
{
try
@ -78,7 +95,13 @@ namespace UnityExplorer.Helpers
var rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);
rt.filterMode = FilterMode.Point;
RenderTexture.active = rt;
#if MONO
Graphics.Blit(tex, rt);
#else
var iCall = ICallHelper.GetICall<d_Blit2>("UnityEngine.Graphics::Blit2");
iCall.Invoke(tex.Pointer, rt.Pointer);
#endif
var _newTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);

View File

@ -5,6 +5,7 @@
<ItemGroup>
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
<InputAssemblies Include="..\lib\mcs.dll" />
<InputAssemblies Include="..\lib\INIFileParser.dll" />
</ItemGroup>
<ILRepack

View File

@ -1,4 +1,5 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace UnityExplorer.Input
{
@ -11,5 +12,12 @@ namespace UnityExplorer.Input
bool GetMouseButtonDown(int btn);
bool GetMouseButton(int btn);
BaseInputModule UIModule { get; }
PointerEventData InputPointerEvent { get; }
void AddUIInputModule();
void ActivateModule();
}
}

View File

@ -2,34 +2,26 @@
using UnityEngine;
using UnityExplorer.Helpers;
using System.Diagnostics.CodeAnalysis;
using UnityEngine.EventSystems;
#if CPP
using UnhollowerBaseLib;
#endif
namespace UnityExplorer.Input
{
public enum InputType
{
InputSystem,
Legacy,
None
}
public static class InputManager
{
public static InputType CurrentType { get; private set; }
private static IHandleInput m_inputModule;
public static void Init()
{
if (InputSystem.TKeyboard != null || (ReflectionHelpers.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
{
m_inputModule = new InputSystem();
}
else if (LegacyInput.TInput != null || (ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
{
m_inputModule = new LegacyInput();
}
if (m_inputModule == null)
{
ExplorerCore.LogWarning("Could not find any Input module!");
m_inputModule = new NoInput();
}
}
public static Vector3 MousePosition => m_inputModule.MousePosition;
public static bool GetKeyDown(KeyCode key) => m_inputModule.GetKeyDown(key);
@ -37,5 +29,37 @@ namespace UnityExplorer.Input
public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);
public static BaseInputModule UIInput => m_inputModule.UIModule;
public static PointerEventData InputPointerEvent => m_inputModule.InputPointerEvent;
public static void ActivateUIModule() => m_inputModule.ActivateModule();
public static void AddUIModule()
{
m_inputModule.AddUIInputModule();
ActivateUIModule();
}
public static void Init()
{
if (InputSystem.TKeyboard != null || (ReflectionHelpers.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
{
m_inputModule = new InputSystem();
CurrentType = InputType.InputSystem;
}
else if (LegacyInput.TInput != null || (ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
{
m_inputModule = new LegacyInput();
CurrentType = InputType.Legacy;
}
if (m_inputModule == null)
{
ExplorerCore.LogWarning("Could not find any Input module!");
m_inputModule = new NoInput();
CurrentType = InputType.None;
}
}
}
}

View File

@ -2,6 +2,9 @@
using System.Reflection;
using UnityExplorer.Helpers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
using System.Collections.Generic;
namespace UnityExplorer.Input
{
@ -64,24 +67,46 @@ namespace UnityExplorer.Input
private static PropertyInfo m_positionProp;
private static MethodInfo m_readVector2InputMethod;
public Vector2 MousePosition => (Vector2)m_readVector2InputMethod.Invoke(MousePositionInfo, new object[0]);
public bool GetKeyDown(KeyCode key)
public Vector2 MousePosition
{
var parsedKey = Enum.Parse(TKey, key.ToString());
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsedKey });
return (bool)m_btnWasPressedProp.GetValue(actualKey, null);
get
{
try
{
return (Vector2)m_readVector2InputMethod.Invoke(MousePositionInfo, new object[0]);
}
catch
{
return Vector2.zero;
}
}
}
public bool GetKey(KeyCode key)
{
var parsed = Enum.Parse(TKey, key.ToString());
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
internal static Dictionary<KeyCode, object> ActualKeyDict = new Dictionary<KeyCode, object>();
return (bool)m_btnIsPressedProp.GetValue(actualKey, null);
internal object GetActualKey(KeyCode key)
{
if (!ActualKeyDict.ContainsKey(key))
{
var s = key.ToString();
if (s.Contains("Control"))
s = s.Replace("Control", "Ctrl");
else if (s.Contains("Return"))
s = "Enter";
var parsed = Enum.Parse(TKey, s);
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
ActualKeyDict.Add(key, actualKey);
}
return ActualKeyDict[key];
}
public bool GetKeyDown(KeyCode key) => (bool)m_btnWasPressedProp.GetValue(GetActualKey(key), null);
public bool GetKey(KeyCode key) => (bool)m_btnIsPressedProp.GetValue(GetActualKey(key), null);
public bool GetMouseButtonDown(int btn)
{
switch (btn)
@ -103,5 +128,44 @@ namespace UnityExplorer.Input
default: throw new NotImplementedException();
}
}
// UI Input
//public Type TInputSystemUIInputModule
// => m_tUIInputModule
// ?? (m_tUIInputModule = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
//internal Type m_tUIInputModule;
public BaseInputModule UIModule => null; // m_newInputModule;
//internal BaseInputModule m_newInputModule;
public PointerEventData InputPointerEvent => null;
public void AddUIInputModule()
{
// if (TInputSystemUIInputModule != null)
// {
//#if CPP
// // m_newInputModule = UIManager.CanvasRoot.AddComponent(Il2CppType.From(TInputSystemUIInputModule)).TryCast<BaseInputModule>();
//#else
// m_newInputModule = (BaseInputModule)UIManager.CanvasRoot.AddComponent(TInputSystemUIInputModule);
//#endif
// }
// else
// {
// ExplorerCore.LogWarning("New input system: Could not find type by name 'UnityEngine.InputSystem.UI.InputSystemUIInputModule'");
// }
}
public void ActivateModule()
{
//#if CPP
// // m_newInputModule.ActivateModule();
//#else
// m_newInputModule.ActivateModule();
//#endif
}
}
}

View File

@ -2,6 +2,8 @@
using System.Reflection;
using UnityExplorer.Helpers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
namespace UnityExplorer.Input
{
@ -36,5 +38,27 @@ namespace UnityExplorer.Input
public bool GetMouseButton(int btn) => (bool)m_getMouseButtonMethod.Invoke(null, new object[] { btn });
public bool GetMouseButtonDown(int btn) => (bool)m_getMouseButtonDownMethod.Invoke(null, new object[] { btn });
// UI Input module
public BaseInputModule UIModule => m_inputModule;
internal StandaloneInputModule m_inputModule;
public PointerEventData InputPointerEvent =>
#if CPP
m_inputModule.m_InputPointerEvent;
#else
null;
#endif
public void AddUIInputModule()
{
m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>();
}
public void ActivateModule()
{
m_inputModule.ActivateModule();
}
}
}

View File

@ -1,4 +1,5 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace UnityExplorer.Input
{
@ -13,5 +14,10 @@ namespace UnityExplorer.Input
public bool GetMouseButton(int btn) => false;
public bool GetMouseButtonDown(int btn) => false;
public BaseInputModule UIModule => null;
public PointerEventData InputPointerEvent => null;
public void ActivateModule() { }
public void AddUIInputModule() { }
}
}

View File

@ -140,7 +140,7 @@ namespace UnityExplorer.Inspectors.GameObjects
{
var vertGroupObj = UIFactory.CreateVerticalGroup(parent, new Color(1, 1, 1, 0));
var vertGroup = vertGroupObj.GetComponent<VerticalLayoutGroup>();
vertGroup.childForceExpandHeight = false;
vertGroup.childForceExpandHeight = true;
vertGroup.childForceExpandWidth = false;
vertGroup.childControlWidth = true;
vertGroup.spacing = 5;

View File

@ -80,7 +80,11 @@ namespace UnityExplorer.Inspectors.GameObjects
text.text = UISyntaxHighlight.ParseFullSyntax(ReflectionHelpers.GetActualType(comp), true);
var toggle = s_compToggles[i];
#if CPP
if (comp.TryCast<Behaviour>() is Behaviour behaviour)
#else
if (comp is Behaviour behaviour)
#endif
{
if (!toggle.gameObject.activeSelf)
toggle.gameObject.SetActive(true);
@ -107,8 +111,11 @@ namespace UnityExplorer.Inspectors.GameObjects
internal static void OnCompToggleClicked(int index, bool value)
{
var comp = s_compShortlist[index];
#if CPP
comp.TryCast<Behaviour>().enabled = value;
#else
(comp as Behaviour).enabled = value;
#endif
}
internal static void OnCompListObjectClicked(int index)
@ -130,13 +137,13 @@ namespace UnityExplorer.Inspectors.GameObjects
}
#region UI CONSTRUCTION
#region UI CONSTRUCTION
internal void ConstructCompList(GameObject parent)
{
var vertGroupObj = UIFactory.CreateVerticalGroup(parent, new Color(1, 1, 1, 0));
var vertGroup = vertGroupObj.GetComponent<VerticalLayoutGroup>();
vertGroup.childForceExpandHeight = false;
vertGroup.childForceExpandHeight = true;
vertGroup.childForceExpandWidth = false;
vertGroup.childControlWidth = true;
vertGroup.spacing = 5;
@ -157,6 +164,7 @@ namespace UnityExplorer.Inspectors.GameObjects
var compScrollObj = UIFactory.CreateScrollView(vertGroupObj, out s_compListContent, out SliderScrollbar scroller, new Color(0.07f, 0.07f, 0.07f));
var contentLayout = compScrollObj.AddComponent<LayoutElement>();
contentLayout.minHeight = 50;
contentLayout.flexibleHeight = 5000;
s_compListPageHandler = new PageHandler(scroller);
s_compListPageHandler.ConstructUI(vertGroupObj);
@ -167,34 +175,34 @@ namespace UnityExplorer.Inspectors.GameObjects
{
int thisIndex = s_compListTexts.Count;
GameObject btnGroupObj = UIFactory.CreateHorizontalGroup(s_compListContent, new Color(0.07f, 0.07f, 0.07f));
HorizontalLayoutGroup btnGroup = btnGroupObj.GetComponent<HorizontalLayoutGroup>();
btnGroup.childForceExpandWidth = true;
btnGroup.childControlWidth = true;
btnGroup.childForceExpandHeight = false;
btnGroup.childControlHeight = true;
btnGroup.childAlignment = TextAnchor.MiddleLeft;
LayoutElement btnLayout = btnGroupObj.AddComponent<LayoutElement>();
btnLayout.minWidth = 25;
btnLayout.flexibleWidth = 999;
btnLayout.minHeight = 25;
btnLayout.flexibleHeight = 0;
btnGroupObj.AddComponent<Mask>();
GameObject groupObj = UIFactory.CreateHorizontalGroup(s_compListContent, new Color(0.07f, 0.07f, 0.07f));
HorizontalLayoutGroup group = groupObj.GetComponent<HorizontalLayoutGroup>();
group.childForceExpandWidth = true;
group.childControlWidth = true;
group.childForceExpandHeight = false;
group.childControlHeight = true;
group.childAlignment = TextAnchor.MiddleLeft;
LayoutElement groupLayout = groupObj.AddComponent<LayoutElement>();
groupLayout.minWidth = 25;
groupLayout.flexibleWidth = 999;
groupLayout.minHeight = 25;
groupLayout.flexibleHeight = 0;
groupObj.AddComponent<Mask>();
// Behaviour enabled toggle
var toggleObj = UIFactory.CreateToggle(btnGroupObj, out Toggle toggle, out Text toggleText, new Color(0.3f, 0.3f, 0.3f));
var toggleObj = UIFactory.CreateToggle(groupObj, out Toggle toggle, out Text toggleText, new Color(0.3f, 0.3f, 0.3f));
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
toggleLayout.minHeight = 25;
toggleLayout.minWidth = 25;
toggleText.text = "";
toggle.isOn = false;
toggle.isOn = true;
s_compToggles.Add(toggle);
toggle.onValueChanged.AddListener((bool val) => { OnCompToggleClicked(thisIndex, val); });
// Main component button
GameObject mainButtonObj = UIFactory.CreateButton(btnGroupObj);
GameObject mainButtonObj = UIFactory.CreateButton(groupObj);
LayoutElement mainBtnLayout = mainButtonObj.AddComponent<LayoutElement>();
mainBtnLayout.minHeight = 25;
mainBtnLayout.flexibleHeight = 0;
@ -223,6 +231,6 @@ namespace UnityExplorer.Inspectors.GameObjects
}
#endregion
#endregion
}
}

View File

@ -164,7 +164,7 @@ namespace UnityExplorer.Inspectors
m_layerDropdown.value = TargetGO.layer;
}
if (m_lastScene != TargetGO.scene.name)
if (string.IsNullOrEmpty(m_lastScene) || m_lastScene != TargetGO.scene.name)
{
m_lastScene = TargetGO.scene.name;
@ -217,10 +217,20 @@ namespace UnityExplorer.Inspectors
s_content = UIFactory.CreateScrollView(parent, out GameObject scrollContent, out _, new Color(0.1f, 0.1f, 0.1f));
var parentLayout = scrollContent.transform.parent.gameObject.AddComponent<VerticalLayoutGroup>();
parentLayout.childForceExpandWidth = true;
parentLayout.childControlWidth = true;
parentLayout.childForceExpandHeight = true;
parentLayout.childControlHeight = true;
var scrollGroup = scrollContent.GetComponent<VerticalLayoutGroup>();
scrollGroup.childForceExpandHeight = true;
scrollGroup.childControlHeight = true;
scrollGroup.childForceExpandWidth = true;
scrollGroup.childControlWidth = true;
scrollGroup.spacing = 5;
var contentFitter = scrollContent.GetComponent<ContentSizeFitter>();
contentFitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
ConstructTopArea(scrollContent);
@ -230,6 +240,9 @@ namespace UnityExplorer.Inspectors
s_childList.ConstructChildList(midGroupObj);
s_compList.ConstructCompList(midGroupObj);
LayoutRebuilder.ForceRebuildLayoutImmediate(s_content.GetComponent<RectTransform>());
Canvas.ForceUpdateCanvases();
}
private void ConstructTopArea(GameObject scrollContent)
@ -431,11 +444,10 @@ namespace UnityExplorer.Inspectors
midGroup.childControlWidth = true;
midGroup.childForceExpandHeight = true;
midGroup.childControlHeight = true;
var midlayout = midGroupObj.AddComponent<LayoutElement>();
midlayout.minHeight = 350;
midlayout.flexibleHeight = 10000;
midlayout.minWidth = 200;
midlayout.flexibleWidth = 25000;
var midLayout = midGroupObj.AddComponent<LayoutElement>();
midLayout.minHeight = 300;
midLayout.flexibleHeight = 5000;
return midGroupObj;
}

View File

@ -38,7 +38,7 @@ namespace UnityExplorer.Inspectors
}
}
public void Inspect(object obj)
public void Inspect(object obj, CacheObjectBase parentMember = null)
{
#if CPP
obj = obj.Il2CppCast(ReflectionHelpers.GetActualType(obj));
@ -76,6 +76,9 @@ namespace UnityExplorer.Inspectors
else
inspector = new InstanceInspector(obj);
if (inspector is ReflectionInspector ri)
ri.ParentMember = parentMember;
m_currentInspectors.Add(inspector);
SetInspectorTab(inspector);
}
@ -230,102 +233,35 @@ namespace UnityExplorer.Inspectors
invisGroup.padding.right = 2;
invisGroup.spacing = 10;
// // time scale group
// var timeGroupObj = UIFactory.CreateHorizontalGroup(invisObj, new Color(1, 1, 1, 0));
// var timeGroup = timeGroupObj.GetComponent<HorizontalLayoutGroup>();
// timeGroup.childForceExpandWidth = false;
// timeGroup.childControlWidth = true;
// timeGroup.childForceExpandHeight = false;
// timeGroup.childControlHeight = true;
// timeGroup.padding.top = 2;
// timeGroup.padding.left = 5;
// timeGroup.padding.right = 2;
// timeGroup.padding.bottom = 2;
// timeGroup.spacing = 5;
// timeGroup.childAlignment = TextAnchor.MiddleCenter;
// var timeGroupLayout = timeGroupObj.AddComponent<LayoutElement>();
// timeGroupLayout.minWidth = 100;
// timeGroupLayout.flexibleWidth = 300;
// timeGroupLayout.minHeight = 25;
// timeGroupLayout.flexibleHeight = 0;
// // time scale title
// var timeTitleObj = UIFactory.CreateLabel(timeGroupObj, TextAnchor.MiddleLeft);
// var timeTitle = timeTitleObj.GetComponent<Text>();
// timeTitle.text = "Time Scale:";
// timeTitle.color = new Color(21f / 255f, 192f / 255f, 235f / 255f);
// var titleLayout = timeTitleObj.AddComponent<LayoutElement>();
// titleLayout.minHeight = 25;
// titleLayout.minWidth = 80;
// titleLayout.flexibleHeight = 0;
// timeTitle.horizontalOverflow = HorizontalWrapMode.Overflow;
// // actual active time label
// var timeLabelObj = UIFactory.CreateLabel(timeGroupObj, TextAnchor.MiddleLeft);
// var timeLabelLayout = timeLabelObj.AddComponent<LayoutElement>();
// timeLabelLayout.minWidth = 40;
// timeLabelLayout.minHeight = 25;
// timeLabelLayout.flexibleHeight = 0;
// // todo make static and update
// var s_timeText = timeLabelObj.GetComponent<Text>();
// s_timeText.text = Time.timeScale.ToString("F1");
// // time scale input
// var timeInputObj = UIFactory.CreateInputField(timeGroupObj);
// var timeInput = timeInputObj.GetComponent<InputField>();
// timeInput.characterValidation = InputField.CharacterValidation.Decimal;
// var timeInputLayout = timeInputObj.AddComponent<LayoutElement>();
// timeInputLayout.minWidth = 90;
// timeInputLayout.flexibleWidth = 0;
// timeInputLayout.minHeight = 25;
// timeInputLayout.flexibleHeight = 0;
// // time scale apply button
// var applyBtnObj = UIFactory.CreateButton(timeGroupObj);
// var applyBtn = applyBtnObj.GetComponent<Button>();
// applyBtn.onClick.AddListener(SetTimeScale);
// var applyText = applyBtnObj.GetComponentInChildren<Text>();
// applyText.text = "Apply";
// applyText.fontSize = 14;
// var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
// applyLayout.minWidth = 50;
// applyLayout.minHeight = 25;
// applyLayout.flexibleHeight = 0;
// void SetTimeScale()
// {
// var scale = float.Parse(timeInput.text);
// Time.timeScale = scale;
// s_timeText.text = Time.timeScale.ToString("F1");
// }
// inspect under mouse button
AddMouseInspectButton(topRowObj, MouseInspector.MouseInspectMode.UI);
AddMouseInspectButton(topRowObj, MouseInspector.MouseInspectMode.World);
}
private static void AddMouseInspectButton(GameObject topRowObj, MouseInspector.MouseInspectMode mode)
{
var inspectObj = UIFactory.CreateButton(topRowObj);
var inspectLayout = inspectObj.AddComponent<LayoutElement>();
inspectLayout.minWidth = 120;
inspectLayout.flexibleWidth = 0;
var inspectText = inspectObj.GetComponentInChildren<Text>();
inspectText.text = "Mouse Inspect";
inspectText.fontSize = 13;
if (mode == MouseInspector.MouseInspectMode.UI)
inspectText.text += " (UI)";
var inspectBtn = inspectObj.GetComponent<Button>();
var inspectColors = inspectBtn.colors;
inspectColors.normalColor = new Color(0.2f, 0.2f, 0.2f);
inspectBtn.colors = inspectColors;
var inspectText = inspectObj.GetComponentInChildren<Text>();
inspectText.text = "Mouse Inspect";
inspectText.fontSize = 13;
inspectBtn.onClick.AddListener(OnInspectMouseClicked);
void OnInspectMouseClicked()
{
MouseInspector.Mode = mode;
MouseInspector.StartInspect();
}
}

View File

@ -3,18 +3,27 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.Input;
using UnityExplorer.UI;
using UnityExplorer.Unstrip;
namespace UnityExplorer.Inspectors
{
public class MouseInspector
{
public enum MouseInspectMode
{
World,
UI
}
public static bool Enabled { get; set; }
//internal static Text s_objUnderMouseName;
public static MouseInspectMode Mode { get; set; }
internal static Text s_objNameLabel;
internal static Text s_objPathLabel;
internal static Text s_mousePosLabel;
@ -29,6 +38,18 @@ namespace UnityExplorer.Inspectors
Enabled = true;
MainMenu.Instance.MainPanel.SetActive(false);
s_UIContent.SetActive(true);
// recache Graphic Raycasters each time we start
var casters = ResourcesUnstrip.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
m_gCasters = new GraphicRaycaster[casters.Length];
for (int i = 0; i < casters.Length; i++)
{
#if CPP
m_gCasters[i] = casters[i].TryCast<GraphicRaycaster>();
#else
m_gCasters[i] = casters[i] as GraphicRaycaster;
#endif
}
}
public static void StopInspect()
@ -40,50 +61,60 @@ namespace UnityExplorer.Inspectors
ClearHitData();
}
internal static GraphicRaycaster[] m_gCasters;
public static void UpdateInspect()
{
if (InputManager.GetKeyDown(KeyCode.Escape))
{
StopInspect();
return;
}
var mousePos = InputManager.MousePosition;
if (mousePos != s_lastMousePos)
{
s_lastMousePos = mousePos;
var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
s_mousePosLabel.text = $"<color=grey>Mouse Position:</color> {((Vector2)InputManager.MousePosition).ToString()}";
float yFix = mousePos.y < 120 ? 80 : -80;
s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0);
}
UpdatePosition(mousePos);
if (!UnityHelpers.MainCamera)
return;
// actual inspect raycast
var ray = UnityHelpers.MainCamera.ScreenPointToRay(mousePos);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
switch (Mode)
{
case MouseInspectMode.UI:
RaycastUI(mousePos); break;
case MouseInspectMode.World:
RaycastWorld(mousePos); break;
}
}
internal static void OnHitGameObject(GameObject obj)
{
if (obj != s_lastHit)
{
s_lastHit = obj;
s_objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
}
if (InputManager.GetMouseButtonDown(0))
{
StopInspect();
InspectorManager.Instance.Inspect(obj);
}
}
internal static void RaycastWorld(Vector2 mousePos)
{
var ray = UnityHelpers.MainCamera.ScreenPointToRay(mousePos);
Physics.Raycast(ray, out RaycastHit hit, 1000f);
if (hit.transform)
{
var obj = hit.transform.gameObject;
if (obj != s_lastHit)
{
s_lastHit = obj;
s_objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
}
if (InputManager.GetMouseButtonDown(0))
{
StopInspect();
InspectorManager.Instance.Inspect(obj);
}
OnHitGameObject(obj);
}
else
{
@ -92,6 +123,56 @@ namespace UnityExplorer.Inspectors
}
}
internal static void RaycastUI(Vector2 mousePos)
{
var ped = new PointerEventData(null)
{
position = mousePos
};
#if MONO
var list = new List<RaycastResult>();
#else
var list = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
#endif
foreach (var gr in m_gCasters)
{
gr.Raycast(ped, list);
if (list.Count > 0)
{
foreach (var hit in list)
{
if (hit.gameObject)
{
var obj = hit.gameObject;
OnHitGameObject(obj);
break;
}
}
}
else
{
if (s_lastHit)
ClearHitData();
}
}
}
internal static void UpdatePosition(Vector2 mousePos)
{
s_lastMousePos = mousePos;
var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
s_mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.ToString()}";
float yFix = mousePos.y < 120 ? 80 : -80;
s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0);
}
internal static void ClearHitData()
{
s_lastHit = null;
@ -99,7 +180,7 @@ namespace UnityExplorer.Inspectors
s_objPathLabel.text = "";
}
#region UI Construction
#region UI Construction
internal static void ConstructUI()
{
@ -112,7 +193,10 @@ namespace UnityExplorer.Inspectors
baseRect.anchorMin = half;
baseRect.anchorMax = half;
baseRect.pivot = half;
baseRect.sizeDelta = new Vector2(700, 100);
baseRect.sizeDelta = new Vector2(700, 150);
var group = content.GetComponent<VerticalLayoutGroup>();
group.childForceExpandHeight = true;
// Title text
@ -131,13 +215,16 @@ namespace UnityExplorer.Inspectors
var pathLabelObj = UIFactory.CreateLabel(content, TextAnchor.MiddleLeft);
s_objPathLabel = pathLabelObj.GetComponent<Text>();
s_objPathLabel.color = Color.grey;
s_objPathLabel.fontStyle = FontStyle.Italic;
s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
var pathLayout = pathLabelObj.AddComponent<LayoutElement>();
pathLayout.minHeight = 75;
pathLayout.flexibleHeight = 0;
s_UIContent.SetActive(false);
}
#endregion
#endregion
}
}

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Reflection;
using UnityExplorer.UI;
using UnityExplorer.Helpers;
using UnityEngine;
namespace UnityExplorer.Inspectors.Reflection
{
@ -14,7 +15,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override Type FallbackType => (MemInfo as FieldInfo).FieldType;
public CacheField(FieldInfo fieldInfo, object declaringInstance) : base(fieldInfo, declaringInstance)
public CacheField(FieldInfo fieldInfo, object declaringInstance, GameObject parent) : base(fieldInfo, declaringInstance, parent)
{
CreateIValue(null, fieldInfo.FieldType);
}
@ -32,6 +33,9 @@ namespace UnityExplorer.Inspectors.Reflection
{
var fi = MemInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, IValue.Value);
if (this.ParentInspector?.ParentMember != null)
this.ParentInspector.ParentMember.SetValue();
}
}
}

View File

@ -19,6 +19,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override Type FallbackType { get; }
public ReflectionInspector ParentInspector { get; set; }
public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; }
@ -43,11 +44,12 @@ namespace UnityExplorer.Inspectors.Reflection
public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName;
public CacheMember(MemberInfo memberInfo, object declaringInstance)
public CacheMember(MemberInfo memberInfo, object declaringInstance, GameObject parentContent)
{
MemInfo = memberInfo;
DeclaringType = memberInfo.DeclaringType;
DeclaringInstance = declaringInstance;
this.m_parentContent = parentContent;
#if CPP
if (DeclaringInstance != null)
DeclaringInstance = DeclaringInstance.Il2CppCast(DeclaringType);

View File

@ -26,7 +26,7 @@ namespace UnityExplorer.Inspectors.Reflection
public string[] m_genericArgInput = new string[0];
public CacheMethod(MethodInfo methodInfo, object declaringInstance) : base(methodInfo, declaringInstance)
public CacheMethod(MethodInfo methodInfo, object declaringInstance, GameObject parent) : base(methodInfo, declaringInstance, parent)
{
GenericArgs = methodInfo.GetGenericArguments();
@ -71,6 +71,9 @@ namespace UnityExplorer.Inspectors.Reflection
}
catch (Exception e)
{
while (e.InnerException != null)
e = e.InnerException;
ExplorerCore.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}");
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Reflection;
using UnityExplorer.UI;
using UnityExplorer.Helpers;
using UnityEngine;
namespace UnityExplorer.Inspectors.Reflection
{
@ -14,7 +15,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool IsStatic => (MemInfo as PropertyInfo).GetAccessors(true)[0].IsStatic;
public CacheProperty(PropertyInfo propertyInfo, object declaringInstance) : base(propertyInfo, declaringInstance)
public CacheProperty(PropertyInfo propertyInfo, object declaringInstance, GameObject parent) : base(propertyInfo, declaringInstance, parent)
{
this.m_arguments = propertyInfo.GetIndexParameters();
this.m_argumentInput = new string[m_arguments.Length];
@ -62,6 +63,9 @@ namespace UnityExplorer.Inspectors.Reflection
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
pi.SetValue(target, IValue.Value, ParseArguments());
if (this.ParentInspector?.ParentMember != null)
this.ParentInspector.ParentMember.SetValue();
}
}
}

View File

@ -4,6 +4,8 @@ using UnityEngine;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
using UnityEngine.UI;
using UnityExplorer.Unstrip;
using System.IO;
namespace UnityExplorer.Inspectors.Reflection
{
@ -45,109 +47,267 @@ namespace UnityExplorer.Inspectors.Reflection
public void ConstructInstanceHelpers()
{
if (!typeof(Component).IsAssignableFrom(m_targetType) && !typeof(UnityEngine.Object).IsAssignableFrom(m_targetType))
return;
var rowObj = UIFactory.CreateHorizontalGroup(Content, new Color(0.1f, 0.1f, 0.1f));
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
rowGroup.childForceExpandWidth = true;
rowGroup.childControlWidth = true;
rowGroup.spacing = 5;
rowGroup.padding.top = 2;
rowGroup.padding.bottom = 2;
rowGroup.padding.right = 2;
rowGroup.padding.left = 2;
var rowLayout = rowObj.AddComponent<LayoutElement>();
rowLayout.minHeight = 25;
rowLayout.flexibleWidth = 5000;
if (typeof(Component).IsAssignableFrom(m_targetType))
{
ConstructCompHelper(rowObj);
}
ConstructUObjHelper(rowObj);
// WIP
//if (m_targetType == typeof(Texture2D))
// ConstructTextureHelper();
// todo other helpers
//if (typeof(Component).IsAssignableFrom(m_targetType))
//{
//}
//else if (typeof(UnityEngine.Object).IsAssignableFrom(m_targetType))
//{
//}
if (m_targetType == typeof(Texture2D))
ConstructTextureHelper();
}
//internal bool showingTextureHelper;
//internal bool constructedTextureViewer;
internal void ConstructCompHelper(GameObject rowObj)
{
var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
var labelLayout = labelObj.AddComponent<LayoutElement>();
labelLayout.minWidth = 90;
labelLayout.minHeight = 25;
labelLayout.flexibleWidth = 0;
var labelText = labelObj.GetComponent<Text>();
labelText.text = "GameObject:";
//internal void ConstructTextureHelper()
//{
// var rowObj = UIFactory.CreateHorizontalGroup(Content, new Color(0.1f, 0.1f, 0.1f));
// var rowLayout = rowObj.AddComponent<LayoutElement>();
// rowLayout.minHeight = 25;
// rowLayout.flexibleHeight = 0;
// var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
// rowGroup.childForceExpandHeight = true;
// rowGroup.childForceExpandWidth = false;
// rowGroup.padding.top = 3;
// rowGroup.padding.left = 3;
// rowGroup.padding.bottom = 3;
// rowGroup.padding.right = 3;
// rowGroup.spacing = 5;
#if MONO
var comp = Target as Component;
#else
var comp = (Target as Il2CppSystem.Object).TryCast<Component>();
#endif
// var showBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.2f, 0.2f));
// var showBtnLayout = showBtnObj.AddComponent<LayoutElement>();
// showBtnLayout.minWidth = 50;
// showBtnLayout.flexibleWidth = 0;
// var showText = showBtnObj.GetComponentInChildren<Text>();
// showText.text = "Show";
// var showBtn = showBtnObj.GetComponent<Button>();
var goBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.5f, 0.2f));
var goBtnLayout = goBtnObj.AddComponent<LayoutElement>();
goBtnLayout.minHeight = 25;
goBtnLayout.minWidth = 200;
goBtnLayout.flexibleWidth = 0;
var text = goBtnObj.GetComponentInChildren<Text>();
text.text = comp.name;
var btn = goBtnObj.GetComponent<Button>();
btn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(comp.gameObject); });
}
// var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
// var labelText = labelObj.GetComponent<Text>();
// labelText.text = "Texture Viewer";
internal void ConstructUObjHelper(GameObject rowObj)
{
var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
var labelLayout = labelObj.AddComponent<LayoutElement>();
labelLayout.minWidth = 60;
labelLayout.minHeight = 25;
labelLayout.flexibleWidth = 0;
var labelText = labelObj.GetComponent<Text>();
labelText.text = "Name:";
// var textureViewerObj = UIFactory.CreateScrollView(Content, out GameObject scrollContent, out _, new Color(0.1f, 0.1f, 0.1f));
// var viewerGroup = scrollContent.GetComponent<VerticalLayoutGroup>();
// viewerGroup.childForceExpandHeight = false;
// viewerGroup.childForceExpandWidth = false;
// viewerGroup.childControlHeight = true;
// viewerGroup.childControlWidth = true;
// var mainLayout = textureViewerObj.GetComponent<LayoutElement>();
// mainLayout.flexibleHeight = -1;
// mainLayout.flexibleWidth = 2000;
// mainLayout.minHeight = 25;
#if MONO
var uObj = Target as UnityEngine.Object;
#else
var uObj = (Target as Il2CppSystem.Object).TryCast<UnityEngine.Object>();
#endif
// textureViewerObj.SetActive(false);
var inputObj = UIFactory.CreateInputField(rowObj, 14, 3, 1);
var inputLayout = inputObj.AddComponent<LayoutElement>();
inputLayout.minHeight = 25;
inputLayout.flexibleWidth = 2000;
var inputField = inputObj.GetComponent<InputField>();
inputField.readOnly = true;
inputField.text = uObj.name;
// showBtn.onClick.AddListener(() =>
// {
// showingTextureHelper = !showingTextureHelper;
//var goBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.5f, 0.2f));
//var goBtnLayout = goBtnObj.AddComponent<LayoutElement>();
//goBtnLayout.minHeight = 25;
//goBtnLayout.minWidth = 200;
//goBtnLayout.flexibleWidth = 0;
//var text = goBtnObj.GetComponentInChildren<Text>();
//text.text = comp.name;
//var btn = goBtnObj.GetComponent<Button>();
//btn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(comp.gameObject); });
}
// if (showingTextureHelper)
// {
// if (!constructedTextureViewer)
// ConstructTextureViewerArea(scrollContent);
internal bool showingTextureHelper;
internal bool constructedTextureViewer;
// showText.text = "Hide";
// textureViewerObj.SetActive(true);
// }
// else
// {
// showText.text = "Show";
// textureViewerObj.SetActive(false);
// }
// });
//}
internal GameObject m_textureViewerObj;
//internal void ConstructTextureViewerArea(GameObject parent)
//{
// constructedTextureViewer = true;
internal void ConstructTextureHelper()
{
var rowObj = UIFactory.CreateHorizontalGroup(Content, new Color(0.1f, 0.1f, 0.1f));
var rowLayout = rowObj.AddComponent<LayoutElement>();
rowLayout.minHeight = 25;
rowLayout.flexibleHeight = 0;
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
rowGroup.childForceExpandHeight = true;
rowGroup.childForceExpandWidth = false;
rowGroup.padding.top = 3;
rowGroup.padding.left = 3;
rowGroup.padding.bottom = 3;
rowGroup.padding.right = 3;
rowGroup.spacing = 5;
// var tex = Target as Texture2D;
var showBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.6f, 0.2f));
var showBtnLayout = showBtnObj.AddComponent<LayoutElement>();
showBtnLayout.minWidth = 50;
showBtnLayout.flexibleWidth = 0;
var showText = showBtnObj.GetComponentInChildren<Text>();
showText.text = "Show";
var showBtn = showBtnObj.GetComponent<Button>();
// if (!tex)
// {
// ExplorerCore.LogWarning("Could not cast the target instance to Texture2D!");
// return;
// }
var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
var labelText = labelObj.GetComponent<Text>();
labelText.text = "Texture Viewer";
// var imageObj = UIFactory.CreateUIObject("TextureViewerImage", parent, new Vector2(1, 1));
// var image = imageObj.AddComponent<Image>();
// var sprite = UIManager.CreateSprite(tex);
// image.sprite = sprite;
var textureViewerObj = UIFactory.CreateScrollView(Content, out GameObject scrollContent, out _, new Color(0.1f, 0.1f, 0.1f));
var viewerGroup = scrollContent.GetComponent<VerticalLayoutGroup>();
viewerGroup.childForceExpandHeight = false;
viewerGroup.childForceExpandWidth = false;
viewerGroup.childControlHeight = true;
viewerGroup.childControlWidth = true;
var mainLayout = textureViewerObj.GetComponent<LayoutElement>();
mainLayout.flexibleHeight = 9999;
mainLayout.flexibleWidth = 9999;
mainLayout.minHeight = 100;
// var fitter = imageObj.AddComponent<ContentSizeFitter>();
// fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// //fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
textureViewerObj.SetActive(false);
// var imageLayout = imageObj.AddComponent<LayoutElement>();
// imageLayout.preferredHeight = sprite.rect.height;
// imageLayout.preferredWidth = sprite.rect.width;
//}
m_textureViewerObj = textureViewerObj;
showBtn.onClick.AddListener(() =>
{
showingTextureHelper = !showingTextureHelper;
if (showingTextureHelper)
{
if (!constructedTextureViewer)
ConstructTextureViewerArea(scrollContent);
showText.text = "Hide";
ToggleTextureViewer(true);
}
else
{
showText.text = "Show";
ToggleTextureViewer(false);
}
});
}
internal void ConstructTextureViewerArea(GameObject parent)
{
constructedTextureViewer = true;
var tex = Target as Texture2D;
#if CPP
if (!tex)
tex = (Target as Il2CppSystem.Object).TryCast<Texture2D>();
#endif
if (!tex)
{
ExplorerCore.LogWarning("Could not cast the target instance to Texture2D! Maybe its null or destroyed?");
return;
}
// Save helper
var saveRowObj = UIFactory.CreateHorizontalGroup(parent, new Color(0.1f, 0.1f, 0.1f));
var saveRow = saveRowObj.GetComponent<HorizontalLayoutGroup>();
saveRow.childForceExpandHeight = true;
saveRow.childForceExpandWidth = true;
saveRow.padding = new RectOffset() { left = 2, bottom = 2, right = 2, top = 2 };
saveRow.spacing = 2;
var btnObj = UIFactory.CreateButton(saveRowObj, new Color(0.2f, 0.2f, 0.2f));
var btnLayout = btnObj.AddComponent<LayoutElement>();
btnLayout.minHeight = 25;
btnLayout.minWidth = 100;
btnLayout.flexibleWidth = 0;
var saveBtn = btnObj.GetComponent<Button>();
var saveBtnText = btnObj.GetComponentInChildren<Text>();
saveBtnText.text = "Save .PNG";
var inputObj = UIFactory.CreateInputField(saveRowObj);
var inputLayout = inputObj.AddComponent<LayoutElement>();
inputLayout.minHeight = 25;
inputLayout.minWidth = 100;
inputLayout.flexibleWidth = 9999;
var inputField = inputObj.GetComponent<InputField>();
var name = tex.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
var savePath = $@"{Config.ModConfig.Instance.Default_Output_Path}\{name}.png";
inputField.text = savePath;
saveBtn.onClick.AddListener(() =>
{
if (tex && !string.IsNullOrEmpty(inputField.text))
{
var path = inputField.text;
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
{
ExplorerCore.LogWarning("Desired save path must end with '.png'!");
return;
}
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
if (File.Exists(path))
File.Delete(path);
if (!tex.IsReadable())
tex = Texture2DHelpers.ForceReadTexture(tex);
#if CPP
byte[] data = tex.EncodeToPNG();
#else
byte[] data = tex.EncodeToPNGSafe();
#endif
File.WriteAllBytes(path, data);
}
});
// Actual texture viewer
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", parent);
var image = imageObj.AddComponent<Image>();
var sprite = ImageConversionUnstrip.CreateSprite(tex);
image.sprite = sprite;
var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
//fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
var imageLayout = imageObj.AddComponent<LayoutElement>();
imageLayout.preferredHeight = sprite.rect.height;
imageLayout.preferredWidth = sprite.rect.width;
}
internal void ToggleTextureViewer(bool enabled)
{
m_textureViewerObj.SetActive(enabled);
m_filterAreaObj.SetActive(!enabled);
m_memberListObj.SetActive(!enabled);
m_updateRowObj.SetActive(!enabled);
}
public void ConstructInstanceFilters(GameObject parent)
{

View File

@ -10,6 +10,9 @@ using UnityExplorer.Helpers;
using UnityExplorer.UI;
using UnityExplorer.UI.Shared;
using System.Reflection;
#if CPP
using CppDictionary = Il2CppSystem.Collections.IDictionary;
#endif
namespace UnityExplorer.Inspectors.Reflection
{
@ -44,7 +47,11 @@ namespace UnityExplorer.Inspectors.Reflection
}
internal IDictionary RefIDictionary;
#if CPP
internal CppDictionary RefCppDictionary;
#else
internal IDictionary RefCppDictionary = null;
#endif
internal Type m_typeOfKeys;
internal Type m_typeofValues;
@ -65,6 +72,11 @@ namespace UnityExplorer.Inspectors.Reflection
{
RefIDictionary = Value as IDictionary;
#if CPP
try { RefCppDictionary = (Value as Il2CppSystem.Object).TryCast<CppDictionary>(); }
catch { }
#endif
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
@ -129,13 +141,6 @@ namespace UnityExplorer.Inspectors.Reflection
{
var value = RefIDictionary[key];
//if (index >= m_rowHolders.Count)
//{
// AddRowHolder();
//}
//var holder = m_rowHolders[index];
var cacheKey = new CachePaired(index, this, this.RefIDictionary, PairTypes.Key, m_listContent);
cacheKey.CreateIValue(key, this.m_typeOfKeys);
cacheKey.Disable();
@ -206,9 +211,10 @@ namespace UnityExplorer.Inspectors.Reflection
RefreshDisplay();
}
#region CPP fixes
#region CPP fixes
#if CPP
// temp fix for Il2Cpp IDictionary until interfaces are fixed
private IDictionary EnumerateWithReflection()
{
var valueType = Value?.GetType() ?? FallbackType;
@ -222,8 +228,8 @@ namespace UnityExplorer.Inspectors.Reflection
var valueList = new List<object>();
// store entries with reflection
EnumerateWithReflection(keys, keyList);
EnumerateWithReflection(values, valueList);
EnumerateCollection(keys, keyList);
EnumerateCollection(values, valueList);
// make actual mono dictionary
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
@ -236,7 +242,7 @@ namespace UnityExplorer.Inspectors.Reflection
return dict;
}
private void EnumerateWithReflection(object collection, List<object> list)
private void EnumerateCollection(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
@ -253,9 +259,9 @@ namespace UnityExplorer.Inspectors.Reflection
}
#endif
#endregion
#endregion
#region UI CONSTRUCTION
#region UI CONSTRUCTION
internal GameObject m_listContent;
internal LayoutElement m_listLayout;
@ -309,6 +315,6 @@ namespace UnityExplorer.Inspectors.Reflection
// m_rowHolders.Add(obj);
//}
#endregion
#endregion
}
}

View File

@ -37,14 +37,7 @@ namespace UnityExplorer.Inspectors.Reflection
if (m_subContentConstructed)
{
// changing types, destroy subcontent
for (int i = 0; i < m_subContentParent.transform.childCount; i++)
{
var child = m_subContentParent.transform.GetChild(i);
GameObject.Destroy(child.gameObject);
}
m_subContentConstructed = false;
DestroySubContent();
}
if (!s_enumNamesCache.ContainsKey(type))
@ -96,7 +89,10 @@ namespace UnityExplorer.Inspectors.Reflection
private void SetValueFromDropdown()
{
var type = Value?.GetType() ?? FallbackType;
var value = Enum.Parse(type, m_dropdownText.text);
var index = m_dropdown.value;
var value = Enum.Parse(type, s_enumNamesCache[type][index].Value);
if (value != null)
{
Value = value;

View File

@ -37,6 +37,11 @@ namespace UnityExplorer.Inspectors.Reflection
internal IEnumerable RefIEnumerable;
internal IList RefIList;
#if CPP
internal Il2CppSystem.Collections.ICollection CppICollection;
#else
internal ICollection CppICollection = null;
#endif
internal readonly Type m_baseEntryType;
@ -49,6 +54,14 @@ namespace UnityExplorer.Inspectors.Reflection
RefIEnumerable = Value as IEnumerable;
RefIList = Value as IList;
#if CPP
if (Value != null && RefIList == null)
{
try { CppICollection = (Value as Il2CppSystem.Object).TryCast<Il2CppSystem.Collections.ICollection>(); }
catch { }
}
#endif
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
@ -77,8 +90,8 @@ namespace UnityExplorer.Inspectors.Reflection
if (Value != null)
{
string count = "?";
if (m_recacheWanted && RefIList != null)
count = RefIList.Count.ToString();
if (m_recacheWanted && (RefIList != null || CppICollection != null))
count = RefIList?.Count.ToString() ?? CppICollection.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
@ -169,89 +182,62 @@ namespace UnityExplorer.Inspectors.Reflection
RefreshDisplay();
}
#region CPP Helpers
#region CPP Helpers
#if CPP
// some temp fixes for Il2Cpp IEnumerables until interfaces are fixed
internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
internal class EnumeratorInfo
{
internal MethodInfo moveNext;
internal PropertyInfo current;
}
private IEnumerable EnumerateWithReflection()
{
if (Value.IsNullOrDestroyed())
if (Value == null)
return null;
var genericDef = Value.GetType().GetGenericTypeDefinition();
if (genericDef == typeof(Il2CppSystem.Collections.Generic.List<>))
return CppListToMono(genericDef);
else if (genericDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
return CppHashSetToMono();
else
return CppIListToMono();
}
// List<T>.ToArray()
private IEnumerable CppListToMono(Type genericTypeDef)
{
if (genericTypeDef == null) return null;
return genericTypeDef
.MakeGenericType(new Type[] { this.m_baseEntryType })
.GetMethod("ToArray")
.Invoke(Value, new object[0]) as IEnumerable;
}
// HashSet.GetEnumerator
private IEnumerable CppHashSetToMono()
{
var set = new HashSet<object>();
// invoke GetEnumerator
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
set.Add(current.GetValue(enumerator));
return set;
}
// IList.Item
private IList CppIListToMono()
{
try
// new test
var CppEnumerable = (Value as Il2CppSystem.Object)?.TryCast<Il2CppSystem.Collections.IEnumerable>();
if (CppEnumerable != null)
{
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.m_baseEntryType });
var list = (IList)Activator.CreateInstance(genericType);
var type = Value.GetType();
if (!s_getEnumeratorMethods.ContainsKey(type))
s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
var enumerator = s_getEnumeratorMethods[type].Invoke(Value, null);
var enumeratorType = enumerator.GetType();
for (int i = 0; ; i++)
if (!s_enumeratorInfos.ContainsKey(enumeratorType))
{
try
s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
{
var itm = Value?.GetType()
.GetProperty("Item")
.GetValue(Value, new object[] { i });
list.Add(itm);
}
catch { break; }
current = enumeratorType.GetProperty("Current"),
moveNext = enumeratorType.GetMethod("MoveNext"),
});
}
var info = s_enumeratorInfos[enumeratorType];
// iterate
var list = new List<object>();
while ((bool)info.moveNext.Invoke(enumerator, null))
list.Add(info.current.GetValue(enumerator));
return list;
}
catch (Exception e)
{
ExplorerCore.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
return null;
}
return null;
}
#endif
#endregion
#endregion
#region UI CONSTRUCTION
#region UI CONSTRUCTION
internal GameObject m_listContent;
internal LayoutElement m_listLayout;
@ -296,6 +282,6 @@ namespace UnityExplorer.Inspectors.Reflection
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
}
#endregion
#endregion
}
}

View File

@ -14,8 +14,9 @@ namespace UnityExplorer.Inspectors.Reflection
{
public InteractiveString(object value, Type valueType) : base(value, valueType) { }
public override bool HasSubContent => false;
public override bool SubContentWanted => false;
public override bool HasSubContent => true;
public override bool SubContentWanted => true;
public override bool WantInspectBtn => false;
public override void OnValueUpdated()
@ -27,10 +28,9 @@ namespace UnityExplorer.Inspectors.Reflection
{
base.OnException(member);
if (m_hiddenObj.gameObject.activeSelf)
if (m_subContentConstructed && m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(false);
// m_baseLabel.text = DefaultLabel;
m_labelLayout.minWidth = 200;
m_labelLayout.flexibleWidth = 5000;
}
@ -45,39 +45,62 @@ namespace UnityExplorer.Inspectors.Reflection
return;
}
if (!m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(true);
m_baseLabel.text = m_richValueType;
if (Value != null)
if (m_subContentConstructed)
{
var toString = Value.ToString();
if (!m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(true);
}
if (!string.IsNullOrEmpty((string)Value))
{
var toString = (string)Value;
if (toString.Length > 15000)
toString = toString.Substring(0, 15000);
m_valueInput.text = toString;
m_placeholderText.text = toString;
m_readonlyInput.text = toString;
if (m_subContentConstructed)
{
m_valueInput.text = toString;
m_placeholderText.text = toString;
}
}
else
{
m_valueInput.text = "";
m_placeholderText.text = "null";
string s = Value == null
? "null"
: "empty";
m_readonlyInput.text = $"<i><color=grey>{s}</color></i>";
if (m_subContentConstructed)
{
m_valueInput.text = "";
m_placeholderText.text = s;
}
}
m_labelLayout.minWidth = 50;
m_labelLayout.flexibleWidth = 0;
}
internal void OnApplyClicked()
{
Value = m_valueInput.text;
Owner.SetValue();
RefreshUIForValue();
}
internal InputField m_valueInput;
// for the default label
internal LayoutElement m_labelLayout;
//internal InputField m_readonlyInput;
internal Text m_readonlyInput;
// for input
internal InputField m_valueInput;
internal GameObject m_hiddenObj;
internal Text m_placeholderText;
@ -90,12 +113,38 @@ namespace UnityExplorer.Inspectors.Reflection
m_labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
m_hiddenObj = UIFactory.CreateLabel(m_valueContent, TextAnchor.MiddleLeft);
var readonlyInputObj = UIFactory.CreateLabel(m_valueContent, TextAnchor.MiddleLeft);
m_readonlyInput = readonlyInputObj.GetComponent<Text>();
m_readonlyInput.horizontalOverflow = HorizontalWrapMode.Overflow;
var testFitter = readonlyInputObj.AddComponent<ContentSizeFitter>();
testFitter.verticalFit = ContentSizeFitter.FitMode.MinSize;
var labelLayout = readonlyInputObj.AddComponent<LayoutElement>();
labelLayout.minHeight = 25;
labelLayout.preferredHeight = 25;
labelLayout.flexibleHeight = 0;
}
public override void ConstructSubcontent()
{
base.ConstructSubcontent();
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(1, 1, 1, 0));
var group = groupObj.GetComponent<VerticalLayoutGroup>();
group.spacing = 4;
group.padding.top = 3;
group.padding.left = 3;
group.padding.right = 3;
group.padding.bottom = 3;
m_hiddenObj = UIFactory.CreateLabel(groupObj, TextAnchor.MiddleLeft);
m_hiddenObj.SetActive(false);
var hiddenText = m_hiddenObj.GetComponent<Text>();
hiddenText.color = Color.clear;
hiddenText.fontSize = 14;
hiddenText.raycastTarget = false;
hiddenText.supportRichText = false;
var hiddenFitter = m_hiddenObj.AddComponent<ContentSizeFitter>();
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var hiddenLayout = m_hiddenObj.AddComponent<LayoutElement>();
@ -121,19 +170,23 @@ namespace UnityExplorer.Inspectors.Reflection
m_placeholderText = m_valueInput.placeholder.GetComponent<Text>();
m_valueInput.onValueChanged.AddListener((string val) =>
m_placeholderText.supportRichText = false;
m_valueInput.textComponent.supportRichText = false;
m_valueInput.onValueChanged.AddListener((string val) =>
{
hiddenText.text = val;
hiddenText.text = val ?? "";
LayoutRebuilder.ForceRebuildLayoutImmediate(Owner.m_mainRect);
});
if (Owner.CanWrite)
{
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyBtnObj = UIFactory.CreateButton(groupObj, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
var applyBtn = applyBtnObj.GetComponent<Button>();
applyBtn.onClick.AddListener(OnApplyClicked);
@ -144,6 +197,8 @@ namespace UnityExplorer.Inspectors.Reflection
{
m_valueInput.readOnly = true;
}
RefreshUIForValue();
}
}
}

View File

@ -226,14 +226,15 @@ namespace UnityExplorer.Inspectors.Reflection
if (StructInfo != null)
{
// changing types, destroy subcontent
for (int i = 0; i < m_subContentParent.transform.childCount; i++)
{
var child = m_subContentParent.transform.GetChild(i);
GameObject.Destroy(child.gameObject);
}
DestroySubContent();
//// changing types, destroy subcontent
//for (int i = 0; i < m_subContentParent.transform.childCount; i++)
//{
// var child = m_subContentParent.transform.GetChild(i);
// GameObject.Destroy(child.gameObject);
//}
m_UIConstructed = false;
//m_UIConstructed = false;
}
m_lastStructType = type;

View File

@ -78,7 +78,13 @@ namespace UnityExplorer.Inspectors.Reflection
m_valueContent.SetActive(false);
GameObject.Destroy(this.m_valueContent.gameObject);
}
if (this.m_subContentParent && SubContentWanted)
DestroySubContent();
}
public virtual void DestroySubContent()
{
if (this.m_subContentParent && HasSubContent)
{
for (int i = 0; i < this.m_subContentParent.transform.childCount; i++)
{
@ -87,6 +93,8 @@ namespace UnityExplorer.Inspectors.Reflection
GameObject.Destroy(child.gameObject);
}
}
m_subContentConstructed = false;
}
public virtual void OnValueUpdated()
@ -95,18 +103,16 @@ namespace UnityExplorer.Inspectors.Reflection
ConstructUI(m_mainContentParent, m_subContentParent);
if (Owner is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException))
{
OnException(ownerMember);
}
else
{
RefreshUIForValue();
}
}
public virtual void OnException(CacheMember member)
{
m_baseLabel.text = "<color=red>" + member.ReflectionException + "</color>";
if (m_UIConstructed)
m_baseLabel.text = "<color=red>" + member.ReflectionException + "</color>";
Value = null;
}
@ -170,6 +176,10 @@ namespace UnityExplorer.Inspectors.Reflection
ConstructSubcontent();
}
internal MethodInfo m_toStringMethod;
internal MethodInfo m_toStringFormatMethod;
internal bool m_gotToStringMethods;
public string GetDefaultLabel(bool updateType = true)
{
var valueType = Value?.GetType() ?? this.FallbackType;
@ -199,8 +209,29 @@ namespace UnityExplorer.Inspectors.Reflection
}
else
{
var toString = (string)valueType.GetMethod("ToString", new Type[0])?.Invoke(Value, null)
?? Value.ToString();
if (!m_gotToStringMethods)
{
m_gotToStringMethods = true;
m_toStringMethod = valueType.GetMethod("ToString", new Type[0]);
m_toStringFormatMethod = valueType.GetMethod("ToString", new Type[] { typeof(string) });
// test format method actually works
try
{
m_toStringFormatMethod.Invoke(Value, new object[] { "F3" });
}
catch
{
m_toStringFormatMethod = null;
}
}
string toString;
if (m_toStringFormatMethod != null)
toString = (string)m_toStringFormatMethod.Invoke(Value, new object[] { "F3" });
else
toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
var fullnametemp = valueType.ToString();
if (fullnametemp.StartsWith("Il2CppSystem"))
@ -297,7 +328,7 @@ namespace UnityExplorer.Inspectors.Reflection
void OnInspectClicked()
{
if (!Value.IsNullOrDestroyed(false))
InspectorManager.Instance.Inspect(this.Value);
InspectorManager.Instance.Inspect(this.Value, this.Owner);
}
m_inspectButton.SetActive(false);

View File

@ -34,7 +34,7 @@ namespace UnityExplorer.Inspectors
}
// Blacklists
private static readonly HashSet<string> s_typeAndMemberBlacklist = new HashSet<string>
private static readonly HashSet<string> bl_typeAndMember = new HashSet<string>
{
#if CPP
// these cause a crash in IL2CPP
@ -45,19 +45,21 @@ namespace UnityExplorer.Inspectors
"Texture2D.SetPixelDataImpl",
#endif
};
private static readonly HashSet<string> s_methodStartsWithBlacklist = new HashSet<string>
private static readonly HashSet<string> bl_memberNameStartsWith = new HashSet<string>
{
// these are redundant
"get_",
"set_",
};
#endregion
#endregion
#region INSTANCE
public override string TabLabel => m_targetTypeShortName;
internal CacheObjectBase ParentMember { get; set; }
internal readonly Type m_targetType;
internal readonly string m_targetTypeShortName;
@ -132,24 +134,101 @@ namespace UnityExplorer.Inspectors
RefreshDisplay();
}
private void OnMemberFilterClicked(MemberTypes type, Button button)
internal bool IsBlacklisted(string sig) => bl_typeAndMember.Any(it => sig.Contains(it));
internal bool IsBlacklisted(MethodInfo method) => bl_memberNameStartsWith.Any(it => method.Name.StartsWith(it));
internal string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
internal string AppendArgsToSig(ParameterInfo[] args)
{
if (m_lastActiveMemButton)
string ret = " (";
foreach (var param in args)
ret += $"{param.ParameterType.Name} {param.Name}, ";
ret += ")";
return ret;
}
public void CacheMembers(Type type)
{
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
var types = ReflectionHelpers.GetAllBaseTypes(type);
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (this is InstanceInspector)
flags |= BindingFlags.Instance;
foreach (var declaringType in types)
{
var lastColors = m_lastActiveMemButton.colors;
lastColors.normalColor = new Color(0.2f, 0.2f, 0.2f);
m_lastActiveMemButton.colors = lastColors;
var target = Target;
#if CPP
target = target.Il2CppCast(declaringType);
#endif
IEnumerable<MemberInfo> infos = declaringType.GetMethods(flags);
infos = infos.Concat(declaringType.GetProperties(flags));
infos = infos.Concat(declaringType.GetFields(flags));
foreach (var member in infos)
{
try
{
//ExplorerCore.Log($"Trying to cache member {sig}...");
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
var sig = GetSig(member);
var mi = member as MethodInfo;
var pi = member as PropertyInfo;
var fi = member as FieldInfo;
if (IsBlacklisted(sig) || (mi != null && IsBlacklisted(mi)))
continue;
var args = mi?.GetParameters() ?? pi?.GetIndexParameters();
if (args != null)
{
if (!CacheMember.CanProcessArgs(args))
continue;
sig += AppendArgsToSig(args);
}
if (cachedSigs.Contains(sig))
continue;
cachedSigs.Add(sig);
if (mi != null)
list.Add(new CacheMethod(mi, target, m_scrollContent));
else if (pi != null)
list.Add(new CacheProperty(pi, target, m_scrollContent));
else
list.Add(new CacheField(fi, target, m_scrollContent));
list.Last().ParentInspector = this;
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
}
m_memberFilter = type;
m_lastActiveMemButton = button;
var typeList = types.ToList();
var colors = m_lastActiveMemButton.colors;
colors.normalColor = new Color(0.2f, 0.6f, 0.2f);
m_lastActiveMemButton.colors = colors;
var sorted = new List<CacheMember>();
sorted.AddRange(list.Where(it => it is CacheMethod)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheProperty)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheField)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
FilterMembers(null, true);
m_sliderScroller.m_slider.value = 1f;
m_allMembers = sorted.ToArray();
}
public override void Update()
@ -178,6 +257,26 @@ namespace UnityExplorer.Inspectors
}
}
private void OnMemberFilterClicked(MemberTypes type, Button button)
{
if (m_lastActiveMemButton)
{
var lastColors = m_lastActiveMemButton.colors;
lastColors.normalColor = new Color(0.2f, 0.2f, 0.2f);
m_lastActiveMemButton.colors = lastColors;
}
m_memberFilter = type;
m_lastActiveMemButton = button;
var colors = m_lastActiveMemButton.colors;
colors.normalColor = new Color(0.2f, 0.6f, 0.2f);
m_lastActiveMemButton.colors = colors;
FilterMembers(null, true);
m_sliderScroller.m_slider.value = 1f;
}
public void FilterMembers(string nameFilter = null, bool force = false)
{
int lastCount = m_membersFiltered.Count;
@ -200,7 +299,7 @@ namespace UnityExplorer.Inspectors
}
// name filter
if (!string.IsNullOrEmpty(nameFilter) && !mem.NameForFiltering.Contains(nameFilter))
if (!string.IsNullOrEmpty(nameFilter) && !mem.NameForFiltering.Contains(nameFilter))
continue;
m_membersFiltered.Add(mem);
@ -266,139 +365,12 @@ namespace UnityExplorer.Inspectors
}
}
public void CacheMembers(Type type)
{
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
var types = ReflectionHelpers.GetAllBaseTypes(type);
foreach (var declaringType in types)
{
MemberInfo[] infos;
try
{
infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags);
}
catch
{
ExplorerCore.Log($"Exception getting members for type: {declaringType.FullName}");
continue;
}
var target = Target;
#if CPP
try
{
target = target.Il2CppCast(declaringType);
}
catch //(Exception e)
{
//ExplorerCore.LogWarning("Excepting casting " + target.GetType().FullName + " to " + declaringType.FullName);
}
#endif
foreach (var member in infos)
{
try
{
// make sure member type is Field, Method or Property (4 / 8 / 16)
int m = (int)member.MemberType;
if (m < 4 || m > 16)
continue;
//ExplorerCore.Log($"Trying to cache member {sig}...");
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
var pi = member as PropertyInfo;
var mi = member as MethodInfo;
if (this is StaticInspector)
{
if (member is FieldInfo fi && !fi.IsStatic) continue;
else if (pi != null && !pi.GetAccessors(true)[0].IsStatic) continue;
else if (mi != null && !mi.IsStatic) continue;
}
// check blacklisted members
var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (s_typeAndMemberBlacklist.Any(it => sig.Contains(it)))
continue;
if (s_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue;
if (mi != null)
AppendParams(mi.GetParameters());
else if (pi != null)
AppendParams(pi.GetIndexParameters());
void AppendParams(ParameterInfo[] _args)
{
sig += " (";
foreach (var param in _args)
sig += $"{param.ParameterType.Name} {param.Name}, ";
sig += ")";
}
if (cachedSigs.Contains(sig))
continue;
try
{
CacheMember cached;
if (mi != null && CacheMember.CanProcessArgs(mi.GetParameters()))
cached = new CacheMethod(mi, target);
else if (pi != null && CacheMember.CanProcessArgs(pi.GetIndexParameters()))
cached = new CacheProperty(pi, target);
else if (member is FieldInfo fi)
cached = new CacheField(fi, target);
else
continue;
cached.m_parentContent = m_scrollContent;
if (cached != null)
{
cachedSigs.Add(sig);
list.Add(cached);
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {sig}!");
ExplorerCore.Log(e.ToString());
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
}
var typeList = types.ToList();
var sorted = new List<CacheMember>();
sorted.AddRange(list.Where(it => it is CacheMethod)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheProperty)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheField)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
m_allMembers = sorted.ToArray();
// ExplorerCore.Log("Cached " + m_allMembers.Length + " members");
}
#region UI CONSTRUCTION
internal GameObject m_filterAreaObj;
internal GameObject m_updateRowObj;
internal GameObject m_memberListObj;
internal void ConstructUI()
{
var parent = InspectorManager.Instance.m_inspectorContent;
@ -459,7 +431,7 @@ namespace UnityExplorer.Inspectors
ConstructFilterArea();
ConstructOptionsArea();
ConstructUpdateRow();
}
internal void ConstructFilterArea()
@ -480,6 +452,8 @@ namespace UnityExplorer.Inspectors
filterGroup.padding.top = 4;
filterGroup.padding.bottom = 4;
m_filterAreaObj = filterAreaObj;
// name filter
var nameFilterRowObj = UIFactory.CreateHorizontalGroup(filterAreaObj, new Color(1, 1, 1, 0));
@ -576,9 +550,9 @@ namespace UnityExplorer.Inspectors
btn.colors = colors;
}
internal void ConstructOptionsArea()
internal void ConstructUpdateRow()
{
var optionsRowObj = UIFactory.CreateHorizontalGroup(Content, new Color(1,1,1,0));
var optionsRowObj = UIFactory.CreateHorizontalGroup(Content, new Color(1, 1, 1, 0));
var optionsLayout = optionsRowObj.AddComponent<LayoutElement>();
optionsLayout.minHeight = 25;
var optionsGroup = optionsRowObj.GetComponent<HorizontalLayoutGroup>();
@ -587,6 +561,8 @@ namespace UnityExplorer.Inspectors
optionsGroup.childAlignment = TextAnchor.MiddleLeft;
optionsGroup.spacing = 10;
m_updateRowObj = optionsRowObj;
// update button
var updateButtonObj = UIFactory.CreateButton(optionsRowObj, new Color(0.2f, 0.2f, 0.2f));
@ -596,7 +572,7 @@ namespace UnityExplorer.Inspectors
var updateText = updateButtonObj.GetComponentInChildren<Text>();
updateText.text = "Update Values";
var updateBtn = updateButtonObj.GetComponent<Button>();
updateBtn.onClick.AddListener(() =>
updateBtn.onClick.AddListener(() =>
{
bool orig = m_autoUpdate;
m_autoUpdate = true;
@ -614,11 +590,12 @@ namespace UnityExplorer.Inspectors
autoUpdateToggle.isOn = false;
autoUpdateToggle.onValueChanged.AddListener((bool val) => { m_autoUpdate = val; });
}
internal void ConstructMemberList()
{
var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.08f, 0.08f, 0.08f));
var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.05f, 0.05f, 0.05f));
m_memberListObj = scrollobj;
m_scrollContentRect = m_scrollContent.GetComponent<RectTransform>();
var scrollGroup = m_scrollContent.GetComponent<VerticalLayoutGroup>();

View File

@ -55,16 +55,15 @@ namespace UnityExplorer.Inspectors
{
get
{
if (!m_dontDestroyObject)
if (!s_dontDestroyObject)
{
m_dontDestroyObject = new GameObject("DontDestroyMe");
GameObject.DontDestroyOnLoad(m_dontDestroyObject);
s_dontDestroyObject = new GameObject("DontDestroyMe");
GameObject.DontDestroyOnLoad(s_dontDestroyObject);
}
return m_dontDestroyObject;
return s_dontDestroyObject;
}
}
internal static GameObject m_dontDestroyObject;
internal static GameObject s_dontDestroyObject;
public void Init()
{
@ -97,22 +96,6 @@ namespace UnityExplorer.Inspectors
}
}
//#if CPP
// public int GetSceneHandle(string sceneName)
// {
// if (sceneName == "DontDestroyOnLoad")
// return DontDestroyScene;
// for (int i = 0; i < SceneManager.sceneCount; i++)
// {
// var scene = SceneManager.GetSceneAt(i);
// if (scene.name == sceneName)
// return scene.handle;
// }
// return -1;
// }
//#endif
internal void OnSceneChange()
{
m_sceneDropdown.OnCancel(null);
@ -121,8 +104,13 @@ namespace UnityExplorer.Inspectors
private void RefreshSceneSelector()
{
var names = new List<string>();
var scenes = new List<Scene>();
var newNames = new List<string>();
var newScenes = new List<Scene>();
if (m_currentScenes == null)
m_currentScenes = new Scene[0];
bool anyChange = SceneManager.sceneCount != m_currentScenes.Length - 1;
for (int i = 0; i < SceneManager.sceneCount; i++)
{
@ -131,33 +119,32 @@ namespace UnityExplorer.Inspectors
if (scene == default)
continue;
scenes.Add(scene);
names.Add(scene.name);
if (!anyChange && !m_currentScenes.Any(it => it.GetHandle() == scene.GetHandle()))
anyChange = true;
newScenes.Add(scene);
newNames.Add(scene.name);
}
names.Add("DontDestroyOnLoad");
scenes.Add(DontDestroyScene);
newNames.Add("DontDestroyOnLoad");
newScenes.Add(DontDestroyScene);
m_sceneDropdown.options.Clear();
foreach (string scene in names)
foreach (string scene in newNames)
{
m_sceneDropdown.options.Add(new Dropdown.OptionData { text = scene });
}
if (!names.Contains(m_sceneDropdownText.text))
if (anyChange)
{
m_sceneDropdownText.text = names[0];
SetTargetScene(scenes[0]);
m_sceneDropdownText.text = newNames[0];
SetTargetScene(newScenes[0]);
}
m_currentScenes = scenes.ToArray();
m_currentScenes = newScenes.ToArray();
}
//#if CPP
// public void SetTargetScene(string name) => SetTargetScene(scene.handle);
//#endif
public void SetTargetScene(Scene scene)
{
if (scene == default)
@ -167,7 +154,7 @@ namespace UnityExplorer.Inspectors
#if CPP
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene.handle);
#else
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene);
GameObject[] rootObjs = scene.GetRootGameObjects();
#endif
SetSceneObjectList(rootObjs);

Binary file not shown.

View File

@ -3,7 +3,12 @@ using System.Collections.Generic;
using UnityExplorer.UI;
using UnityEngine;
using System;
using System.Runtime.InteropServices;
using System.Text;
using UnityExplorer.Unstrip;
#if CPP
using UnhollowerBaseLib;
using UnityExplorer.Helpers;
#endif
namespace UnityExplorer.Tests
@ -19,11 +24,16 @@ namespace UnityExplorer.Tests
"three",
};
public static void StaticMethod() { }
}
public class TestClass
{
public string AAALongString = @"1
2
3
4
5";
public Vector2 AATestVector2 = new Vector2(1, 2);
public Vector3 AATestVector3 = new Vector3(1, 2, 3);
public Vector4 AATestVector4 = new Vector4(1, 2, 3, 4);
@ -112,13 +122,15 @@ namespace UnityExplorer.Tests
private static bool m_setOnlyProperty;
public static bool ReadSetOnlyProperty => m_setOnlyProperty;
public Texture TestTexture;
public Texture2D TestTexture;
public static Sprite TestSprite;
#if CPP
public static Il2CppSystem.Collections.Generic.HashSet<string> CppHashSetTest;
public static Il2CppSystem.Collections.Generic.List<string> CppStringTest;
public static Il2CppSystem.Collections.IList CppIList;
//public static Il2CppSystem.Collections.Generic.Dictionary<string, string> CppDictTest;
//public static Il2CppSystem.Collections.Generic.Dictionary<int, float> CppDictTest2;
#endif
public TestClass()
@ -134,20 +146,7 @@ namespace UnityExplorer.Tests
}
#if CPP
TestTexture = UIManager.MakeSolidTexture(Color.white, 1000, 600);
TestTexture.name = "TestTexture";
var r = new Rect(0, 0, TestTexture.width, TestTexture.height);
var v2 = Vector2.zero;
var v4 = Vector4.zero;
TestSprite = Sprite.CreateSprite_Injected((Texture2D)TestTexture, ref r, ref v2, 100f, 0u, SpriteMeshType.Tight, ref v4, false);
GameObject.DontDestroyOnLoad(TestTexture);
GameObject.DontDestroyOnLoad(TestSprite);
//// test loading a tex from file
//var dataToLoad = System.IO.File.ReadAllBytes(@"Mods\UnityExplorer\Tex_Nemundis_Nebula.png");
//ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}");
TextureSpriteTest();
CppHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
CppHashSetTest.Add("1");
@ -157,9 +156,38 @@ namespace UnityExplorer.Tests
CppStringTest = new Il2CppSystem.Collections.Generic.List<string>();
CppStringTest.Add("1");
CppStringTest.Add("2");
//CppDictTest = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
//CppDictTest.Add("key1", "value1");
//CppDictTest.Add("key2", "value2");
//CppDictTest.Add("key3", "value3");
//CppDictTest2 = new Il2CppSystem.Collections.Generic.Dictionary<int, float>();
//CppDictTest2.Add(0, 0.5f);
//CppDictTest2.Add(1, 0.5f);
//CppDictTest2.Add(2, 0.5f);
#endif
}
private void TextureSpriteTest()
{
TestTexture = new Texture2D(32, 32, TextureFormat.ARGB32, false)
{
name = "TestTexture"
};
TestSprite = ImageConversionUnstrip.CreateSprite(TestTexture);
GameObject.DontDestroyOnLoad(TestTexture);
GameObject.DontDestroyOnLoad(TestSprite);
// test loading a tex from file
if (System.IO.File.Exists(@"D:\Downloads\test.png"))
{
var dataToLoad = System.IO.File.ReadAllBytes(@"D:\Downloads\test.png");
ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}");
}
}
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2) where T : Component
{
arg2 = "this is arg2";

View File

@ -77,11 +77,6 @@ namespace UnityExplorer.UI
catch { }
// Setup Harmony Patches
TryPatch(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_EventSystem_set_current))),
true);
TryPatch(typeof(Cursor),
"lockState",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_lockState))),
@ -91,10 +86,15 @@ namespace UnityExplorer.UI
"visible",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))),
true);
TryPatch(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_EventSystem_set_current))),
true);
}
catch (Exception e)
{
ExplorerCore.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}");
ExplorerCore.Log($"Exception on ForceUnlockCursor.Init! {e.GetType()}, {e.Message}");
}
}
@ -105,8 +105,10 @@ namespace UnityExplorer.UI
var harmony =
#if ML
ExplorerMelonMod.Instance.harmonyInstance;
#else
#elif BIE
ExplorerBepInPlugin.HarmonyInstance;
#elif STANDALONE
ExplorerStandalone.HarmonyInstance;
#endif
System.Reflection.PropertyInfo prop = type.GetProperty(property);
@ -120,10 +122,10 @@ namespace UnityExplorer.UI
harmony.Patch(prop.GetGetMethod(), postfix: patch);
}
}
catch // (Exception e)
catch (Exception e)
{
//string suf = setter ? "set_" : "get_";
//ExplorerCore.Log($"Unable to patch {type.Name}.{suf}{property}: {e.Message}");
string suf = setter ? "set_" : "get_";
ExplorerCore.Log($"Unable to patch {type.Name}.{suf}{property}: {e.Message}");
}
}
@ -158,15 +160,39 @@ namespace UnityExplorer.UI
public static void SetEventSystem()
{
// temp disabled for new InputSystem
if (InputManager.CurrentType == InputType.InputSystem)
return;
// Disable current event system object
if (m_lastEventSystem || EventSystem.current)
{
if (!m_lastEventSystem)
m_lastEventSystem = EventSystem.current;
//ExplorerCore.Log("Disabling current event system...");
m_lastEventSystem.enabled = false;
//m_lastEventSystem.gameObject.SetActive(false);
}
// Set to our current system
m_settingEventSystem = true;
UIManager.SetEventSystem();
EventSystem.current = UIManager.EventSys;
UIManager.EventSys.enabled = true;
InputManager.ActivateUIModule();
m_settingEventSystem = false;
}
public static void ReleaseEventSystem()
{
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (m_lastEventSystem)
{
m_lastEventSystem.enabled = true;
//m_lastEventSystem.gameObject.SetActive(true);
m_settingEventSystem = true;
EventSystem.current = m_lastEventSystem;
m_lastInputModule?.ActivateModule();

View File

@ -16,7 +16,7 @@ namespace UnityExplorer.UI.Modules
public static DebugConsole Instance { get; private set; }
public static bool LogUnity { get; set; } = ModConfig.Instance.Log_Unity_Debug;
public static bool SaveToDisk { get; set; } = ModConfig.Instance.Save_Logs_To_Disk;
//public static bool SaveToDisk { get; set; } = ModConfig.Instance.Save_Logs_To_Disk;
internal static StreamWriter s_streamWriter;
@ -49,8 +49,8 @@ namespace UnityExplorer.UI.Modules
// set up IO
if (!SaveToDisk)
return;
//if (!SaveToDisk)
// return;
var path = ExplorerCore.EXPLORER_FOLDER + @"\Logs";

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
//using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Config;
@ -19,6 +18,7 @@ namespace UnityExplorer.UI.Modules
private Toggle m_unlockMouseToggle;
private InputField m_pageLimitInput;
private InputField m_defaultOutputInput;
private Toggle m_hideOnStartupToggle;
public override void Init()
{
@ -27,26 +27,21 @@ namespace UnityExplorer.UI.Modules
public override void Update()
{
// not needed?
}
internal void OnApply()
{
if (!string.IsNullOrEmpty(m_keycodeInput.text) && Enum.Parse(typeof(KeyCode), m_keycodeInput.text) is KeyCode keyCode)
{
ModConfig.Instance.Main_Menu_Toggle = keyCode;
}
ModConfig.Instance.Force_Unlock_Mouse = m_unlockMouseToggle.isOn;
if (!string.IsNullOrEmpty(m_pageLimitInput.text) && int.TryParse(m_pageLimitInput.text, out int lim))
{
ModConfig.Instance.Default_Page_Limit = lim;
}
ModConfig.Instance.Default_Output_Path = m_defaultOutputInput.text;
// todo default output path
ModConfig.Instance.Hide_On_Startup = m_hideOnStartupToggle.isOn;
ModConfig.SaveSettings();
ModConfig.InvokeConfigChanged();
@ -98,6 +93,7 @@ namespace UnityExplorer.UI.Modules
ConstructMouseUnlockOpt(optionsGroupObj);
ConstructPageLimitOpt(optionsGroupObj);
ConstructOutputPathOpt(optionsGroupObj);
ConstructHideOnStartupOpt(optionsGroupObj);
var applyBtnObj = UIFactory.CreateButton(Content, new Color(0.2f, 0.2f, 0.2f));
var applyText = applyBtnObj.GetComponentInChildren<Text>();
@ -113,10 +109,34 @@ namespace UnityExplorer.UI.Modules
applyBtn.onClick.AddListener(OnApply);
}
private void ConstructHideOnStartupOpt(GameObject optionsGroupObj)
{
var rowObj = UIFactory.CreateHorizontalGroup(optionsGroupObj, new Color(1, 1, 1, 0));
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
rowGroup.childControlWidth = true;
rowGroup.childForceExpandWidth = false;
rowGroup.childControlHeight = true;
rowGroup.childForceExpandHeight = true;
var groupLayout = rowObj.AddComponent<LayoutElement>();
groupLayout.minHeight = 25;
groupLayout.flexibleHeight = 0;
groupLayout.minWidth = 200;
groupLayout.flexibleWidth = 1000;
var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
var labelText = labelObj.GetComponent<Text>();
labelText.text = "Hide UI on startup:";
var labelLayout = labelObj.AddComponent<LayoutElement>();
labelLayout.minWidth = 150;
labelLayout.minHeight = 25;
UIFactory.CreateToggle(rowObj, out m_hideOnStartupToggle, out Text toggleText);
m_hideOnStartupToggle.isOn = ModConfig.Instance.Hide_On_Startup;
toggleText.text = "";
}
internal void ConstructKeycodeOpt(GameObject parent)
{
//public KeyCode Main_Menu_Toggle = KeyCode.F7;
var rowObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
rowGroup.childControlWidth = true;
@ -146,8 +166,6 @@ namespace UnityExplorer.UI.Modules
internal void ConstructMouseUnlockOpt(GameObject parent)
{
//public bool Force_Unlock_Mouse = true;
var rowObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
rowGroup.childControlWidth = true;

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
//using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
@ -9,6 +8,7 @@ using UnityExplorer.Helpers;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Shared;
using UnityExplorer.Unstrip;
using System.Reflection;
#if CPP
using UnhollowerRuntimeLib;
#endif
@ -21,7 +21,7 @@ namespace UnityExplorer.UI.Modules
GameObject,
Component,
Custom,
Instance,
Singleton,
StaticClass
}
@ -46,11 +46,14 @@ namespace UnityExplorer.UI.Modules
public static SearchPage Instance;
internal SearchContext m_context;
private SceneFilter m_sceneFilter;
private ChildFilter m_childFilter;
// ui elements
private Text m_resultCountText;
internal SearchContext m_context;
private InputField m_customTypeInput;
private InputField m_nameInput;
@ -60,9 +63,6 @@ namespace UnityExplorer.UI.Modules
private Dropdown m_sceneDropdown;
private int m_lastSceneCount = -1;
private SceneFilter m_sceneFilter;
private ChildFilter m_childFilter;
private GameObject m_extraFilterRow;
@ -132,10 +132,9 @@ namespace UnityExplorer.UI.Modules
else
{
var obj = m_results[itemIndex];
var unityObj = obj as UnityEngine.Object;
var uObj = obj as UnityEngine.Object;
if (obj == null || (uObj != null && !uObj))
if (obj == null || (unityObj != null && !unityObj))
continue;
if (i >= m_resultShortList.Count)
@ -150,17 +149,25 @@ namespace UnityExplorer.UI.Modules
var text = m_resultListTexts[i];
var name = $"<color={UISyntaxHighlight.Class_Instance}>{ReflectionHelpers.GetActualType(obj).Name}</color>";
if (m_context != SearchContext.Instance && m_context != SearchContext.StaticClass)
if (m_context != SearchContext.StaticClass)
{
if (uObj && !string.IsNullOrEmpty(uObj.name))
name += $": {uObj.name}";
else
name += ": <i><color=grey>untitled</color></i>";
}
var name = UISyntaxHighlight.ParseFullSyntax(obj.GetActualType(), true);
text.text = name;
if (unityObj && m_context != SearchContext.Singleton)
{
if (unityObj && !string.IsNullOrEmpty(unityObj.name))
name += $": {unityObj.name}";
else
name += ": <i><color=grey>untitled</color></i>";
}
text.text = name;
}
else
{
var type = obj as Type;
text.text = UISyntaxHighlight.ParseFullSyntax(type, true);
}
var label = text.transform.parent.parent.gameObject;
if (!label.activeSelf)
@ -222,14 +229,127 @@ namespace UnityExplorer.UI.Modules
}
m_sceneDropdown.transform.Find("Label").GetComponent<Text>().text = "Any";
m_sceneFilter = SceneFilter.Any;
}
// ~~~~~ UI Callbacks ~~~~~
internal void OnUnitySearchClicked()
internal void OnSearchClicked()
{
m_resultListPageHandler.CurrentPage = 0;
if (m_context == SearchContext.StaticClass)
StaticClassSearch();
else if (m_context == SearchContext.Singleton)
SingletonSearch();
else
UnityObjectSearch();
RefreshResultList();
if (m_results.Length > 0)
m_resultCountText.text = $"{m_results.Length} Results";
else
m_resultCountText.text = "No results...";
}
internal void StaticClassSearch()
{
var list = new List<Type>();
var nameFilter = "";
if (!string.IsNullOrEmpty(m_nameInput.text))
nameFilter = m_nameInput.text.ToLower();
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes().Where(it => it.IsSealed && it.IsAbstract))
{
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
continue;
list.Add(type);
}
}
m_results = list.ToArray();
}
internal string[] s_instanceNames = new string[]
{
"m_instance",
"m_Instance",
"s_instance",
"s_Instance",
"_instance",
"_Instance",
"instance",
"Instance",
"<Instance>k__BackingField",
"<instance>k__BackingField",
};
private void SingletonSearch()
{
var instances = new List<object>();
var nameFilter = "";
if (!string.IsNullOrEmpty(m_nameInput.text))
nameFilter = m_nameInput.text.ToLower();
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
// Search all non-static, non-enum classes.
foreach (var type in asm.TryGetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum))
{
try
{
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
continue;
#if CPP
// Only look for Properties in IL2CPP, not for Mono.
PropertyInfo pi;
foreach (var name in s_instanceNames)
{
pi = type.GetProperty(name, flags);
if (pi != null)
{
var instance = pi.GetValue(null, null);
if (instance != null)
{
instances.Add(instance);
continue;
}
}
}
#endif
// Look for a typical Instance backing field.
FieldInfo fi;
foreach (var name in s_instanceNames)
{
fi = type.GetField(name, flags);
if (fi != null)
{
var instance = fi.GetValue(null);
if (instance != null)
{
instances.Add(instance);
break;
}
}
}
}
catch { }
}
}
m_results = instances.ToArray();
}
internal void UnityObjectSearch()
{
Type searchType = null;
switch (m_context)
{
@ -260,11 +380,8 @@ namespace UnityExplorer.UI.Modules
if (searchType == null)
return;
#if MONO
var allObjects = ResourcesUnstrip.FindObjectsOfTypeAll(searchType);
#else
var allObjects = ResourcesUnstrip.FindObjectsOfTypeAll(Il2CppType.From(searchType));
#endif
var results = new List<object>();
// perform filter comparers
@ -308,12 +425,12 @@ namespace UnityExplorer.UI.Modules
: obj.TryCast<Component>().gameObject;
#endif
if (!go)
continue;
// scene check
if (m_sceneFilter != SceneFilter.Any)
{
if (!go)
continue;
switch (m_context)
{
case SearchContext.GameObject:
@ -328,24 +445,23 @@ namespace UnityExplorer.UI.Modules
}
}
// root object check (no parent)
if (m_childFilter == ChildFilter.HasParent && !go.transform.parent)
continue;
else if (m_childFilter == ChildFilter.RootObject && go.transform.parent)
continue;
if (m_childFilter != ChildFilter.Any)
{
if (!go)
continue;
// root object check (no parent)
if (m_childFilter == ChildFilter.HasParent && !go.transform.parent)
continue;
else if (m_childFilter == ChildFilter.RootObject && go.transform.parent)
continue;
}
}
results.Add(obj);
}
m_results = results.ToArray();
if (m_results.Length > 0)
m_resultCountText.text = $"{m_results.Length} Results";
else
m_resultCountText.text = "No results...";
RefreshResultList();
}
private void OnResultPageTurn()
@ -493,6 +609,23 @@ namespace UnityExplorer.UI.Modules
m_customTypeInput = customTypeObj.GetComponent<InputField>();
m_customTypeInput.placeholder.gameObject.GetComponent<Text>().text = "eg. UnityEngine.Texture2D, etc...";
// static class and singleton buttons
var secondRow = UIFactory.CreateHorizontalGroup(optionsGroupObj, new Color(1, 1, 1, 0));
var secondGroup = secondRow.GetComponent<HorizontalLayoutGroup>();
secondGroup.childForceExpandWidth = false;
secondGroup.childForceExpandHeight = false;
secondGroup.spacing = 3;
var secondLayout = secondRow.AddComponent<LayoutElement>();
secondLayout.minHeight = 25;
var spacer = UIFactory.CreateUIObject("spacer", secondRow);
var spaceLayout = spacer.AddComponent<LayoutElement>();
spaceLayout.minWidth = 125;
spaceLayout.minHeight = 25;
AddContextButton(secondRow, "Static Class", SearchContext.StaticClass);
AddContextButton(secondRow, "Singleton", SearchContext.Singleton);
// search input
var nameRowObj = UIFactory.CreateHorizontalGroup(optionsGroupObj, new Color(1, 1, 1, 0));
@ -604,7 +737,7 @@ namespace UnityExplorer.UI.Modules
searchBtnLayout.flexibleHeight = 0;
var searchBtn = searchBtnObj.GetComponent<Button>();
searchBtn.onClick.AddListener(OnUnitySearchClicked);
searchBtn.onClick.AddListener(OnSearchClicked);
}
internal void AddContextButton(GameObject parent, string label, SearchContext context, float width = 110)

View File

@ -33,6 +33,8 @@ namespace UnityExplorer.UI.Shared
this.sliderScroller = sliderScroller;
this.inputField = inputField;
sliderScroller.m_parentInputScroller = this;
inputField.onValueChanged.AddListener(OnTextChanged);
inputRect = inputField.GetComponent<RectTransform>();
@ -68,6 +70,17 @@ namespace UnityExplorer.UI.Shared
}
}
internal bool CheckDestroyed()
{
if (sliderScroller == null || sliderScroller.CheckDestroyed())
{
Instances.Remove(this);
return true;
}
return false;
}
internal void OnTextChanged(string text)
{
m_lastText = text;

View File

@ -8,150 +8,154 @@ using UnityExplorer;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
// Basically just to fix an issue with Scrollbars, instead we use a Slider as the scrollbar.
public class SliderScrollbar
namespace UnityExplorer.UI.Shared
{
internal static readonly List<SliderScrollbar> Instances = new List<SliderScrollbar>();
public bool IsActive { get; private set; }
internal readonly Scrollbar m_scrollbar;
internal readonly Slider m_slider;
internal readonly RectTransform m_scrollRect;
public SliderScrollbar(Scrollbar scrollbar, Slider slider)
// Basically just to fix an issue with Scrollbars, instead we use a Slider as the scrollbar.
public class SliderScrollbar
{
Instances.Add(this);
internal static readonly List<SliderScrollbar> Instances = new List<SliderScrollbar>();
this.m_scrollbar = scrollbar;
this.m_slider = slider;
this.m_scrollRect = scrollbar.transform.parent.GetComponent<RectTransform>();
public bool IsActive { get; private set; }
this.m_scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
this.m_slider.onValueChanged.AddListener(this.OnSliderValueChanged);
internal readonly Scrollbar m_scrollbar;
internal readonly Slider m_slider;
internal readonly RectTransform m_scrollRect;
this.RefreshVisibility();
this.m_slider.Set(1f, false);
}
internal InputFieldScroller m_parentInputScroller;
internal bool CheckDestroyed()
{
if (!m_slider || !m_scrollbar)
public SliderScrollbar(Scrollbar scrollbar, Slider slider)
{
Instances.Remove(this);
return true;
Instances.Add(this);
this.m_scrollbar = scrollbar;
this.m_slider = slider;
this.m_scrollRect = scrollbar.transform.parent.GetComponent<RectTransform>();
this.m_scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
this.m_slider.onValueChanged.AddListener(this.OnSliderValueChanged);
this.RefreshVisibility();
this.m_slider.Set(1f, false);
}
return false;
}
internal void Update()
{
this.RefreshVisibility();
}
internal void RefreshVisibility()
{
if (!m_slider.gameObject.activeInHierarchy)
internal bool CheckDestroyed()
{
IsActive = false;
return;
if (!m_slider || !m_scrollbar)
{
Instances.Remove(this);
return true;
}
return false;
}
bool shouldShow = !Mathf.Approximately(this.m_scrollbar.size, 1);
var obj = this.m_slider.handleRect.gameObject;
if (IsActive != shouldShow)
internal void Update()
{
IsActive = shouldShow;
obj.SetActive(IsActive);
if (IsActive)
this.m_slider.Set(this.m_scrollbar.value, false);
else
m_slider.Set(1f, false);
this.RefreshVisibility();
}
internal void RefreshVisibility()
{
if (!m_slider.gameObject.activeInHierarchy)
{
IsActive = false;
return;
}
bool shouldShow = !Mathf.Approximately(this.m_scrollbar.size, 1);
var obj = this.m_slider.handleRect.gameObject;
if (IsActive != shouldShow)
{
IsActive = shouldShow;
obj.SetActive(IsActive);
if (IsActive)
this.m_slider.Set(this.m_scrollbar.value, false);
else
m_slider.Set(1f, false);
}
}
public void OnScrollbarValueChanged(float _value)
{
if (this.m_slider.value != _value)
this.m_slider.Set(_value, false);
}
public void OnSliderValueChanged(float _value)
{
this.m_scrollbar.value = _value;
}
#region UI CONSTRUCTION
public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider)
{
GameObject sliderObj = UIFactory.CreateUIObject("Slider", parent, UIFactory.thinSize);
GameObject bgObj = UIFactory.CreateUIObject("Background", sliderObj);
GameObject fillAreaObj = UIFactory.CreateUIObject("Fill Area", sliderObj);
GameObject fillObj = UIFactory.CreateUIObject("Fill", fillAreaObj);
GameObject handleSlideAreaObj = UIFactory.CreateUIObject("Handle Slide Area", sliderObj);
GameObject handleObj = UIFactory.CreateUIObject("Handle", handleSlideAreaObj);
Image bgImage = bgObj.AddComponent<Image>();
bgImage.type = Image.Type.Sliced;
bgImage.color = new Color(0.05f, 0.05f, 0.05f, 1.0f);
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
bgRect.offsetMax = new Vector2(-10f, 0f);
RectTransform fillAreaRect = fillAreaObj.GetComponent<RectTransform>();
fillAreaRect.anchorMin = new Vector2(0f, 0.25f);
fillAreaRect.anchorMax = new Vector2(1f, 0.75f);
fillAreaRect.anchoredPosition = new Vector2(-5f, 0f);
fillAreaRect.sizeDelta = new Vector2(-20f, 0f);
Image fillImage = fillObj.AddComponent<Image>();
fillImage.type = Image.Type.Sliced;
fillImage.color = Color.clear;
fillObj.GetComponent<RectTransform>().sizeDelta = new Vector2(10f, 0f);
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
handleSlideRect.anchorMin = new Vector2(0f, 0f);
handleSlideRect.anchorMax = new Vector2(1f, 1f);
handleSlideRect.offsetMin = new Vector2(15f, 30f);
handleSlideRect.offsetMax = new Vector2(-15f, 0f);
handleSlideRect.sizeDelta = new Vector2(-30f, -30f);
Image handleImage = handleObj.AddComponent<Image>();
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
var handleRect = handleObj.GetComponent<RectTransform>();
handleRect.sizeDelta = new Vector2(15f, 30f);
handleRect.offsetMin = new Vector2(-13f, -28f);
handleRect.offsetMax = new Vector2(3f, -2f);
var sliderBarLayout = sliderObj.AddComponent<LayoutElement>();
sliderBarLayout.minWidth = 25;
sliderBarLayout.flexibleWidth = 0;
sliderBarLayout.minHeight = 30;
sliderBarLayout.flexibleHeight = 5000;
slider = sliderObj.AddComponent<Slider>();
slider.fillRect = fillObj.GetComponent<RectTransform>();
slider.handleRect = handleObj.GetComponent<RectTransform>();
slider.targetGraphic = handleImage;
slider.direction = Slider.Direction.BottomToTop;
UIFactory.SetDefaultColorTransitionValues(slider);
return sliderObj;
}
#endregion
}
public void OnScrollbarValueChanged(float _value)
{
if (this.m_slider.value != _value)
this.m_slider.Set(_value, false);
}
public void OnSliderValueChanged(float _value)
{
this.m_scrollbar.value = _value;
}
#region UI CONSTRUCTION
public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider)
{
GameObject sliderObj = UIFactory.CreateUIObject("Slider", parent, UIFactory.thinSize);
GameObject bgObj = UIFactory.CreateUIObject("Background", sliderObj);
GameObject fillAreaObj = UIFactory.CreateUIObject("Fill Area", sliderObj);
GameObject fillObj = UIFactory.CreateUIObject("Fill", fillAreaObj);
GameObject handleSlideAreaObj = UIFactory.CreateUIObject("Handle Slide Area", sliderObj);
GameObject handleObj = UIFactory.CreateUIObject("Handle", handleSlideAreaObj);
Image bgImage = bgObj.AddComponent<Image>();
bgImage.type = Image.Type.Sliced;
bgImage.color = new Color(0.05f, 0.05f, 0.05f, 1.0f);
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
bgRect.offsetMax = new Vector2(-10f, 0f);
RectTransform fillAreaRect = fillAreaObj.GetComponent<RectTransform>();
fillAreaRect.anchorMin = new Vector2(0f, 0.25f);
fillAreaRect.anchorMax = new Vector2(1f, 0.75f);
fillAreaRect.anchoredPosition = new Vector2(-5f, 0f);
fillAreaRect.sizeDelta = new Vector2(-20f, 0f);
Image fillImage = fillObj.AddComponent<Image>();
fillImage.type = Image.Type.Sliced;
fillImage.color = Color.clear;
fillObj.GetComponent<RectTransform>().sizeDelta = new Vector2(10f, 0f);
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
handleSlideRect.anchorMin = new Vector2(0f, 0f);
handleSlideRect.anchorMax = new Vector2(1f, 1f);
handleSlideRect.offsetMin = new Vector2(15f, 30f);
handleSlideRect.offsetMax = new Vector2(-15f, 0f);
handleSlideRect.sizeDelta = new Vector2(-30f, -30f);
Image handleImage = handleObj.AddComponent<Image>();
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
var handleRect = handleObj.GetComponent<RectTransform>();
handleRect.sizeDelta = new Vector2(15f, 30f);
handleRect.offsetMin = new Vector2(-13f, -28f);
handleRect.offsetMax = new Vector2(3f, -2f);
var sliderBarLayout = sliderObj.AddComponent<LayoutElement>();
sliderBarLayout.minWidth = 25;
sliderBarLayout.flexibleWidth = 0;
sliderBarLayout.minHeight = 30;
sliderBarLayout.flexibleHeight = 5000;
slider = sliderObj.AddComponent<Slider>();
slider.fillRect = fillObj.GetComponent<RectTransform>();
slider.handleRect = handleObj.GetComponent<RectTransform>();
slider.targetGraphic = handleImage;
slider.direction = Slider.Direction.BottomToTop;
UIFactory.SetDefaultColorTransitionValues(slider);
return sliderObj;
}
#endregion
}
#if MONO
public static class SliderExtensions
{
@ -175,4 +179,5 @@ public static class SliderExtensions
SetMethod.Invoke(slider, new object[] { value, invokeCallback });
}
}
#endif
#endif
}

View File

@ -178,13 +178,9 @@ namespace UnityExplorer.UI
Image image = groupObj.AddComponent<Image>();
if (color != default)
{
image.color = color;
}
else
{
image.color = new Color(44f / 255f, 44f / 255f, 44f / 255f);
}
return groupObj;
}
@ -657,16 +653,16 @@ namespace UnityExplorer.UI
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var contentLayout = content.AddComponent<VerticalLayoutGroup>();
contentLayout.childForceExpandHeight = true;
contentLayout.childControlHeight = true;
contentLayout.childForceExpandWidth = true;
contentLayout.childControlWidth = true;
contentLayout.padding.left = 5;
contentLayout.padding.right = 5;
contentLayout.padding.top = 5;
contentLayout.padding.bottom = 5;
contentLayout.spacing = 5;
var contentGroup = content.AddComponent<VerticalLayoutGroup>();
contentGroup.childForceExpandHeight = true;
contentGroup.childControlHeight = true;
contentGroup.childForceExpandWidth = true;
contentGroup.childControlWidth = true;
contentGroup.padding.left = 5;
contentGroup.padding.right = 5;
contentGroup.padding.top = 5;
contentGroup.padding.bottom = 5;
contentGroup.spacing = 5;
GameObject scrollBarObj = CreateUIObject("DynamicScrollbar", mainObj);

View File

@ -4,10 +4,10 @@ using UnityEngine.UI;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Modules;
using System.IO;
//using TMPro;
using System.Reflection;
using UnityExplorer.Helpers;
using UnityExplorer.UI.Shared;
using UnityExplorer.Input;
#if CPP
using UnityExplorer.Unstrip;
#endif
@ -18,37 +18,15 @@ namespace UnityExplorer.UI
{
public static GameObject CanvasRoot { get; private set; }
public static EventSystem EventSys { get; private set; }
public static StandaloneInputModule InputModule { get; private set; }
//internal static Material UIMaterial { get; private set; }
internal static Sprite ResizeCursor { get; private set; }
internal static Font ConsoleFont { get; private set; }
internal static Sprite ResizeCursor { get; private set; }
internal static Shader BackupShader { get; private set; }
public static void Init()
{
var bundlePath = ExplorerCore.EXPLORER_FOLDER + @"\explorerui.bundle";
if (File.Exists(bundlePath))
{
var bundle = AssetBundle.LoadFromFile(bundlePath);
// Fix for games which don't ship with 'UI/Default' shader.
if (Graphic.defaultGraphicMaterial.shader?.name != "UI/Default")
{
ExplorerCore.Log("This game does not ship with the 'UI/Default' shader, using manual Default Shader...");
Graphic.defaultGraphicMaterial.shader = bundle.LoadAsset<Shader>("DefaultUI");
}
ResizeCursor = bundle.LoadAsset<Sprite>("cursor");
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
ExplorerCore.Log("Loaded UI bundle");
}
else
{
ExplorerCore.LogWarning("Could not find the ExplorerUI Bundle! It should exist at '" + bundlePath + "'");
return;
}
LoadBundle();
// Create core UI Canvas and Event System handler
CreateRootCanvas();
@ -62,47 +40,32 @@ namespace UnityExplorer.UI
Canvas.ForceUpdateCanvases();
}
public static void SetEventSystem()
{
EventSystem.current = EventSys;
InputModule.ActivateModule();
}
public static void OnSceneChange()
{
SceneExplorer.Instance?.OnSceneChange();
SearchPage.Instance?.OnSceneChange();
}
public static void Update()
{
MainMenu.Instance?.Update();
if (EventSys && InputModule)
if (EventSys)
{
if (EventSystem.current != EventSys)
{
ForceUnlockCursor.SetEventSystem();
//ForceUnlockCursor.Unlock = true;
}
// Fix for games which override the InputModule pointer events (eg, VRChat)
#if CPP
if (InputModule.m_InputPointerEvent != null)
// Some IL2CPP games behave weird with multiple UI Input Systems, some fixes for them.
var evt = InputManager.InputPointerEvent;
if (evt != null)
{
PointerEventData evt = InputModule.m_InputPointerEvent;
if (!evt.eligibleForClick && evt.selectedObject)
{
evt.eligibleForClick = true;
}
}
#endif
}
if (PanelDragger.Instance != null)
{
PanelDragger.Instance.Update();
}
for (int i = 0; i < SliderScrollbar.Instances.Count; i++)
{
@ -118,13 +81,75 @@ namespace UnityExplorer.UI
{
var input = InputFieldScroller.Instances[i];
if (input.sliderScroller.CheckDestroyed())
if (input.CheckDestroyed())
i--;
else
input.Update();
}
}
private static AssetBundle LoadExplorerUi(string id)
{
return AssetBundle.LoadFromMemory(ReadFully(typeof(ExplorerCore).Assembly.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle")));
}
private static byte[] ReadFully(this Stream input)
{
using (var ms = new MemoryStream())
{
byte[] buffer = new byte[81920];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) != 0)
ms.Write(buffer, 0, read);
return ms.ToArray();
}
}
private static void LoadBundle()
{
AssetBundle bundle = null;
try
{
bundle = LoadExplorerUi("modern");
}
catch
{
ExplorerCore.Log("Failed to load modern ExplorerUI Bundle, falling back to legacy");
try
{
bundle = LoadExplorerUi("legacy");
}
catch
{
// ignored
}
}
if (bundle == null)
{
ExplorerCore.LogWarning("Could not load the ExplorerUI Bundle!");
return;
}
BackupShader = bundle.LoadAsset<Shader>("DefaultUI");
// Fix for games which don't ship with 'UI/Default' shader.
if (Graphic.defaultGraphicMaterial.shader?.name != "UI/Default")
{
ExplorerCore.Log("This game does not ship with the 'UI/Default' shader, using manual Default Shader...");
Graphic.defaultGraphicMaterial.shader = BackupShader;
}
ResizeCursor = bundle.LoadAsset<Sprite>("cursor");
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
ExplorerCore.Log("Loaded UI bundle");
}
private static GameObject CreateRootCanvas()
{
GameObject rootObj = new GameObject("ExplorerCanvas");
@ -135,14 +160,13 @@ namespace UnityExplorer.UI
CanvasRoot.transform.position = new Vector3(0f, 0f, 1f);
EventSys = rootObj.AddComponent<EventSystem>();
InputModule = rootObj.AddComponent<StandaloneInputModule>();
InputModule.ActivateModule();
InputManager.AddUIModule();
Canvas canvas = rootObj.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
canvas.referencePixelsPerUnit = 100;
canvas.sortingOrder = 999;
canvas.pixelPerfect = false;
//canvas.pixelPerfect = false;
CanvasScaler scaler = rootObj.AddComponent<CanvasScaler>();
scaler.referenceResolution = new Vector2(1920, 1080);
@ -152,37 +176,5 @@ namespace UnityExplorer.UI
return rootObj;
}
public static Sprite CreateSprite(Texture2D tex, Rect size = default)
{
#if CPP
Vector2 pivot = Vector2.zero;
Vector4 border = Vector4.zero;
if (size == default)
{
size = new Rect(0, 0, tex.width, tex.height);
}
return Sprite.CreateSprite_Injected(tex, ref size, ref pivot, 100f, 0u, SpriteMeshType.Tight, ref border, false);
#else
return Sprite.Create(tex, size, Vector2.zero);
#endif
}
public static Texture2D MakeSolidTexture(Color color, int width, int height)
{
Color[] pixels = new Color[width * height];
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = color;
}
Texture2D tex = new Texture2D(width, height);
tex.SetPixels(pixels);
tex.Apply();
return tex;
}
}
}

View File

@ -42,10 +42,10 @@ namespace UnityExplorer.UI
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
{
string ret = "";
if (type == null)
return "????????????";
throw new ArgumentNullException("type");
string ret = "";
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{

View File

@ -25,17 +25,9 @@
<Prefer32Bit>false</Prefer32Bit>
<RootNamespace>UnityExplorer</RootNamespace>
<!-- Set this to the BepInEx Il2Cpp Game folder, without the ending '\' character. -->
<BIECppGameFolder>D:\Steam\steamapps\common\Outward</BIECppGameFolder>
<!-- Set this to the BepInEx Mono Game folder, without the ending '\' character. -->
<BIEMonoGameFolder>D:\source\Unity Projects\Test\_BUILD_MONO</BIEMonoGameFolder>
<!-- Set this to the BepInEx Mono Managed folder, without the ending '\' character. -->
<BIEMonoManagedFolder>D:\source\Unity Projects\Test\_BUILD_MONO\Test_Data\Managed</BIEMonoManagedFolder>
<BIECppGameFolder>E:\source\Unity Projects\Test\_BUILD</BIECppGameFolder>
<!-- Set this to the MelonLoader Il2Cpp Game folder, without the ending '\' character. -->
<MLCppGameFolder>D:\Steam\steamapps\common\VRChat</MLCppGameFolder>
<!-- Set this to the MelonLoader Mono Game folder, without the ending '\' character. -->
<MLMonoGameFolder>D:\source\Unity Projects\Test\_BUILD_MONO</MLMonoGameFolder>
<!-- Set this to the MelonLoader Mono Managed folder, without the ending '\' character. -->
<MLMonoManagedFolder>D:\source\Unity Projects\Test\_BUILD_MONO\Test_Data\Managed</MLMonoManagedFolder>
<MLCppGameFolder>E:\source\Unity Projects\Test\_BUILD</MLCppGameFolder>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
@ -76,7 +68,29 @@
<IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_STANDALONE_Mono|AnyCPU'">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.Standalone.Mono\</OutputPath>
<DefineConstants>MONO,STANDALONE</DefineConstants>
<AssemblyName>UnityExplorer.STANDALONE.Mono</AssemblyName>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_STANDALONE_Cpp|AnyCPU'">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.Standalone.Il2Cpp\</OutputPath>
<DefineConstants>CPP,STANDALONE</DefineConstants>
<AssemblyName>UnityExplorer.STANDALONE.IL2CPP</AssemblyName>
<IsCpp>true</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="INIFileParser, Version=2.5.2.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
<HintPath>packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -103,7 +117,7 @@
<!-- MelonLoader Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'">
<Reference Include="MelonLoader.ModHandler">
<HintPath>$(MLMonoGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<HintPath>..\lib\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
@ -221,6 +235,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ExplorerStandalone.cs" />
<Compile Include="Helpers\EventHelper.cs" />
<Compile Include="Inspectors\MouseInspector.cs" />
<Compile Include="Inspectors\Reflection\CacheObject\CacheEnumerated.cs" />
@ -297,6 +312,7 @@
<Compile Include="Unstrip\SceneUnstrip.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\UIFactory.cs" />
<EmbeddedResource Include="Resources\*" />
</ItemGroup>
<ItemGroup>
<None Include="ILRepack.targets" />

View File

@ -11,6 +11,8 @@ Global
Release_BIE_Mono|Any CPU = Release_BIE_Mono|Any CPU
Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU
Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU
Release_STANDALONE_Cpp|Any CPU = Release_STANDALONE_Cpp|Any CPU
Release_STANDALONE_Mono|Any CPU = Release_STANDALONE_Mono|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release_BIE_Cpp|Any CPU
@ -21,6 +23,10 @@ Global
{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|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
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release_STANDALONE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release_STANDALONE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release_STANDALONE_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Mono|Any CPU.Build.0 = Release_STANDALONE_Mono|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -24,6 +24,17 @@ namespace UnityExplorer.Unstrip
return new AssetBundle(ptr);
}
private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc);
public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0)
{
var iCall = ICallHelper.GetICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal");
var ptr = iCall(((Il2CppStructArray<byte>) binary).Pointer, crc);
return new AssetBundle(ptr);
}
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~

View File

@ -10,6 +10,16 @@ namespace UnityExplorer.Unstrip
{
public static class ImageConversionUnstrip
{
// LoadImage helper from a filepath
public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable)
{
if (!File.Exists(filePath))
return false;
return tex.LoadImage(File.ReadAllBytes(filePath), markNonReadable);
}
#if CPP
// byte[] ImageConversion.EncodeToPNG(this Texture2D image);
@ -17,8 +27,12 @@ namespace UnityExplorer.Unstrip
public static byte[] EncodeToPNG(this Texture2D tex)
{
IntPtr ptr = ICallHelper.GetICall<d_EncodeToPNG>("UnityEngine.ImageConversion::EncodeToPNG")
.Invoke(tex.Pointer);
var iCall = ICallHelper.GetICall<d_EncodeToPNG>("UnityEngine.ImageConversion::EncodeToPNG");
IntPtr ptr = iCall.Invoke(tex.Pointer);
if (ptr == IntPtr.Zero)
return null;
return new Il2CppStructArray<byte>(ptr);
}
@ -29,28 +43,40 @@ namespace UnityExplorer.Unstrip
public static bool LoadImage(this Texture2D tex, byte[] data, bool markNonReadable)
{
Il2CppStructArray<byte> il2cppArray = new Il2CppStructArray<byte>(data.Length);
for (int i = 0; i < data.Length; i++)
il2cppArray[i] = data[i];
var il2cppArray = (Il2CppStructArray<byte>)data;
bool ret = ICallHelper.GetICall<d_LoadImage>("UnityEngine.ImageConversion::LoadImage")
.Invoke(tex.Pointer, il2cppArray.Pointer, markNonReadable);
var iCall = ICallHelper.GetICall<d_LoadImage>("UnityEngine.ImageConversion::LoadImage");
return iCall.Invoke(tex.Pointer, il2cppArray.Pointer, markNonReadable);
}
return ret;
// Sprite Sprite.Create
internal delegate IntPtr d_CreateSprite(IntPtr texture, ref Rect rect, ref Vector2 pivot, float pixelsPerUnit,
uint extrude, int meshType, ref Vector4 border, bool generateFallbackPhysicsShape);
public static Sprite CreateSprite(Texture texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border)
{
var iCall = ICallHelper.GetICall<d_CreateSprite>("UnityEngine.Sprite::CreateSprite_Injected");
var ptr = iCall.Invoke(texture.Pointer, ref rect, ref pivot, pixelsPerUnit, extrude, 1, ref border, false);
if (ptr == IntPtr.Zero)
return null;
else
return new Sprite(ptr);
}
#endif
// Helper for LoadImage from filepath
// Simpler CreateSprite helper
public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable)
public static Sprite CreateSprite(Texture2D texture)
{
if (!File.Exists(filePath))
{
return false;
}
byte[] data = File.ReadAllBytes(filePath);
return tex.LoadImage(data, markNonReadable);
#if CPP
return CreateSprite(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero, 100f, 0u, Vector4.zero);
#else
return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
#endif
}
}
}

View File

@ -3,24 +3,27 @@ using Mono.CSharp;
using UnityExplorer.Helpers;
#if CPP
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
#endif
namespace UnityExplorer.Unstrip
{
public class ResourcesUnstrip
{
public static UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
{
#if MONO
return UnityEngine.Resources.FindObjectsOfTypeAll(type);
#else
var iCall = ICallHelper.GetICall<d_FindObjectsOfTypeAll>("UnityEngine.Resources::FindObjectsOfTypeAll");
var cppType = Il2CppType.From(type);
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(cppType.Pointer));
#endif
}
#if CPP
internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type);
public static UnityEngine.Object[] FindObjectsOfTypeAll(Il2CppSystem.Type type)
{
var iCall = ICallHelper.GetICall<d_FindObjectsOfTypeAll>("UnityEngine.Resources::FindObjectsOfTypeAll");
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(type.Pointer));
}
#else
public static UnityEngine.Object[] FindObjectsOfTypeAll(Type type) => UnityEngine.Resources.FindObjectsOfTypeAll(type);
#endif
}
}

View File

@ -3,36 +3,25 @@ using UnityExplorer.Helpers;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityExplorer.Inspectors;
using System.Reflection;
namespace UnityExplorer.Unstrip
{
public class SceneUnstrip
public static class SceneUnstrip
{
#if MONO
public static GameObject[] GetRootGameObjects(Scene scene) => scene.GetRootGameObjects();
//public static GameObject[] GetRootGameObjects(int handle)
//{
// Scene scene = default;
// if (handle == SceneExplorer.DontDestroyHandle)
// scene = SceneExplorer.DontDestroyObject.scene;
// else
// {
// for (int i = 0; i < SceneManager.sceneCount; i++)
// {
// var iscene = SceneManager.GetSceneAt(i);
// if (iscene.handle == handle)
// scene = iscene;
// }
// }
// if (scene != default && scene.handle != -1)
// return scene.GetRootGameObjects();
// return new GameObject[0];
//}
private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionHelpers.CommonFlags);
#endif
public static int GetHandle(this Scene scene)
{
#if CPP
return scene.handle;
#else
return (int)fi_Scene_handle.GetValue(scene);
#endif
}
#if CPP
//Scene.GetRootGameObjects();
@ -43,13 +32,16 @@ namespace UnityExplorer.Unstrip
public static GameObject[] GetRootGameObjects(int handle)
{
if (handle == -1)
{
return new GameObject[0];
}
Il2CppSystem.Collections.Generic.List<GameObject> list = new Il2CppSystem.Collections.Generic.List<GameObject>(GetRootCount(handle));
int count = GetRootCount(handle);
d_GetRootGameObjects iCall = ICallHelper.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
if (count < 1)
return new GameObject[0];
var list = new Il2CppSystem.Collections.Generic.List<GameObject>(count);
var iCall = ICallHelper.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
iCall.Invoke(handle, list.Pointer);
@ -58,14 +50,14 @@ namespace UnityExplorer.Unstrip
//Scene.rootCount;
internal delegate int GetRootCountInternal_delegate(int handle);
internal delegate int d_GetRootCountInternal(int handle);
public static int GetRootCount(Scene scene) => GetRootCount(scene.handle);
public static int GetRootCount(int handle)
{
GetRootCountInternal_delegate iCall = ICallHelper.GetICall<GetRootCountInternal_delegate>("UnityEngine.SceneManagement.Scene::GetRootCountInternal");
return iCall.Invoke(handle);
return ICallHelper.GetICall<d_GetRootCountInternal>("UnityEngine.SceneManagement.Scene::GetRootCountInternal")
.Invoke(handle);
}
#endif
}

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.1" targetFramework="net472" />
<package id="ini-parser" version="2.5.2" targetFramework="net35" />
</packages>