Compare commits

..

11 Commits
3.1.0 ... 3.1.5

14 changed files with 166 additions and 75 deletions

View File

@ -49,15 +49,14 @@
0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. 0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game.
1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above. 1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
2. Take the `UnityExplorer.dll` file and put it in `[GameFolder]\BepInEx\plugins\` 2. Take the `UnityExplorer.BIE.___.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`. 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\unhollowed\base\` folder.
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.
### MelonLoader ### MelonLoader
0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) for your game. 0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) for your game.
1. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above. 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`
## Mod Config ## Mod Config

View File

@ -11,7 +11,7 @@ namespace UnityExplorer.Config
public static ModConfig Instance; public static ModConfig Instance;
internal static readonly IniDataParser _parser = new IniDataParser(); internal static readonly IniDataParser _parser = new IniDataParser();
internal const string INI_PATH = ExplorerCore.EXPLORER_FOLDER + @"\config.ini"; internal static readonly string INI_PATH = Path.Combine(ExplorerCore.EXPLORER_FOLDER, "config.ini");
static ModConfig() static ModConfig()
{ {

View File

@ -16,10 +16,15 @@ namespace UnityExplorer
public class ExplorerCore public class ExplorerCore
{ {
public const string NAME = "UnityExplorer"; public const string NAME = "UnityExplorer";
public const string VERSION = "3.1.0"; public const string VERSION = "3.1.5";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer"; public const string GUID = "com.sinai.unityexplorer";
#if ML
public const string EXPLORER_FOLDER = @"Mods\UnityExplorer"; public const string EXPLORER_FOLDER = @"Mods\UnityExplorer";
#elif BIE
public static string EXPLORER_FOLDER = Path.Combine(BepInEx.Paths.ConfigPath, "UnityExplorer");
#endif
public static ExplorerCore Instance { get; private set; } public static ExplorerCore Instance { get; private set; }

View File

@ -15,6 +15,16 @@ namespace UnityExplorer.Helpers
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod(); private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
private static MethodInfo m_encodeToPNGMethod; 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() private static MethodInfo GetEncodeToPNGMethod()
{ {
if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion) if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
@ -60,7 +70,12 @@ namespace UnityExplorer.Helpers
pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); 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); var _newTex = new Texture2D((int)rect.width, (int)rect.height);
#endif
_newTex.SetPixels(pixels); _newTex.SetPixels(pixels);
return _newTex; return _newTex;

View File

@ -111,8 +111,11 @@ namespace UnityExplorer.Inspectors.GameObjects
internal static void OnCompToggleClicked(int index, bool value) internal static void OnCompToggleClicked(int index, bool value)
{ {
var comp = s_compShortlist[index]; var comp = s_compShortlist[index];
#if CPP
comp.TryCast<Behaviour>().enabled = value;
#else
(comp as Behaviour).enabled = value; (comp as Behaviour).enabled = value;
#endif
} }
internal static void OnCompListObjectClicked(int index) internal static void OnCompListObjectClicked(int index)

View File

@ -272,7 +272,14 @@ namespace UnityExplorer.Inspectors.Reflection
if (File.Exists(path)) if (File.Exists(path))
File.Delete(path); File.Delete(path);
var data = tex.EncodeToPNG(); if (!tex.IsReadable())
tex = Texture2DHelpers.ForceReadTexture(tex);
#if CPP
byte[] data = tex.EncodeToPNG();
#else
byte[] data = tex.EncodeToPNGSafe();
#endif
File.WriteAllBytes(path, data); File.WriteAllBytes(path, data);
} }
}); });

View File

