mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-24 01:12:41 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
fdfaaadd89 | |||
58d60a10d4 | |||
0432c6d56c | |||
8c34aa2be5 | |||
4a1c54fac1 | |||
190467fa5c | |||
44f54d9190 | |||
3b4ea31b50 | |||
ad7b05f721 | |||
852ca8e9eb |
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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.1";
|
public const string VERSION = "3.1.6";
|
||||||
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; }
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -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,11 +158,25 @@ 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;
|
||||||
|
UIManager.EventSys.enabled = true;
|
||||||
InputManager.ActivateUIModule();
|
InputManager.ActivateUIModule();
|
||||||
m_settingEventSystem = false;
|
m_settingEventSystem = false;
|
||||||
}
|
}
|
||||||
@ -180,6 +188,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();
|
||||||
|
@ -229,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 ~~~~~
|
||||||
|
@ -45,10 +45,6 @@ namespace UnityExplorer.UI
|
|||||||
SceneExplorer.Instance?.OnSceneChange();
|
SceneExplorer.Instance?.OnSceneChange();
|
||||||
SearchPage.Instance?.OnSceneChange();
|
SearchPage.Instance?.OnSceneChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CPP
|
|
||||||
internal static float s_timeOfLastClick;
|
|
||||||
#endif
|
|
||||||
public static void Update()
|
public static void Update()
|
||||||
{
|
{
|
||||||
MainMenu.Instance?.Update();
|
MainMenu.Instance?.Update();
|
||||||
@ -56,35 +52,20 @@ 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)
|
||||||
{
|
{
|
||||||
if (Time.realtimeSinceStartup - s_timeOfLastClick > 0.1f)
|
if (!evt.eligibleForClick && evt.selectedObject)
|
||||||
{
|
evt.eligibleForClick = true;
|
||||||
s_timeOfLastClick = Time.realtimeSinceStartup;
|
|
||||||
|
|
||||||
if (!evt.eligibleForClick && evt.selectedObject)
|
|
||||||
evt.eligibleForClick = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (evt.eligibleForClick)
|
|
||||||
evt.eligibleForClick = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
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++)
|
||||||
{
|
{
|
||||||
@ -107,33 +88,66 @@ 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");
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
else
|
catch
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("Could not find the ExplorerUI Bundle! It should exist at '" + bundlePath + "'");
|
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;
|
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()
|
private static GameObject CreateRootCanvas()
|
||||||
@ -152,7 +166,7 @@ namespace UnityExplorer.UI
|
|||||||
canvas.renderMode = RenderMode.ScreenSpaceCamera;
|
canvas.renderMode = RenderMode.ScreenSpaceCamera;
|
||||||
canvas.referencePixelsPerUnit = 100;
|
canvas.referencePixelsPerUnit = 100;
|
||||||
canvas.sortingOrder = 999;
|
canvas.sortingOrder = 999;
|
||||||
canvas.pixelPerfect = false;
|
//canvas.pixelPerfect = false;
|
||||||
|
|
||||||
CanvasScaler scaler = rootObj.AddComponent<CanvasScaler>();
|
CanvasScaler scaler = rootObj.AddComponent<CanvasScaler>();
|
||||||
scaler.referenceResolution = new Vector2(1920, 1080);
|
scaler.referenceResolution = new Vector2(1920, 1080);
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
<RootNamespace>UnityExplorer</RootNamespace>
|
<RootNamespace>UnityExplorer</RootNamespace>
|
||||||
<!-- Set this to the BepInEx Il2Cpp Game folder, without the ending '\' character. -->
|
<!-- Set this to the BepInEx Il2Cpp Game folder, without the ending '\' character. -->
|
||||||
<BIECppGameFolder>D:\source\Unity Projects\Test\_BUILD</BIECppGameFolder>
|
<BIECppGameFolder>E:\source\Unity Projects\Test\_BUILD</BIECppGameFolder>
|
||||||
<!-- Set this to the MelonLoader Il2Cpp Game folder, without the ending '\' character. -->
|
<!-- Set this to the MelonLoader Il2Cpp Game folder, without the ending '\' character. -->
|
||||||
<MLCppGameFolder>D:\source\Unity Projects\Test\_BUILD</MLCppGameFolder>
|
<MLCppGameFolder>E:\source\Unity Projects\Test\_BUILD</MLCppGameFolder>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -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" />
|
||||||
|
@ -24,6 +24,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 ~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user