Compare commits

..

23 Commits
4.3.2 ... 4.3.5

Author SHA1 Message Date
6aa9b3aa15 Bump version 2021-10-26 15:12:56 +11:00
587842f46b Add search filter to scene loader dropdown 2021-10-26 15:12:52 +11:00
b9c641f170 Fix scene loader not showing 2021-10-26 15:12:44 +11:00
1e68529430 Don't patch stripped UnloadAllAssetBundles 2021-10-22 15:54:14 +11:00
e84e96a63c Bump version 2021-10-21 20:04:48 +11:00
ec8c88ab9b Actually fix Logs folder 2021-10-19 16:34:46 +11:00
0eae78eeb2 Update LogPanel.cs 2021-10-19 16:32:33 +11:00
a07ead2142 Fix log folder not being created 2021-10-19 04:30:35 +11:00
b93567c7ea Bump version 2021-10-16 18:11:08 +11:00
957d80c7ec Fix IOUtility creating folders for file paths 2021-10-16 18:10:23 +11:00
d530d10798 Exceptions is IsNullOrDestroyed should return true 2021-10-12 20:27:32 +11:00
66daabf770 Add try/catch to IsNullOrDestroyed 2021-10-12 20:20:08 +11:00
9fe998aa22 Cleanup 2021-10-01 17:21:54 +10:00
28b6db80f9 Fix InputSystem EventSystem bug 2021-10-01 17:21:50 +10:00
22effbb399 Update README.md 2021-09-30 21:13:29 +10:00
50f0c31e98 Add patch for asset bundle unloading to prevent UnityExplorer's bundle being unloaded 2021-09-30 21:09:27 +10:00
911522457e Clarify "Classes" help in C# Console 2021-09-30 21:09:04 +10:00
d5d1841109 Include Unhollower in ILRepack references 2021-09-30 21:08:45 +10:00
239534e09c Add GetAllLoadedAssetBundles 2021-09-30 21:08:29 +10:00
bc5d16051f Fix issue with InputSystem 2021-09-30 21:08:08 +10:00
f81822f219 bump lib versions 2021-09-30 21:06:38 +10:00
f159cf5ea7 Attempt fix at bundle unloading 2021-09-23 16:40:47 +10:00
1e6bacb32b manual unity libs no longer required for BepInEx 2021-09-23 16:21:47 +10:00
36 changed files with 309 additions and 156 deletions

View File