@ -87,12 +87,6 @@ namespace UnityExplorer.UI
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))), new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))),
true); true);
#if BIE
#if CPP
// temporarily disabling this patch in BepInEx il2cpp as it's causing a crash in some games.
return;
#endif
#endif
TryPatch(typeof(EventSystem), TryPatch(typeof(EventSystem),
"current", "current",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_EventSystem_set_current))), new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_EventSystem_set_current))),
@ -164,9 +158,22 @@ namespace UnityExplorer.UI
public static void SetEventSystem() public static void SetEventSystem()
{ {
// temp disabled for new InputSystem
if (InputManager.CurrentType == InputType.InputSystem) if (InputManager.CurrentType == InputType.InputSystem)
return; 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; m_settingEventSystem = true;
EventSystem.current = UIManager.EventSys; EventSystem.current = UIManager.EventSys;
InputManager.ActivateUIModule(); InputManager.ActivateUIModule();
@ -180,6 +187,9 @@ namespace UnityExplorer.UI
if (m_lastEventSystem) if (m_lastEventSystem)
{ {
m_lastEventSystem.enabled = true;
//m_lastEventSystem.gameObject.SetActive(true);
m_settingEventSystem = true; m_settingEventSystem = true;
EventSystem.current = m_lastEventSystem; EventSystem.current = m_lastEventSystem;
m_lastInputModule?.ActivateModule(); m_lastInputModule?.ActivateModule();

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
//using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.UI; using UnityEngine.UI;
@ -51,8 +50,6 @@ namespace UnityExplorer.UI.Modules
private SceneFilter m_sceneFilter; private SceneFilter m_sceneFilter;
private ChildFilter m_childFilter; private ChildFilter m_childFilter;
internal bool m_isStaticClassSearching;
// ui elements // ui elements
private Text m_resultCountText; private Text m_resultCountText;
@ -232,6 +229,7 @@ namespace UnityExplorer.UI.Modules
} }
m_sceneDropdown.transform.Find("Label").GetComponent<Text>().text = "Any"; m_sceneDropdown.transform.Find("Label").GetComponent<Text>().text = "Any";
m_sceneFilter = SceneFilter.Any;
} }
// ~~~~~ UI Callbacks ~~~~~ // ~~~~~ UI Callbacks ~~~~~
@ -248,12 +246,15 @@ namespace UnityExplorer.UI.Modules
UnityObjectSearch(); UnityObjectSearch();
RefreshResultList(); RefreshResultList();
if (m_results.Length > 0)
m_resultCountText.text = $"{m_results.Length} Results";
else
m_resultCountText.text = "No results...";
} }
internal void StaticClassSearch() internal void StaticClassSearch()
{ {
m_isStaticClassSearching = true;
var list = new List<Type>(); var list = new List<Type>();
var nameFilter = ""; var nameFilter = "";
@ -274,16 +275,30 @@ namespace UnityExplorer.UI.Modules
m_results = list.ToArray(); 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() private void SingletonSearch()
{ {
m_isStaticClassSearching = false;
var instances = new List<object>(); var instances = new List<object>();
var nameFilter = ""; var nameFilter = "";
if (!string.IsNullOrEmpty(m_nameInput.text)) if (!string.IsNullOrEmpty(m_nameInput.text))
nameFilter = m_nameInput.text.ToLower(); nameFilter = m_nameInput.text.ToLower();
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{ {
// All non-static classes // All non-static classes
@ -293,31 +308,36 @@ namespace UnityExplorer.UI.Modules
{ {
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter)) if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
continue; continue;
#if CPP
// First look for an "Instance" Property // Only look for Properties in IL2CPP, not for Mono.
if (type.GetProperty("Instance", ReflectionHelpers.CommonFlags) is PropertyInfo pi PropertyInfo pi;
&& pi.CanRead foreach (var name in s_instanceNames)
&& pi.GetGetMethod(true).IsStatic) {
pi = type.GetProperty(name, flags);
if (pi != null)
{ {
var instance = pi.GetValue(null, null); var instance = pi.GetValue(null, null);
if (instance != null) if (instance != null)
instances.Add(instance);
}
else
{ {
// Otherwise, look for a typical Instance backing field. instances.Add(instance);
continue;
}
}
}
#endif
// Look for a typical Instance backing field.
FieldInfo fi; FieldInfo fi;
fi = type.GetField("m_instance", ReflectionHelpers.CommonFlags); foreach (var name in s_instanceNames)
if (fi == null) {
fi = type.GetField("s_instance", ReflectionHelpers.CommonFlags); fi = type.GetField(name, flags);
if (fi == null) if (fi != null)
fi = type.GetField("_instance", ReflectionHelpers.CommonFlags);
if (fi == null)
fi = type.GetField("instance", ReflectionHelpers.CommonFlags);
if (fi != null && fi.IsStatic)
{ {
var instance = fi.GetValue(null); var instance = fi.GetValue(null);
if (instance != null)
{
instances.Add(instance);
continue;
}
} }
} }
} }
@ -330,8 +350,6 @@ namespace UnityExplorer.UI.Modules
internal void UnityObjectSearch() internal void UnityObjectSearch()
{ {
m_isStaticClassSearching = false;
Type searchType = null; Type searchType = null;
switch (m_context) switch (m_context)
{ {
@ -444,11 +462,6 @@ namespace UnityExplorer.UI.Modules
} }
m_results = results.ToArray(); m_results = results.ToArray();
if (m_results.Length > 0)
m_resultCountText.text = $"{m_results.Length} Results";
else
m_resultCountText.text = "No results...";
} }
private void OnResultPageTurn() private void OnResultPageTurn()

View File

@ -45,7 +45,6 @@ namespace UnityExplorer.UI
SceneExplorer.Instance?.OnSceneChange(); SceneExplorer.Instance?.OnSceneChange();
SearchPage.Instance?.OnSceneChange(); SearchPage.Instance?.OnSceneChange();
} }
public static void Update() public static void Update()
{ {
MainMenu.Instance?.Update(); MainMenu.Instance?.Update();
@ -53,12 +52,9 @@ namespace UnityExplorer.UI
if (EventSys) if (EventSys)
{ {
if (EventSystem.current != EventSys) if (EventSystem.current != EventSys)
{
ForceUnlockCursor.SetEventSystem(); ForceUnlockCursor.SetEventSystem();
}
#if CPP #if CPP
// Fix for games which override the InputModule pointer events (eg, VRChat) // Some IL2CPP games behave weird with multiple UI Input Systems, some fixes for them.
var evt = InputManager.InputPointerEvent; var evt = InputManager.InputPointerEvent;
if (evt != null) if (evt != null)
{ {
@ -69,9 +65,7 @@ namespace UnityExplorer.UI
} }
if (PanelDragger.Instance != null) if (PanelDragger.Instance != null)
{
PanelDragger.Instance.Update(); PanelDragger.Instance.Update();
}
for (int i = 0; i < SliderScrollbar.Instances.Count; i++) for (int i = 0; i < SliderScrollbar.Instances.Count; i++)
{ {
@ -94,12 +88,51 @@ namespace UnityExplorer.UI
} }
} }
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() private static void LoadBundle()
{ {
var bundlePath = ExplorerCore.EXPLORER_FOLDER + @"\explorerui.bundle"; AssetBundle bundle = null;
if (File.Exists(bundlePath))
try
{ {
var bundle = AssetBundle.LoadFromFile(bundlePath); 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"); BackupShader = bundle.LoadAsset<Shader>("DefaultUI");
@ -116,12 +149,6 @@ namespace UnityExplorer.UI
ExplorerCore.Log("Loaded UI bundle"); ExplorerCore.Log("Loaded UI bundle");
} }
else
{
ExplorerCore.LogWarning("Could not find the ExplorerUI Bundle! It should exist at '" + bundlePath + "'");
return;
}
}
private static GameObject CreateRootCanvas() private static GameObject CreateRootCanvas()
{ {

View File

@ -293,6 +293,7 @@
<Compile Include="Unstrip\SceneUnstrip.cs" /> <Compile Include="Unstrip\SceneUnstrip.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\UIFactory.cs" /> <Compile Include="UI\UIFactory.cs" />
<EmbeddedResource Include="Resources\*" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="ILRepack.targets" /> <None Include="ILRepack.targets" />

View File

@ -25,6 +25,17 @@ namespace UnityExplorer.Unstrip
return new AssetBundle(ptr); 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 ~~~~~~~~~~~~ // ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
private readonly IntPtr m_bundlePtr = IntPtr.Zero; private readonly IntPtr m_bundlePtr = IntPtr.Zero;