@ -23,7 +23,6 @@
| BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
1. Take the `UnityExplorer.BIE.[version].dll` file and put it in `BepInEx\plugins\`
2. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version, create a folder `BepInEx\unity-libs\`, then extract the Unity libs into this folder.
<i>Note: BepInEx 6 is obtainable via [BepisBuilds](https://builds.bepis.io/projects/bepinex_be)</i>
@ -48,6 +47,24 @@ The standalone release can be used with any injector or loader of your choice, b
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
# Common issues
These are some common fixes to issues which are present in some games, please create an issue in this repository if these fixes don't work.
### Input not working properly
This can be caused by a number of issues, but most commonly by the Event System.
1. Open the UnityExplorer config file. See the "Settings" section below if you're unsure where it is.
2. For the "Disable EventSystem Override" option, set the value to `true`
3. Restart the game.
### UI not appearing or gets destroyed during startup
1. Open the UnityExplorer config file. See the "Settings" section below if you're unsure where it is.
2. For the "Startup Delay" option, set the value to something higher, at least as long as it takes to reach the main menu of the game.
3. Restart the game.
# Features
<p align="center">

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -104,7 +104,6 @@ namespace UnityExplorer.CSConsole
}
}
#region UI Listeners and options
// TODO save
@ -659,7 +658,7 @@ var x = 5;
// You can soft-overwrite a class by compiling it again with the same name. The old class will still technically exist in memory.
// Compiled classes can be accessed from both inside and outside this console.
// Note: in IL2CPP, injecting these classes with ClassInjector may crash the game!
// Note: in IL2CPP, you must declare a Namespace to inject these classes with ClassInjector or it will crash the game.
public class HelloWorld
{

View File

@ -80,7 +80,7 @@ namespace UnityExplorer.CacheObject.IValues
return;
}
var path = IOUtility.EnsureValidDirectory(SaveFilePath.Text);
var path = IOUtility.EnsureValidFilePath(SaveFilePath.Text);
if (File.Exists(path))
File.Delete(path);

View File

@ -113,9 +113,6 @@ namespace UnityExplorer.Core.Input
public static void SetEventSystem()
{
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
{
lastEventSystem = EventSystem.current;
@ -132,14 +129,12 @@ namespace UnityExplorer.Core.Input
public static void ReleaseEventSystem()
{
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (lastEventSystem && lastEventSystem.gameObject.activeSelf)
{
lastEventSystem.enabled = true;
settingEventSystem = true;
UIManager.EventSys.enabled = false;
EventSystem.current = lastEventSystem;
lastInputModule?.ActivateModule();
settingEventSystem = false;

View File

@ -14,7 +14,7 @@ namespace UnityExplorer.Core.Input
bool GetMouseButtonDown(int btn);
bool GetMouseButton(int btn);
BaseInputModule UIModule { get; }
BaseInputModule UIInputModule { get; }
void AddUIInputModule();
void ActivateModule();

View File

@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
namespace UnityExplorer.Core.Input
{
@ -16,39 +17,43 @@ namespace UnityExplorer.Core.Input
{
public static InputType CurrentType { get; private set; }
private static IHandleInput m_inputModule;
private static IHandleInput m_inputHandler;
public static Vector3 MousePosition => m_inputModule.MousePosition;
public static Vector3 MousePosition => m_inputHandler.MousePosition;
public static bool GetKeyDown(KeyCode key)
{
if (key == KeyCode.None)
return false;
return m_inputModule.GetKeyDown(key);
return m_inputHandler.GetKeyDown(key);
}
public static bool GetKey(KeyCode key)
{
if (key == KeyCode.None)
return false;
return m_inputModule.GetKey(key);
return m_inputHandler.GetKey(key);
}
public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);
public static bool GetMouseButtonDown(int btn) => m_inputHandler.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputHandler.GetMouseButton(btn);
public static BaseInputModule UIInput => m_inputModule.UIModule;
public static BaseInputModule UIInput => m_inputHandler.UIInputModule;
public static Vector2 MouseScrollDelta => m_inputModule.MouseScrollDelta;
public static void ActivateUIModule() => m_inputModule.ActivateModule();
public static Vector2 MouseScrollDelta => m_inputHandler.MouseScrollDelta;
public static void AddUIModule()
{
m_inputModule.AddUIInputModule();
m_inputHandler.AddUIInputModule();
ActivateUIModule();
}
public static void ActivateUIModule()
{
UIManager.EventSys.m_CurrentInputModule = UIInput;
m_inputHandler.ActivateModule();
}
public static void Init()
{
InitHandler();
@ -65,7 +70,7 @@ namespace UnityExplorer.Core.Input
{
try
{
m_inputModule = new LegacyInput();
m_inputHandler = new LegacyInput();
CurrentType = InputType.Legacy;
// make sure its working
@ -84,7 +89,7 @@ namespace UnityExplorer.Core.Input
{
try
{
m_inputModule = new InputSystem();
m_inputHandler = new InputSystem();
CurrentType = InputType.InputSystem;
ExplorerCore.Log("Initialized new InputSystem support.");
return;
@ -96,7 +101,7 @@ namespace UnityExplorer.Core.Input
}
ExplorerCore.LogWarning("Could not find any Input Module Type!");
m_inputModule = new NoInput();
m_inputHandler = new NoInput();
CurrentType = InputType.None;
}
}

View File

@ -201,7 +201,7 @@ namespace UnityExplorer.Core.Input
?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
internal Type m_tUIInputModule;
public BaseInputModule UIModule => m_newInputModule;
public BaseInputModule UIInputModule => m_newInputModule;
internal BaseInputModule m_newInputModule;
public void AddUIInputModule()
@ -239,6 +239,9 @@ namespace UnityExplorer.Core.Input
private void CreateAction(object map, string actionName, string[] bindings, string propertyName)
{
var disable = map.GetType().GetMethod("Disable");
disable.Invoke(map, ArgumentUtility.EmptyArgs);
var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction");
var addAction = inputExtensions.GetMethod("AddAction");
var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null })
@ -262,8 +265,16 @@ namespace UnityExplorer.Core.Input
public void ActivateModule()
{
m_newInputModule.ActivateModule();
UI_Enable.Invoke(UI_ActionMap, ArgumentUtility.EmptyArgs);
try
{
m_newInputModule.m_EventSystem = UIManager.EventSys;
m_newInputModule.ActivateModule();
UI_Enable.Invoke(UI_ActionMap, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception enabling InputSystem UI Input Module: " + ex);
}
}
}
}

View File

@ -42,17 +42,25 @@ namespace UnityExplorer.Core.Input
// UI Input module
public BaseInputModule UIModule => m_inputModule;
public BaseInputModule UIInputModule => m_inputModule;
internal StandaloneInputModule m_inputModule;
public void AddUIInputModule()
{
m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>();
m_inputModule.m_EventSystem = UIManager.EventSys;
}
public void ActivateModule()
{
m_inputModule.ActivateModule();
try
{
m_inputModule.ActivateModule();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception enabling StandaloneInputModule: {ex}");
}
}
}
}

View File

@ -16,7 +16,7 @@ namespace UnityExplorer.Core.Input
public bool GetMouseButton(int btn) => false;
public bool GetMouseButtonDown(int btn) => false;
public BaseInputModule UIModule => null;
public BaseInputModule UIInputModule => null;
public void ActivateModule() { }
public void AddUIInputModule() { }
}

View File

@ -4,48 +4,69 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnhollowerBaseLib;
using UnhollowerBaseLib.Attributes;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityExplorer.Core.Runtime.Il2Cpp;
namespace UnityExplorer
{
public class AssetBundle
public class AssetBundle : UnityEngine.Object
{
static AssetBundle()
{
ClassInjector.RegisterTypeInIl2Cpp<AssetBundle>();
}
// ~~~~~~~~~~~~ Static ~~~~~~~~~~~~
internal delegate IntPtr d_LoadFromFile(IntPtr path, uint crc, ulong offset);
[HideFromIl2Cpp]
public static AssetBundle LoadFromFile(string path)
{
var iCall = ICallManager.GetICall<d_LoadFromFile>("UnityEngine.AssetBundle::LoadFromFile_Internal");
var ptr = iCall.Invoke(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL);
var ptr = iCall(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL);
return new AssetBundle(ptr);
}
private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc);
[HideFromIl2Cpp]
public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0)
{
var iCall = ICallManager.GetICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal");
var ptr = iCall(((Il2CppStructArray<byte>) binary).Pointer, crc);
var ptr = iCall(((Il2CppStructArray<byte>)binary).Pointer, crc);
return new AssetBundle(ptr);
}
public delegate IntPtr d_GetAllLoadedAssetBundles_Native();
[HideFromIl2Cpp]
public static AssetBundle[] GetAllLoadedAssetBundles()
{
var iCall = ICallManager.GetICall<d_GetAllLoadedAssetBundles_Native>("UnityEngine.AssetBundle::GetAllLoadedAssetBundles_Native");
var ptr = iCall();
if (ptr == IntPtr.Zero)
return null;
return (AssetBundle[])new Il2CppReferenceArray<AssetBundle>(ptr);
}
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
private readonly IntPtr m_bundlePtr = IntPtr.Zero;
public readonly IntPtr m_bundlePtr = IntPtr.Zero;
public AssetBundle(IntPtr ptr) { m_bundlePtr = ptr; }
public AssetBundle(IntPtr ptr) : base(ptr) { m_bundlePtr = ptr; }
// LoadAllAssets()
internal delegate IntPtr d_LoadAssetWithSubAssets_Internal(IntPtr _this, IntPtr name, IntPtr type);
[HideFromIl2Cpp]
public UnityEngine.Object[] LoadAllAssets()
{
var iCall = ICallManager.GetICall<d_LoadAssetWithSubAssets_Internal>("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal");
var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), Il2CppType.Of<UnityEngine.Object>().Pointer);
var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), UnhollowerRuntimeLib.Il2CppType.Of<UnityEngine.Object>().Pointer);
if (ptr == IntPtr.Zero)
return new UnityEngine.Object[0];
@ -57,10 +78,11 @@ namespace UnityExplorer
internal delegate IntPtr d_LoadAsset_Internal(IntPtr _this, IntPtr name, IntPtr type);
[HideFromIl2Cpp]
public T LoadAsset<T>(string name) where T : UnityEngine.Object
{
var iCall = ICallManager.GetICall<d_LoadAsset_Internal>("UnityEngine.AssetBundle::LoadAsset_Internal");
var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), Il2CppType.Of<T>().Pointer);
var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), UnhollowerRuntimeLib.Il2CppType.Of<T>().Pointer);
if (ptr == IntPtr.Zero)
return null;
@ -72,10 +94,11 @@ namespace UnityExplorer
internal delegate void d_Unload(IntPtr _this, bool unloadAllLoadedObjects);
public void Unload(bool unloadAssets = true)
[HideFromIl2Cpp]
public void Unload(bool unloadAllLoadedObjects)
{
var iCall = ICallManager.GetICall<d_Unload>("UnityEngine.AssetBundle::Unload");
iCall.Invoke(this.m_bundlePtr, unloadAssets);
iCall.Invoke(this.m_bundlePtr, unloadAllLoadedObjects);
}
}
}

View File

@ -11,14 +11,15 @@ namespace UnityExplorer
private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars();
private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars();
public static string EnsureValidDirectory(string path)
public static string EnsureValidFilePath(string fullPathWithFile)
{
path = string.Concat(path.Split(invalidDirectoryCharacters));
// Remove invalid path characters
fullPathWithFile = string.Concat(fullPathWithFile.Split(invalidDirectoryCharacters));
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
// Create directory (does nothing if it exists)
Directory.CreateDirectory(Path.GetDirectoryName(fullPathWithFile));
return path;
return fullPathWithFile;
}
public static string EnsureValidFilename(string filename)

View File

@ -9,7 +9,6 @@ using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object;
// Project-wide namespace for accessibility
namespace UnityExplorer
{
public static class UnityHelpers
@ -32,25 +31,28 @@ namespace UnityExplorer
/// </summary>
public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true)
{
var unityObj = obj as Object;
if (obj == null)
try
{
if (!suppressWarning)
ExplorerCore.LogWarning("The target instance is null!");
if (obj == null)
{
if (!suppressWarning)
ExplorerCore.LogWarning("The target instance is null!");
return true;
}
else if (obj is Object)
{
if (!unityObj)
return true;
}
else if (obj is Object unityObj && !unityObj)
{
if (!suppressWarning)
ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!");
return true;
}
return false;
}
catch
{
return true;
}
return false;
}
/// <summary>

View File

@ -20,7 +20,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "4.3.2";
public const string VERSION = "4.3.5";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";

View File

@ -68,6 +68,8 @@ namespace UnityExplorer.Hooks
// Set current hook cell
public void OnCellBorrowed(HookCell cell) { }
public void SetCell(HookCell cell, int index)
{
if (index >= this.currentHooks.Count)
@ -163,6 +165,36 @@ namespace UnityExplorer.Hooks
Panel.AddHooksScrollPool.Refresh(true, true);
}
// Set eligable method cell
public void OnCellBorrowed(AddHookCell cell) { }
public void SetCell(AddHookCell cell, int index)
{
if (index >= this.filteredEligableMethods.Count)
{
cell.Disable();
return;
}
cell.CurrentDisplayedIndex = index;
var method = this.filteredEligableMethods[index];
cell.MethodNameLabel.text = HighlightMethod(method);
var sig = method.FullDescription();
if (hookedSignatures.Contains(sig))
{
cell.HookButton.Component.gameObject.SetActive(false);
cell.HookedLabel.gameObject.SetActive(true);
}
else
{
cell.HookButton.Component.gameObject.SetActive(true);
cell.HookedLabel.gameObject.SetActive(false);
}
}
// ~~~~~~~~~~~ Hook source editor window ~~~~~~~~~~~
public void OnEditorInputChanged(string value)
@ -191,40 +223,7 @@ namespace UnityExplorer.Hooks
}
}
// OnBorrow methods not needed
public void OnCellBorrowed(HookCell cell) { }
public void OnCellBorrowed(AddHookCell cell) { }
// Set eligable method cell
public void SetCell(AddHookCell cell, int index)
{
if (index >= this.filteredEligableMethods.Count)
{
cell.Disable();
return;
}
cell.CurrentDisplayedIndex = index;
var method = this.filteredEligableMethods[index];
cell.MethodNameLabel.text = HighlightMethod(method);
var sig = method.FullDescription();
if (hookedSignatures.Contains(sig))
{
cell.HookButton.Component.gameObject.SetActive(false);
cell.HookedLabel.gameObject.SetActive(true);
}
else
{
cell.HookButton.Component.gameObject.SetActive(true);
cell.HookedLabel.gameObject.SetActive(false);
}
}
// private static readonly string VOID_HIGHLIGHT = $"<color=#{SignatureHighlighter.keywordBlueHex}>void</color> ";
// ~~~~~~~~~~ Method syntax highlighting
private static readonly Dictionary<string, string> highlightedMethods = new Dictionary<string, string>();

View File

@ -16,6 +16,7 @@
<ReferenceFolders Include="..\lib\BepInEx.6.Mono\" />
<ReferenceFolders Include="..\lib\BepInEx.5\" />
<ReferenceFolders Include="..\lib\MelonLoader\" />
<ReferenceFolders Include="..\lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\bin\Release\net4.7.2\" />
</ItemGroup>
<ILRepack

View File

@ -644,7 +644,7 @@ namespace UnityExplorer.Inspectors
var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 9999, flexibleHeight: 9999);
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 1, flexibleHeight: 1);
textureViewer.SetActive(false);
}
@ -664,6 +664,7 @@ namespace UnityExplorer.Inspectors
textureImage.sprite = sprite;
textureImageLayout.preferredHeight = sprite.rect.height;
// not really working, its always stretched horizontally for some reason.
textureImageLayout.preferredWidth = sprite.rect.width;
}
@ -688,7 +689,7 @@ namespace UnityExplorer.Inspectors
return;
}
path = IOUtility.EnsureValidDirectory(path);
path = IOUtility.EnsureValidFilePath(path);
if (File.Exists(path))
File.Delete(path);
@ -699,7 +700,6 @@ namespace UnityExplorer.Inspectors
tex = TextureUtilProvider.ForceReadTexture(tex);
byte[] data = TextureUtilProvider.Instance.EncodeToPNG(tex);
File.WriteAllBytes(path, data);
if (tex != TextureRef)

View File

@ -34,7 +34,7 @@ namespace UnityExplorer.Loader.ML
}
}
// This wrapper exists to handle the arbitrary "LemonAction" delegates which ML now uses in 0.4.4+.
// This wrapper exists to handle the "LemonAction" delegates which ML now uses in 0.4.4+.
// Reflection is required since the delegate type changed between 0.4.3 and 0.4.4.
// A wrapper class is required to link the MelonPreferences_Entry and the delegate instance.
public class EntryDelegateWrapper<T>

View File

@ -42,6 +42,11 @@ namespace UnityExplorer.ObjectExplorer
private Dropdown sceneDropdown;
private readonly Dictionary<Scene, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<Scene, Dropdown.OptionData>();
// scene loader
private Dropdown allSceneDropdown;
private ButtonRef loadButton;
private ButtonRef loadAdditiveButton;
private IEnumerable<GameObject> GetRootEntries() => SceneHandler.CurrentRootObjects;
public void Update()
@ -155,7 +160,7 @@ namespace UnityExplorer.ObjectExplorer
private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop)
{
var text = allSceneDrop.options[allSceneDrop.value].text;
var text = allSceneDrop.captionText.text;
if (text == DEFAULT_LOAD_TEXT)
return;
@ -250,6 +255,38 @@ namespace UnityExplorer.ObjectExplorer
private const string DEFAULT_LOAD_TEXT = "[Select a scene]";
private void RefreshSceneLoaderOptions(string filter)
{
allSceneDropdown.options.Clear();
allSceneDropdown.options.Add(new Dropdown.OptionData(DEFAULT_LOAD_TEXT));
foreach (var scene in SceneHandler.AllSceneNames)
{
if (string.IsNullOrEmpty(filter) || scene.ContainsIgnoreCase(filter))
allSceneDropdown.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scene)));
}
allSceneDropdown.RefreshShownValue();
if (loadButton != null)
RefreshSceneLoaderButtons();
}
private void RefreshSceneLoaderButtons()
{
var text = allSceneDropdown.captionText.text;
if (text == DEFAULT_LOAD_TEXT)
{
loadButton.Component.interactable = false;
loadAdditiveButton.Component.interactable = false;
}
else
{
loadButton.Component.interactable = true;
loadAdditiveButton.Component.interactable = true;
}
}
private void ConstructSceneLoader()
{
// Scene Loader
@ -259,36 +296,41 @@ namespace UnityExplorer.ObjectExplorer
{
var sceneLoaderObj = UIFactory.CreateVerticalGroup(m_uiRoot, "SceneLoader", true, true, true, true);
UIFactory.SetLayoutElement(sceneLoaderObj, minHeight: 25);
//sceneLoaderObj.SetActive(false);
// Title
var loaderTitle = UIFactory.CreateLabel(sceneLoaderObj, "SceneLoaderLabel", "Scene Loader", TextAnchor.MiddleLeft, Color.white, true, 14);
UIFactory.SetLayoutElement(loaderTitle.gameObject, minHeight: 25, flexibleHeight: 0);
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out Dropdown allSceneDrop, "", 14, null);
// Search filter
var searchFilterObj = UIFactory.CreateInputField(sceneLoaderObj, "SearchFilterInput", "Filter scene names...");
UIFactory.SetLayoutElement(searchFilterObj.UIRoot, minHeight: 25, flexibleHeight: 0);
searchFilterObj.OnValueChanged += RefreshSceneLoaderOptions;
// Dropdown
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out allSceneDropdown, "", 14, null);
UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 25, minWidth: 150, flexibleWidth: 0, flexibleHeight: 0);
allSceneDrop.options.Add(new Dropdown.OptionData(DEFAULT_LOAD_TEXT));
RefreshSceneLoaderOptions(string.Empty);
foreach (var scene in SceneHandler.AllSceneNames)
allSceneDrop.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scene)));
allSceneDrop.value = 1;
allSceneDrop.value = 0;
// Button row
var buttonRow = UIFactory.CreateHorizontalGroup(sceneLoaderObj, "LoadButtons", true, true, true, true, 4);
var loadButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Single)", new Color(0.1f, 0.3f, 0.3f));
loadButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Single)", new Color(0.1f, 0.3f, 0.3f));
UIFactory.SetLayoutElement(loadButton.Component.gameObject, minHeight: 25, minWidth: 150);
loadButton.OnClick += () =>
{
TryLoadScene(LoadSceneMode.Single, allSceneDrop);
TryLoadScene(LoadSceneMode.Single, allSceneDropdown);
};
var loadAdditiveButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Additive)", new Color(0.1f, 0.3f, 0.3f));
loadAdditiveButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Additive)", new Color(0.1f, 0.3f, 0.3f));
UIFactory.SetLayoutElement(loadAdditiveButton.Component.gameObject, minHeight: 25, minWidth: 150);
loadAdditiveButton.OnClick += () =>
{
TryLoadScene(LoadSceneMode.Additive, allSceneDrop);
TryLoadScene(LoadSceneMode.Additive, allSceneDropdown);
};
var disabledColor = new Color(0.24f, 0.24f, 0.24f);
@ -298,19 +340,9 @@ namespace UnityExplorer.ObjectExplorer
loadButton.Component.interactable = false;
loadAdditiveButton.Component.interactable = false;
allSceneDrop.onValueChanged.AddListener((int val) =>
allSceneDropdown.onValueChanged.AddListener((int val) =>
{
var text = allSceneDrop.options[val].text;
if (text == DEFAULT_LOAD_TEXT)
{
loadButton.Component.interactable = false;
loadAdditiveButton.Component.interactable = false;
}
else
{
loadButton.Component.interactable = true;
loadAdditiveButton.Component.interactable = true;
}
RefreshSceneLoaderButtons();
});
}
}

View File

@ -97,6 +97,8 @@ namespace UnityExplorer.ObjectExplorer
var scenePath = (string)method.Invoke(null, new object[] { i });
AllSceneNames.Add(scenePath);
}
WasAbleToGetScenesInBuild = true;
}
catch (Exception ex)
{

View File

@ -61,8 +61,10 @@ namespace UnityExplorer.UI.Panels
private void SetupIO()
{
var fileName = $"UnityExplorer {DateTime.Now:u}.txt";
fileName = IOUtility.EnsureValidFilename(fileName);
var path = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Logs");
path = IOUtility.EnsureValidDirectory(path);
CurrentStreamPath = IOUtility.EnsureValidFilePath(Path.Combine(path, fileName));
// clean old log(s)
var files = Directory.GetFiles(path);
@ -75,11 +77,6 @@ namespace UnityExplorer.UI.Panels
File.Delete(files[i]);
}
var fileName = $"UnityExplorer {DateTime.Now:u}.txt";
fileName = IOUtility.EnsureValidFilename(fileName);
CurrentStreamPath = Path.Combine(path, fileName);
File.WriteAllLines(CurrentStreamPath, Logs.Select(it => it.message).ToArray());
}

View File

@ -1,7 +1,10 @@
using System;
using HarmonyLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
@ -40,18 +43,15 @@ namespace UnityExplorer.UI
public static bool Initializing { get; internal set; } = true;
private static readonly Dictionary<Panels, UIPanel> UIPanels = new Dictionary<Panels, UIPanel>();
public static VerticalAnchor NavbarAnchor = VerticalAnchor.Top;
// References
public static GameObject CanvasRoot { get; private set; }
public static Canvas Canvas { get; private set; }
public static EventSystem EventSys { get; private set; }
internal static GameObject PoolHolder { get; private set; }
internal static GameObject PanelHolder { get; private set; }
private static readonly Dictionary<Panels, UIPanel> UIPanels = new Dictionary<Panels, UIPanel>();
internal static Font ConsoleFont { get; private set; }
internal static Font DefaultFont { get; private set; }
@ -196,15 +196,9 @@ namespace UnityExplorer.UI
// Panels
public static UIPanel GetPanel(Panels panel)
{
return UIPanels[panel];
}
public static UIPanel GetPanel(Panels panel) => UIPanels[panel];
public static T GetPanel<T>(Panels panel) where T : UIPanel
{
return (T)UIPanels[panel];
}
public static T GetPanel<T>(Panels panel) where T : UIPanel => (T)UIPanels[panel];
public static void TogglePanel(Panels panel)
{
@ -325,9 +319,14 @@ namespace UnityExplorer.UI
CanvasRoot.layer = 5;
CanvasRoot.transform.position = new Vector3(0f, 0f, 1f);
CanvasRoot.SetActive(false);
EventSys = CanvasRoot.AddComponent<EventSystem>();
InputManager.AddUIModule();
EventSys.enabled = false;
CanvasRoot.SetActive(true);
Canvas = CanvasRoot.AddComponent<Canvas>();
Canvas.renderMode = RenderMode.ScreenSpaceCamera;
Canvas.referencePixelsPerUnit = 100;
@ -415,9 +414,12 @@ namespace UnityExplorer.UI
// UI AssetBundle
internal static AssetBundle ExplorerBundle;
private static void LoadBundle()
{
AssetBundle bundle;
SetupAssetBundlePatches();
try
{
// Get the Major and Minor of the Unity version
@ -428,42 +430,50 @@ namespace UnityExplorer.UI
// Use appropriate AssetBundle for Unity version
// >= 2017
if (major >= 2017)
bundle = LoadBundle("modern");
ExplorerBundle = LoadBundle("modern");
// 5.6.0 to <2017
else if (major == 5 && minor >= 6)
bundle = LoadBundle("legacy.5.6");
ExplorerBundle = LoadBundle("legacy.5.6");
// < 5.6.0
else
bundle = LoadBundle("legacy");
ExplorerBundle = LoadBundle("legacy");
}
catch
{
ExplorerCore.LogWarning($"Exception parsing Unity version, falling back to old AssetBundle load method...");
bundle = LoadBundle("modern") ?? LoadBundle("legacy.5.6") ?? LoadBundle("legacy");
ExplorerBundle = LoadBundle("modern") ?? LoadBundle("legacy.5.6") ?? LoadBundle("legacy");
}
AssetBundle LoadBundle(string id)
{
ExplorerCore.Log($"Loading {id} bundle for Unity {Application.unityVersion}");
return AssetBundle.LoadFromMemory(ReadFully(typeof(ExplorerCore)
var bundle = AssetBundle.LoadFromMemory(ReadFully(typeof(ExplorerCore)
.Assembly
.GetManifestResourceStream($"UnityExplorer.Resources.{id}.bundle")));
if (bundle)
ExplorerCore.Log($"Loaded {id} bundle for Unity {Application.unityVersion}");
return bundle;
}
if (bundle == null)
if (ExplorerBundle == null)
{
ExplorerCore.LogWarning("Could not load the ExplorerUI Bundle!");
ConsoleFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
DefaultFont = ConsoleFont;
ExplorerCore.LogWarning("Could not load the UnityExplorer UI Bundle!");
DefaultFont = ConsoleFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
return;
}
// Bundle loaded
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
DefaultFont = bundle.LoadAsset<Font>("arial");
BackupShader = bundle.LoadAsset<Shader>("DefaultUI");
ConsoleFont = ExplorerBundle.LoadAsset<Font>("CONSOLA");
ConsoleFont.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(ConsoleFont);
DefaultFont = ExplorerBundle.LoadAsset<Font>("arial");
DefaultFont.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(DefaultFont);
BackupShader = ExplorerBundle.LoadAsset<Shader>("DefaultUI");
BackupShader.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(BackupShader);
// Fix for games which don't ship with 'UI/Default' shader.
if (Graphic.defaultGraphicMaterial.shader?.name != "UI/Default")
{
@ -472,7 +482,6 @@ namespace UnityExplorer.UI
}
else
BackupShader = Graphic.defaultGraphicMaterial.shader;
}
private static byte[] ReadFully(Stream input)
@ -486,5 +495,57 @@ namespace UnityExplorer.UI
return ms.ToArray();
}
}
// AssetBundle patch
private static Type TypeofAssetBundle => ReflectionUtility.GetTypeByName("UnityEngine.AssetBundle");
private static void SetupAssetBundlePatches()
{
try
{
if (TypeofAssetBundle.GetMethod("UnloadAllAssetBundles", AccessTools.all) is MethodInfo unloadAllBundles)
{
#if CPP
// if IL2CPP, ensure method wasn't stripped
if (UnhollowerBaseLib.UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(unloadAllBundles) == null)
return;
#endif
var processor = ExplorerCore.Harmony.CreateProcessor(unloadAllBundles);
var prefix = new HarmonyMethod(typeof(UIManager).GetMethod(nameof(Prefix_UnloadAllAssetBundles), AccessTools.all));
processor.AddPrefix(prefix);
processor.Patch();
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up AssetBundle.UnloadAllAssetBundles patch: {ex}");
}
}
static bool Prefix_UnloadAllAssetBundles(bool unloadAllObjects)
{
try
{
var method = typeof(AssetBundle).GetMethod("GetAllLoadedAssetBundles", AccessTools.all);
if (method == null)
return true;
var bundles = method.Invoke(null, ArgumentUtility.EmptyArgs) as AssetBundle[];
foreach (var obj in bundles)
{
if (obj.m_CachedPtr == ExplorerBundle.m_CachedPtr)
continue;
obj.Unload(unloadAllObjects);
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception unloading AssetBundles: {ex}");
}
return false;
}
}
}