mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-23 00:52:31 +08:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
c8899be3ae | |||
cd5c69c965 | |||
5427312f18 | |||
eb7e80d910 | |||
a54888ae3a | |||
4f0553d293 | |||
9f0f7f9b57 | |||
383c6f19e8 | |||
428fab28f9 | |||
760b2981ad | |||
eee7d6bcc4 | |||
e270f205a1 | |||
084aee617c | |||
d0e508727a | |||
a9a53ba924 | |||
3cd9819790 | |||
5abfa3da67 | |||
e5d2d29a47 | |||
6a47e542e5 | |||
f1b83e7c9e | |||
44c6503ae2 | |||
6970dcbbc7 | |||
ac9c2d5286 | |||
496a5de55e | |||
b062924af7 | |||
5aef8ddc99 | |||
c134c1752e |
@ -17,10 +17,10 @@
|
||||
| [BepInEx](https://github.com/BepInEx/BepInEx) 6.X | ✅ [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.BepInEx6.Mono.zip) |
|
||||
| [BepInEx](https://github.com/BepInEx/BepInEx) 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
|
||||
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1 | ✅ [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) |
|
||||
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.0 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.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) |
|
||||
|
||||
### Known issues
|
||||
* UI layouts broken/unusable after changing resolutions: delete the file `data.ini` in the UnityExplorer folder (same place as where you put the DLL). Better fix being worked on.
|
||||
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
|
||||
* The C# console may unexpectedly produce a GC Mark Overflow crash when calling certain outside methods. Not clear yet what is causing this, but it's being looked into.
|
||||
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further.
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
### MelonLoader
|
||||
|
||||
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1+ for your game. This version can currently be obtained from [here](https://github.com/LavaGang/MelonLoader/actions).
|
||||
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1+ for your game (or use `MelonLoader_Legacy` for `0.3.0`). This version can currently be obtained from [here](https://github.com/LavaGang/MelonLoader/actions).
|
||||
2. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
|
||||
3. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
|
||||
|
||||
@ -95,8 +95,7 @@ Depending on the release you are using, the config file will be found at:
|
||||
|
||||
## Building
|
||||
|
||||
Building the project should be straight-forward, the references are all inside the `lib\` folder.
|
||||
|
||||
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
|
||||
1. Open the `src\UnityExplorer.sln` project in Visual Studio.
|
||||
2. 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. Alternatively, use "Batch Build" and select all releases.
|
||||
3. The DLLs are built to the `Release\` folder in the root of the repository.
|
||||
|
BIN
lib/MelonLoader_Legacy/MelonLoader.dll
Normal file
BIN
lib/MelonLoader_Legacy/MelonLoader.dll
Normal file
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.
Binary file not shown.
Binary file not shown.
@ -7,7 +7,7 @@ using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI;
|
||||
using System.Collections;
|
||||
using HarmonyLib;
|
||||
|
||||
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
@ -26,18 +26,16 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static bool ShouldActuallyUnlock => UIManager.ShowMenu && Unlock;
|
||||
|
||||
private static CursorLockMode m_lastLockMode;
|
||||
private static bool m_lastVisibleState;
|
||||
private static CursorLockMode lastLockMode;
|
||||
private static bool lastVisibleState;
|
||||
|
||||
private static bool m_currentlySettingCursor = false;
|
||||
|
||||
private static Type CursorType
|
||||
=> m_cursorType
|
||||
?? (m_cursorType = ReflectionUtility.GetTypeByName("UnityEngine.Cursor"));
|
||||
private static Type m_cursorType;
|
||||
private static bool currentlySettingCursor = false;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
lastLockMode = Cursor.lockState;
|
||||
lastVisibleState = Cursor.visible;
|
||||
|
||||
SetupPatches();
|
||||
|
||||
UpdateCursorControl();
|
||||
@ -78,7 +76,7 @@ namespace UnityExplorer.Core.Input
|
||||
{
|
||||
try
|
||||
{
|
||||
m_currentlySettingCursor = true;
|
||||
currentlySettingCursor = true;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
@ -90,14 +88,14 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
else
|
||||
{
|
||||
Cursor.lockState = m_lastLockMode;
|
||||
Cursor.visible = m_lastVisibleState;
|
||||
Cursor.lockState = lastLockMode;
|
||||
Cursor.visible = lastVisibleState;
|
||||
|
||||
if (UIManager.EventSys)
|
||||
ReleaseEventSystem();
|
||||
}
|
||||
|
||||
m_currentlySettingCursor = false;
|
||||
currentlySettingCursor = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -107,9 +105,9 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
// Event system overrides
|
||||
|
||||
private static bool m_settingEventSystem;
|
||||
private static EventSystem m_lastEventSystem;
|
||||
private static BaseInputModule m_lastInputModule;
|
||||
private static bool settingEventSystem;
|
||||
private static EventSystem lastEventSystem;
|
||||
private static BaseInputModule lastInputModule;
|
||||
|
||||
public static void SetEventSystem()
|
||||
{
|
||||
@ -118,16 +116,16 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
|
||||
{
|
||||
m_lastEventSystem = EventSystem.current;
|
||||
m_lastEventSystem.enabled = false;
|
||||
lastEventSystem = EventSystem.current;
|
||||
lastEventSystem.enabled = false;
|
||||
}
|
||||
|
||||
// Set to our current system
|
||||
m_settingEventSystem = true;
|
||||
settingEventSystem = true;
|
||||
UIManager.EventSys.enabled = true;
|
||||
EventSystem.current = UIManager.EventSys;
|
||||
InputManager.ActivateUIModule();
|
||||
m_settingEventSystem = false;
|
||||
settingEventSystem = false;
|
||||
}
|
||||
|
||||
public static void ReleaseEventSystem()
|
||||
@ -135,14 +133,14 @@ namespace UnityExplorer.Core.Input
|
||||
if (InputManager.CurrentType == InputType.InputSystem)
|
||||
return;
|
||||
|
||||
if (m_lastEventSystem && m_lastEventSystem.gameObject.activeSelf)
|
||||
if (lastEventSystem && lastEventSystem.gameObject.activeSelf)
|
||||
{
|
||||
m_lastEventSystem.enabled = true;
|
||||
lastEventSystem.enabled = true;
|
||||
|
||||
m_settingEventSystem = true;
|
||||
EventSystem.current = m_lastEventSystem;
|
||||
m_lastInputModule?.ActivateModule();
|
||||
m_settingEventSystem = false;
|
||||
settingEventSystem = true;
|
||||
EventSystem.current = lastEventSystem;
|
||||
lastInputModule?.ActivateModule();
|
||||
settingEventSystem = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,30 +150,30 @@ namespace UnityExplorer.Core.Input
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorType == null)
|
||||
throw new Exception("Could not load Type 'UnityEngine.Cursor'!");
|
||||
|
||||
// Get current cursor state and enable cursor
|
||||
m_lastLockMode = (CursorLockMode?)CursorType.GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? CursorLockMode.None;
|
||||
|
||||
m_lastVisibleState = (bool?)CursorType.GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? false;
|
||||
|
||||
ExplorerCore.Loader.SetupCursorPatches();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Error on CursorUnlocker.Init! {e.GetType()}, {e.Message}");
|
||||
ExplorerCore.Log($"Exception setting up Cursor patches: {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
if (!m_settingEventSystem && value != UIManager.EventSys)
|
||||
if (!UIManager.EventSys)
|
||||
{
|
||||
m_lastEventSystem = value;
|
||||
m_lastInputModule = value?.currentInputModule;
|
||||
if (value)
|
||||
{
|
||||
lastEventSystem = value;
|
||||
lastInputModule = value.currentInputModule;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settingEventSystem && value != UIManager.EventSys)
|
||||
{
|
||||
lastEventSystem = value;
|
||||
lastInputModule = value?.currentInputModule;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
@ -191,9 +189,9 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
if (!currentlySettingCursor)
|
||||
{
|
||||
m_lastLockMode = value;
|
||||
lastLockMode = value;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
value = CursorLockMode.None;
|
||||
@ -202,9 +200,9 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
if (!currentlySettingCursor)
|
||||
{
|
||||
m_lastVisibleState = value;
|
||||
lastVisibleState = value;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
value = true;
|
||||
|
@ -61,7 +61,7 @@ namespace UnityExplorer.Core.Input
|
||||
// First, just try to use the legacy input, see if its working.
|
||||
// The InputSystem package may be present but not actually activated, so we can find out this way.
|
||||
|
||||
if (LegacyInput.TInput != null || (ReflectionUtility.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
|
||||
if (LegacyInput.TInput != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -80,7 +80,7 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
}
|
||||
|
||||
if (InputSystem.TKeyboard != null || (ReflectionUtility.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
|
||||
if (InputSystem.TKeyboard != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ using UnityExplorer.Core;
|
||||
using CppType = Il2CppSystem.Type;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnhollowerBaseLib.Attributes;
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
@ -66,6 +67,8 @@ namespace UnityExplorer
|
||||
|
||||
private static void BuildDeobfuscationCache()
|
||||
{
|
||||
float start = UnityEngine.Time.realtimeSinceStartup;
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in asm.TryGetTypes())
|
||||
@ -73,22 +76,29 @@ namespace UnityExplorer
|
||||
}
|
||||
|
||||
if (DeobfuscatedTypes.Count > 0)
|
||||
ExplorerCore.Log($"Built IL2CPP deobfuscation cache, initial count: {DeobfuscatedTypes.Count}");
|
||||
{
|
||||
ExplorerCore.Log($"Built deobfuscation cache in {UnityEngine.Time.realtimeSinceStartup - start} seconds, " +
|
||||
$"initial count: {DeobfuscatedTypes.Count} ");
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryCacheDeobfuscatedType(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Thanks to Slaynash for this
|
||||
if (type.CustomAttributes.Any(it => it.AttributeType.Name == "ObfuscatedNameAttribute"))
|
||||
{
|
||||
var cppType = Il2CppType.From(type);
|
||||
if (!type.CustomAttributes.Any())
|
||||
return;
|
||||
|
||||
if (!DeobfuscatedTypes.ContainsKey(cppType.FullName))
|
||||
foreach (var att in type.CustomAttributes)
|
||||
{
|
||||
// Thanks to Slaynash for this
|
||||
|
||||
if (att.AttributeType == typeof(ObfuscatedNameAttribute))
|
||||
{
|
||||
DeobfuscatedTypes.Add(cppType.FullName, type);
|
||||
reverseDeobCache.Add(type.FullName, cppType.FullName);
|
||||
string obfuscatedName = att.ConstructorArguments[0].Value.ToString();
|
||||
|
||||
DeobfuscatedTypes.Add(obfuscatedName, type);
|
||||
reverseDeobCache.Add(type.FullName, obfuscatedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,6 +268,18 @@ namespace UnityExplorer
|
||||
}
|
||||
}
|
||||
|
||||
//private static bool IsAssignableFrom(Type thisType, Type fromType)
|
||||
//{
|
||||
// if (!Il2CppTypeNotNull(fromType, out IntPtr fromTypePtr)
|
||||
// || !Il2CppTypeNotNull(thisType, out IntPtr thisTypePtr))
|
||||
// {
|
||||
// // one or both of the types are not Il2Cpp types, use normal check
|
||||
// return thisType.IsAssignableFrom(fromType);
|
||||
// }
|
||||
//
|
||||
// return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr);
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@ -438,42 +460,27 @@ namespace UnityExplorer
|
||||
|
||||
#region Force-loading game modules
|
||||
|
||||
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||
|
||||
internal override bool Internal_LoadModule(string moduleName)
|
||||
{
|
||||
if (!moduleName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
|
||||
moduleName += ".dll";
|
||||
internal static string UnhollowedFolderPath => Path.GetFullPath(
|
||||
#if ML
|
||||
var path = Path.Combine("MelonLoader", "Managed", $"{moduleName}");
|
||||
Path.Combine("MelonLoader", "Managed")
|
||||
#elif BIE
|
||||
Path.Combine("BepInEx", "unhollowed")
|
||||
#else
|
||||
var path = Path.Combine("BepInEx", "unhollowed", $"{moduleName}");
|
||||
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules")
|
||||
#endif
|
||||
return DoLoadModule(path);
|
||||
}
|
||||
);
|
||||
|
||||
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||
|
||||
// Force loading all il2cpp modules
|
||||
|
||||
internal void TryLoadGameModules()
|
||||
{
|
||||
string dirpath =
|
||||
#if ML
|
||||
Path.Combine("MelonLoader", "Managed");
|
||||
#elif BIE
|
||||
Path.Combine("BepInEx", "unhollowed");
|
||||
#else
|
||||
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules");
|
||||
#endif
|
||||
;
|
||||
|
||||
if (Directory.Exists(dirpath))
|
||||
if (Directory.Exists(UnhollowedFolderPath))
|
||||
{
|
||||
var files = Directory.GetFiles(dirpath);
|
||||
var files = Directory.GetFiles(UnhollowedFolderPath);
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
var name = Path.GetFileName(filePath);
|
||||
if (!name.StartsWith("Unity") && !name.StartsWith("Assembly-CSharp"))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
DoLoadModule(filePath, true);
|
||||
@ -484,6 +491,8 @@ namespace UnityExplorer
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{UnhollowedFolderPath}'");
|
||||
}
|
||||
|
||||
internal bool DoLoadModule(string fullPath, bool suppressWarning = false)
|
||||
@ -493,7 +502,8 @@ namespace UnityExplorer
|
||||
|
||||
try
|
||||
{
|
||||
Assembly.Load(File.ReadAllBytes(fullPath));
|
||||
Assembly.LoadFile(fullPath);
|
||||
//Assembly.Load(File.ReadAllBytes(fullPath));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -508,7 +518,7 @@ namespace UnityExplorer
|
||||
#endregion
|
||||
|
||||
|
||||
#region Il2cpp reflection blacklist
|
||||
#region Il2cpp reflection blacklist
|
||||
|
||||
public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist);
|
||||
|
||||
@ -636,6 +646,9 @@ namespace UnityExplorer
|
||||
"UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke",
|
||||
"UnityEngine.Scripting.GarbageCollector.CollectIncremental",
|
||||
"UnityEngine.SpherecastCommand.ScheduleBatch",
|
||||
"UnityEngine.Texture.GetPixelDataSize",
|
||||
"UnityEngine.Texture.GetPixelDataOffset",
|
||||
"UnityEngine.Texture.GetPixelDataOffset",
|
||||
"UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke",
|
||||
"UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke",
|
||||
"UnityEngine.Texture2D.SetPixelDataImpl",
|
||||
@ -654,10 +667,54 @@ namespace UnityExplorer
|
||||
"UnityEngine.XR.InputDevice.SendHapticImpulse",
|
||||
};
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
|
||||
#region Temp il2cpp list/dictionary fixes
|
||||
#region IL2CPP IEnumerable and IDictionary
|
||||
|
||||
protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type)
|
||||
{
|
||||
// Check for system types (not unhollowed)
|
||||
if (base.Internal_TryGetEntryType(enumerableType, out type))
|
||||
return true;
|
||||
|
||||
// Type is either an IL2CPP enumerable, or its not generic.
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
// Temporary naive solution until IL2CPP interface support improves.
|
||||
// This will work fine for most cases, but there are edge cases which would not work.
|
||||
type = type.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unable to determine entry type
|
||||
type = typeof(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool Internal_TryGetEntryTypes(Type type, out Type keys, out Type values)
|
||||
{
|
||||
if (base.Internal_TryGetEntryTypes(type, out keys, out values))
|
||||
return true;
|
||||
|
||||
// Type is either an IL2CPP dictionary, or its not generic.
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
// Naive solution until IL2CPP interfaces improve.
|
||||
var args = type.GetGenericArguments();
|
||||
if (args.Length == 2)
|
||||
{
|
||||
keys = args[0];
|
||||
values = args[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
keys = typeof(object);
|
||||
values = typeof(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Temp fix until Unhollower interface support improves
|
||||
|
||||
|
@ -151,12 +151,14 @@ namespace UnityExplorer
|
||||
internal virtual string Internal_ProcessTypeInString(string theString, Type type)
|
||||
=> theString;
|
||||
|
||||
// Force loading modules
|
||||
public static bool LoadModule(string moduleName)
|
||||
=> Instance.Internal_LoadModule(moduleName);
|
||||
//// Force loading modules
|
||||
//public static bool LoadModule(string moduleName)
|
||||
// => Instance.Internal_LoadModule(moduleName);
|
||||
//
|
||||
//internal virtual bool Internal_LoadModule(string moduleName)
|
||||
// => false;
|
||||
|
||||
internal virtual bool Internal_LoadModule(string moduleName)
|
||||
=> false;
|
||||
// Singleton finder
|
||||
|
||||
public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
|
||||
=> Instance.Internal_FindSingleton(possibleNames, type, flags, instances);
|
||||
@ -441,7 +443,6 @@ namespace UnityExplorer
|
||||
blacklist = Instance.DefaultReflectionBlacklist;
|
||||
ConfigManager.Reflection_Signature_Blacklist.Value = blacklist;
|
||||
ConfigManager.Handler.SaveConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(blacklist))
|
||||
@ -466,6 +467,7 @@ namespace UnityExplorer
|
||||
return false;
|
||||
|
||||
var sig = $"{member.DeclaringType.FullName}.{member.Name}";
|
||||
|
||||
return currentBlacklist.Contains(sig);
|
||||
}
|
||||
|
||||
@ -495,6 +497,39 @@ namespace UnityExplorer
|
||||
enumerator = (list as IEnumerable).GetEnumerator();
|
||||
return true;
|
||||
}
|
||||
|
||||
// TryGetEntryType
|
||||
|
||||
public static bool TryGetEntryType(Type enumerableType, out Type type)
|
||||
=> Instance.Internal_TryGetEntryType(enumerableType, out type);
|
||||
|
||||
protected virtual bool Internal_TryGetEntryType(Type enumerableType, out Type type)
|
||||
{
|
||||
// Check for arrays
|
||||
if (enumerableType.IsArray)
|
||||
{
|
||||
type = enumerableType.GetElementType();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for implementation of IEnumerable<T>, IList<T> or ICollection<T>
|
||||
foreach (var t in enumerableType.GetInterfaces())
|
||||
{
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
var typeDef = t.GetGenericTypeDefinition();
|
||||
if (typeDef == typeof(IEnumerable<>) || typeDef == typeof(IList<>) || typeDef == typeof(ICollection<>))
|
||||
{
|
||||
type = t.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unable to determine any generic element type, just use object.
|
||||
type = typeof(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
// IsDictionary
|
||||
|
||||
@ -524,5 +559,28 @@ namespace UnityExplorer
|
||||
yield return new DictionaryEntry(enumerator.Key, enumerator.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// TryGetEntryTypes
|
||||
|
||||
public static bool TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values)
|
||||
=> Instance.Internal_TryGetEntryTypes(dictionaryType, out keys, out values);
|
||||
|
||||
protected virtual bool Internal_TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values)
|
||||
{
|
||||
foreach (var t in dictionaryType.GetInterfaces())
|
||||
{
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>))
|
||||
{
|
||||
var args = t.GetGenericArguments();
|
||||
keys = args[0];
|
||||
values = args[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
keys = typeof(object);
|
||||
values = typeof(object);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
public override void Initialize()
|
||||
{
|
||||
ExplorerCore.Context = RuntimeContext.IL2CPP;
|
||||
//Reflection = new Il2CppReflection();
|
||||
TextureUtil = new Il2CppTextureUtil();
|
||||
}
|
||||
|
||||
@ -30,19 +29,12 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
try
|
||||
{
|
||||
//Application.add_logMessageReceived(new Action<string, string, LogType>(ExplorerCore.Instance.OnUnityLog));
|
||||
|
||||
var logType = ReflectionUtility.GetTypeByName("UnityEngine.Application+LogCallback");
|
||||
var castMethod = logType.GetMethod("op_Implicit", new[] { typeof(Action<string, string, LogType>) });
|
||||
var addMethod = typeof(Application).GetMethod("add_logMessageReceived", BF.Static | BF.Public, null, new[] { logType }, null);
|
||||
addMethod.Invoke(null, new[]
|
||||
{
|
||||
castMethod.Invoke(null, new[] { new Action<string, string, LogType>(Application_logMessageReceived) })
|
||||
});
|
||||
Application.add_logMessageReceived(new Action<string, string, LogType>(Application_logMessageReceived));
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!");
|
||||
ExplorerCore.Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,25 +82,7 @@ namespace UnityExplorer.Core
|
||||
}
|
||||
private static GameObject dontDestroyObject;
|
||||
|
||||
public static bool InspectingAssetScene => SelectedScene == AssetScene;
|
||||
|
||||
internal static Scene AssetScene => AssetObject.scene;
|
||||
internal static int AssetHandle => AssetScene.handle;
|
||||
|
||||
internal static GameObject AssetObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!assetObject)
|
||||
{
|
||||
assetObject = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject))
|
||||
.First(it => !it.TryCast<GameObject>().scene.IsValid())
|
||||
.TryCast<GameObject>();
|
||||
}
|
||||
return assetObject;
|
||||
}
|
||||
}
|
||||
private static GameObject assetObject;
|
||||
public static bool InspectingAssetScene => !SelectedScene?.IsValid() ?? false;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
@ -122,7 +104,7 @@ namespace UnityExplorer.Core
|
||||
catch (Exception ex)
|
||||
{
|
||||
gotAllScenesInBuild = false;
|
||||
ExplorerCore.Log($"Unable to generate list of all Scenes in the build: {ex}");
|
||||
ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +113,7 @@ namespace UnityExplorer.Core
|
||||
int curHandle = SelectedScene?.handle ?? -1;
|
||||
// DontDestroyOnLoad always exists, so default to true if our curHandle is that handle.
|
||||
// otherwise we will check while iterating.
|
||||
bool inspectedExists = curHandle == DontDestroyHandle || curHandle == AssetHandle;
|
||||
bool inspectedExists = curHandle == DontDestroyHandle || curHandle == 0;
|
||||
|
||||
// Quick sanity check if the loaded scenes changed
|
||||
bool anyChange = LoadedSceneCount != allLoadedScenes.Count;
|
||||
@ -145,7 +127,7 @@ namespace UnityExplorer.Core
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
{
|
||||
Scene scene = SceneManager.GetSceneAt(i);
|
||||
if (scene == default || scene.handle == -1 || !scene.isLoaded)
|
||||
if (scene == default || !scene.isLoaded)
|
||||
continue;
|
||||
|
||||
// If no changes yet, ensure the previous list contained this handle.
|
||||
@ -161,7 +143,7 @@ namespace UnityExplorer.Core
|
||||
|
||||
// Always add the DontDestroyOnLoad scene and the "none" scene.
|
||||
allLoadedScenes.Add(DontDestroyScene);
|
||||
allLoadedScenes.Add(AssetScene);
|
||||
allLoadedScenes.Add(default);
|
||||
|
||||
// Default to first scene if none selected or previous selection no longer exists.
|
||||
if (!inspectedExists)
|
||||
|
@ -14,8 +14,40 @@ using UnhollowerBaseLib;
|
||||
|
||||
namespace UnityExplorer.Tests
|
||||
{
|
||||
public class TestIndexer : IList<int>
|
||||
{
|
||||
private readonly List<int> list = new List<int>() { 1,2,3,4,5 };
|
||||
|
||||
public int Count => list.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
int IList<int>.this[int index]
|
||||
{
|
||||
get => list[index];
|
||||
set => list[index] = value;
|
||||
}
|
||||
|
||||
public int IndexOf(int item) => list.IndexOf(item);
|
||||
public bool Contains(int item) => list.Contains(item);
|
||||
|
||||
public void Add(int item) => list.Add(item);
|
||||
public void Insert(int index, int item) => list.Insert(index, item);
|
||||
|
||||
public bool Remove(int item) => list.Remove(item);
|
||||
public void RemoveAt(int index) => list.RemoveAt(index);
|
||||
|
||||
public void Clear() => list.Clear();
|
||||
|
||||
public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
|
||||
|
||||
public IEnumerator<int> GetEnumerator() => list.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
|
||||
}
|
||||
|
||||
public static class TestClass
|
||||
{
|
||||
public static readonly TestIndexer AAAAATest = new TestIndexer();
|
||||
|
||||
public static void ATestMethod(string s, float f, Vector3 vector, DateTime date, Quaternion quater, bool b, CameraClearFlags enumvalue)
|
||||
{
|
||||
ExplorerCore.Log($"{s}, {f}, {vector.ToString()}, {date}, {quater.eulerAngles.ToString()}, {b}, {enumvalue}");
|
||||
|
@ -13,7 +13,6 @@ namespace UnityExplorer
|
||||
public static class ToStringUtility
|
||||
{
|
||||
internal static Dictionary<string, MethodInfo> toStringMethods = new Dictionary<string, MethodInfo>();
|
||||
internal static Dictionary<string, MethodInfo> toStringFormattedMethods = new Dictionary<string, MethodInfo>();
|
||||
|
||||
private const string nullString = "<color=grey>null</color>";
|
||||
private const string nullUnknown = nullString + " (?)";
|
||||
@ -132,22 +131,12 @@ namespace UnityExplorer
|
||||
|
||||
var type = value.GetActualType();
|
||||
|
||||
// Find and cache the relevant ToString method for this Type, if haven't already.
|
||||
// Find and cache the ToString method for this Type, if haven't already.
|
||||
|
||||
if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName))
|
||||
{
|
||||
try
|
||||
{
|
||||
var formatMethod = type.GetMethod("ToString", ArgumentUtility.ParseArgs);
|
||||
formatMethod.Invoke(value, new object[] { ParseUtility.NUMBER_FORMAT });
|
||||
toStringFormattedMethods.Add(type.AssemblyQualifiedName, formatMethod);
|
||||
toStringMethods.Add(type.AssemblyQualifiedName, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
|
||||
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
|
||||
}
|
||||
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
|
||||
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
|
||||
}
|
||||
|
||||
// Invoke the ToString method on the object
|
||||
@ -157,10 +146,7 @@ namespace UnityExplorer
|
||||
string toString;
|
||||
try
|
||||
{
|
||||
if (toStringFormattedMethods.TryGetValue(type.AssemblyQualifiedName, out MethodInfo formatMethod))
|
||||
toString = (string)formatMethod.Invoke(value, new object[] { ParseUtility.NUMBER_FORMAT });
|
||||
else
|
||||
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
|
||||
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ namespace UnityExplorer
|
||||
public static class ExplorerCore
|
||||
{
|
||||
public const string NAME = "UnityExplorer";
|
||||
public const string VERSION = "4.0.0";
|
||||
public const string VERSION = "4.0.5";
|
||||
public const string AUTHOR = "Sinai";
|
||||
public const string GUID = "com.sinai.unityexplorer";
|
||||
|
||||
|
@ -2,11 +2,24 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<Target Name="ILRepacker" AfterTargets="Build">
|
||||
<!-- Actual merged assemblies -->
|
||||
<ItemGroup>
|
||||
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
|
||||
<InputAssemblies Include="..\lib\mcs-unity\mcs\bin\Release\mcs.dll" />
|
||||
<InputAssemblies Include="packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- MonoMod for MelonLoader 0.3.0 -->
|
||||
<ItemGroup Condition="'$(IsMelonLoaderLegacy)'=='true'">
|
||||
<InputAssemblies Include="..\lib\HarmonyX\Harmony\bin\Release\net45\Mono.Cecil.dll" />
|
||||
<InputAssemblies Include="..\lib\HarmonyX\Harmony\bin\Release\net45\Mono.Cecil.Mdb.dll" />
|
||||
<InputAssemblies Include="..\lib\HarmonyX\Harmony\bin\Release\net45\Mono.Cecil.Pdb.dll" />
|
||||
<InputAssemblies Include="..\lib\HarmonyX\Harmony\bin\Release\net45\Mono.Cecil.Rocks.dll" />
|
||||
<InputAssemblies Include="..\lib\HarmonyX\Harmony\bin\Release\net45\MonoMod.RuntimeDetour.dll" />
|
||||
<InputAssemblies Include="..\lib\HarmonyX\Harmony\bin\Release\net45\MonoMod.Utils.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Required references for ILRepack -->
|
||||
<ItemGroup>
|
||||
<ReferenceFolders Include="..\lib\" />
|
||||
<ReferenceFolders Include="..\lib\HarmonyX\Harmony\bin\Release\net35\" />
|
||||
@ -15,6 +28,7 @@
|
||||
<ReferenceFolders Include="..\lib\BepInEx.5\" />
|
||||
<ReferenceFolders Include="..\lib\MelonLoader\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ILRepack
|
||||
Parallel="true"
|
||||
Internalize="true"
|
||||
|
@ -9,11 +9,15 @@ using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Loader.ML;
|
||||
#if ML_LEGACY
|
||||
using Harmony;
|
||||
#else
|
||||
using HarmonyLib;
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
|
||||
#endif
|
||||
|
||||
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
||||
[assembly: MelonGame(null, null)]
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
|
||||
[assembly: MelonColor(ConsoleColor.DarkCyan)]
|
||||
|
||||
namespace UnityExplorer
|
||||
@ -67,7 +71,11 @@ namespace UnityExplorer
|
||||
try
|
||||
{
|
||||
var prop = type.GetProperty(property);
|
||||
#if ML_LEGACY
|
||||
this.Harmony.Patch(prop.GetSetMethod(), prefix: prefix);
|
||||
#else
|
||||
HarmonyInstance.Patch(prop.GetSetMethod(), prefix: prefix);
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
#if ML
|
||||
|
||||
#if !ML_LEGACY // ML 0.3.1+ config handler
|
||||
|
||||
using MelonLoader;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -75,4 +78,129 @@ namespace UnityExplorer.Loader.ML
|
||||
}
|
||||
}
|
||||
|
||||
#else // ML 0.3.0 config handler
|
||||
|
||||
using MelonLoader;
|
||||
using MelonLoader.Tomlyn.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Config;
|
||||
|
||||
namespace UnityExplorer.Loader.ML
|
||||
{
|
||||
public class MelonLoaderConfigHandler : ConfigHandler
|
||||
{
|
||||
internal const string CTG_NAME = "UnityExplorer";
|
||||
|
||||
internal MelonPreferences_Category prefCategory;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings");
|
||||
|
||||
try { MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter); } catch { }
|
||||
try { MelonPreferences.Mapper.RegisterMapper(AnchorReader, AnchorWriter); } catch { }
|
||||
}
|
||||
|
||||
public override void LoadConfig()
|
||||
{
|
||||
foreach (var entry in ConfigManager.ConfigElements)
|
||||
{
|
||||
var key = entry.Key;
|
||||
if (prefCategory.GetEntry(key) is MelonPreferences_Entry)
|
||||
{
|
||||
var config = entry.Value;
|
||||
config.BoxedValue = config.GetLoaderConfigValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
||||
{
|
||||
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.IsInternal) as MelonPreferences_Entry<T>;
|
||||
|
||||
entry.OnValueChangedUntyped += () =>
|
||||
{
|
||||
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
|
||||
return;
|
||||
|
||||
config.Value = entry.Value;
|
||||
};
|
||||
}
|
||||
|
||||
public override void SetConfigValue<T>(ConfigElement<T> config, T value)
|
||||
{
|
||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
||||
{
|
||||
entry.Value = value;
|
||||
entry.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public override T GetConfigValue<T>(ConfigElement<T> config)
|
||||
{
|
||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
||||
return entry.Value;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public override void OnAnyConfigChanged()
|
||||
{
|
||||
}
|
||||
|
||||
public override void SaveConfig()
|
||||
{
|
||||
MelonPreferences.Save();
|
||||
}
|
||||
|
||||
// Enum config handlers
|
||||
|
||||
public static KeyCode KeycodeReader(TomlObject value)
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyCode kc = (KeyCode)Enum.Parse(typeof(KeyCode), (value as TomlString).Value);
|
||||
|
||||
if (kc == default)
|
||||
throw new Exception();
|
||||
|
||||
return kc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return KeyCode.F7;
|
||||
}
|
||||
}
|
||||
|
||||
public static TomlObject KeycodeWriter(KeyCode value)
|
||||
{
|
||||
return MelonPreferences.Mapper.ToToml(value.ToString());
|
||||
}
|
||||
|
||||
public static UI.UIManager.VerticalAnchor AnchorReader(TomlObject value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (UI.UIManager.VerticalAnchor)Enum.Parse(typeof(UI.UIManager.VerticalAnchor), (value as TomlString).Value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return UI.UIManager.VerticalAnchor.Top;
|
||||
}
|
||||
}
|
||||
|
||||
public static TomlObject AnchorWriter(UI.UIManager.VerticalAnchor anchor)
|
||||
{
|
||||
return MelonPreferences.Mapper.ToToml(anchor.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -11,6 +11,7 @@ using UnityExplorer.UI.CSConsole;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.UI.Panels;
|
||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityExplorer.UI.CSConsole
|
||||
{
|
||||
@ -328,15 +329,28 @@ namespace UnityExplorer.UI.CSConsole
|
||||
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition));
|
||||
}
|
||||
|
||||
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
|
||||
|
||||
private static PropertyInfo GetSelectionGuardPropInfo()
|
||||
{
|
||||
selectionGuardPropInfo = typeof(EventSystem).GetProperty("m_SelectionGuard");
|
||||
if (selectionGuardPropInfo == null)
|
||||
selectionGuardPropInfo = typeof(EventSystem).GetProperty("m_selectionGuard");
|
||||
return selectionGuardPropInfo;
|
||||
}
|
||||
|
||||
private static PropertyInfo selectionGuardPropInfo;
|
||||
|
||||
private static IEnumerator SetAutocompleteCaretCoro(int caretPosition)
|
||||
{
|
||||
var color = Input.Component.selectionColor;
|
||||
color.a = 0f;
|
||||
Input.Component.selectionColor = color;
|
||||
EventSystem.current.SetSelectedGameObject(null, null);
|
||||
try { EventSystem.current.SetSelectedGameObject(null, null); } catch { }
|
||||
yield return null;
|
||||
|
||||
EventSystem.current.SetSelectedGameObject(Input.UIRoot, null);
|
||||
try { SelectionGuardProperty.SetValue(EventSystem.current, false, null); } catch { }
|
||||
try { EventSystem.current.SetSelectedGameObject(Input.UIRoot, null); } catch { }
|
||||
Input.Component.Select();
|
||||
yield return null;
|
||||
|
||||
|
@ -246,7 +246,6 @@ namespace UnityExplorer.UI.CacheObject
|
||||
var sig = GetSig(member);
|
||||
|
||||
//ExplorerCore.Log($"Trying to cache member {sig}...");
|
||||
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
|
||||
|
||||
CacheMember cached;
|
||||
Type returnType;
|
||||
@ -324,7 +323,8 @@ namespace UnityExplorer.UI.CacheObject
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
|
||||
internal static string GetSig(MemberInfo member)
|
||||
=> $"{member.DeclaringType.Name}.{member.Name}";
|
||||
|
||||
internal static string GetArgumentString(ParameterInfo[] args)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
@ -21,8 +22,8 @@ namespace UnityExplorer.UI.IValues
|
||||
|
||||
public override bool CanWrite => base.CanWrite && RefIDictionary != null && !RefIDictionary.IsReadOnly;
|
||||
|
||||
public Type KeyType;
|
||||
public Type ValueType;
|
||||
public Type KeysType;
|
||||
public Type ValuesType;
|
||||
public IDictionary RefIDictionary;
|
||||
|
||||
public int ItemCount => cachedEntries.Count;
|
||||
@ -75,23 +76,13 @@ namespace UnityExplorer.UI.IValues
|
||||
else
|
||||
{
|
||||
var type = value.GetActualType();
|
||||
if (type.IsGenericType && type.GetGenericArguments().Length == 2)
|
||||
{
|
||||
KeyType = type.GetGenericArguments()[0];
|
||||
ValueType = type.GetGenericArguments()[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyType = typeof(object);
|
||||
ValueType = typeof(object);
|
||||
}
|
||||
ReflectionUtility.TryGetEntryTypes(type, out KeysType, out ValuesType);
|
||||
|
||||
CacheEntries(value);
|
||||
|
||||
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}";
|
||||
}
|
||||
|
||||
|
||||
this.DictScrollPool.Refresh(true, false);
|
||||
}
|
||||
|
||||
@ -116,7 +107,7 @@ namespace UnityExplorer.UI.IValues
|
||||
else
|
||||
cache = cachedEntries[idx];
|
||||
|
||||
cache.SetFallbackType(ValueType);
|
||||
cache.SetFallbackType(ValuesType);
|
||||
cache.SetKey(dictEnumerator.Current.Key);
|
||||
cache.SetValueFromSource(dictEnumerator.Current.Value);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
@ -19,13 +20,15 @@ namespace UnityExplorer.UI.IValues
|
||||
object ICacheObjectController.Target => this.CurrentOwner.Value;
|
||||
public Type TargetType { get; private set; }
|
||||
|
||||
public override bool CanWrite => base.CanWrite && RefIList != null && !RefIList.IsReadOnly;
|
||||
public override bool CanWrite => base.CanWrite && ((RefIList != null && !RefIList.IsReadOnly) || IsWritableGenericIList);
|
||||
|
||||
public Type EntryType;
|
||||
public IList RefIList;
|
||||
|
||||
public int ItemCount => values.Count;
|
||||
private readonly List<object> values = new List<object>();
|
||||
private bool IsWritableGenericIList;
|
||||
private PropertyInfo genericIndexer;
|
||||
|
||||
public int ItemCount => cachedEntries.Count;
|
||||
private readonly List<CacheListEntry> cachedEntries = new List<CacheListEntry>();
|
||||
|
||||
public ScrollPool<CacheListEntryCell> ListScrollPool { get; private set; }
|
||||
@ -49,7 +52,6 @@ namespace UnityExplorer.UI.IValues
|
||||
private void ClearAndRelease()
|
||||
{
|
||||
RefIList = null;
|
||||
values.Clear();
|
||||
|
||||
foreach (var entry in cachedEntries)
|
||||
{
|
||||
@ -66,18 +68,13 @@ namespace UnityExplorer.UI.IValues
|
||||
if (value == null)
|
||||
{
|
||||
// should never be null
|
||||
if (values.Any())
|
||||
if (cachedEntries.Any())
|
||||
ClearAndRelease();
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = value.GetActualType();
|
||||
if (type.IsGenericType)
|
||||
EntryType = type.GetGenericArguments()[0];
|
||||
else if (type.HasElementType)
|
||||
EntryType = type.GetElementType();
|
||||
else
|
||||
EntryType = typeof(object);
|
||||
ReflectionUtility.TryGetEntryType(type, out EntryType);
|
||||
|
||||
CacheEntries(value);
|
||||
|
||||
@ -92,7 +89,12 @@ namespace UnityExplorer.UI.IValues
|
||||
{
|
||||
RefIList = value as IList;
|
||||
|
||||
values.Clear();
|
||||
// Check if the type implements IList<T> but not IList (ie. Il2CppArrayBase)
|
||||
if (RefIList == null)
|
||||
CheckGenericIList(value);
|
||||
else
|
||||
IsWritableGenericIList = false;
|
||||
|
||||
int idx = 0;
|
||||
|
||||
if (ReflectionUtility.TryGetEnumerator(value, out IEnumerator enumerator))
|
||||
@ -103,8 +105,6 @@ namespace UnityExplorer.UI.IValues
|
||||
{
|
||||
var entry = enumerator.Current;
|
||||
|
||||
values.Add(entry);
|
||||
|
||||
// If list count increased, create new cache entries
|
||||
CacheListEntry cache;
|
||||
if (idx >= cachedEntries.Count)
|
||||
@ -122,9 +122,9 @@ namespace UnityExplorer.UI.IValues
|
||||
}
|
||||
|
||||
// Remove excess cached entries if list count decreased
|
||||
if (cachedEntries.Count > values.Count)
|
||||
if (cachedEntries.Count > idx)
|
||||
{
|
||||
for (int i = cachedEntries.Count - 1; i >= values.Count; i--)
|
||||
for (int i = cachedEntries.Count - 1; i >= idx; i--)
|
||||
{
|
||||
var cache = cachedEntries[i];
|
||||
if (cache.CellView != null)
|
||||
@ -141,14 +141,61 @@ namespace UnityExplorer.UI.IValues
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckGenericIList(object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = value.GetType();
|
||||
if (type.GetInterfaces().Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||
IsWritableGenericIList = !(bool)type.GetProperty("IsReadOnly").GetValue(value, null);
|
||||
else
|
||||
IsWritableGenericIList = false;
|
||||
|
||||
if (IsWritableGenericIList)
|
||||
{
|
||||
// Find the "this[int index]" property.
|
||||
// It might be a private implementation.
|
||||
foreach (var prop in type.GetProperties(ReflectionUtility.FLAGS))
|
||||
{
|
||||
if ((prop.Name == "Item"
|
||||
|| (prop.Name.StartsWith("System.Collections.Generic.IList<") && prop.Name.EndsWith(">.Item")))
|
||||
&& prop.GetIndexParameters() is ParameterInfo[] parameters
|
||||
&& parameters.Length == 1
|
||||
&& parameters[0].ParameterType == typeof(int))
|
||||
{
|
||||
genericIndexer = prop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (genericIndexer == null)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Failed to find indexer property for IList<T> type '{type.FullName}'!");
|
||||
IsWritableGenericIList = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Exception processing IEnumerable for IList<T> check: {ex.ReflectionExToString()}");
|
||||
IsWritableGenericIList = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Setting the value of an index to the list
|
||||
|
||||
public void TrySetValueToIndex(object value, int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
//value = value.TryCast(this.EntryType);
|
||||
RefIList[index] = value;
|
||||
if (!IsWritableGenericIList)
|
||||
{
|
||||
RefIList[index] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
genericIndexer.SetValue(CurrentOwner.Value, value, new object[] { index });
|
||||
}
|
||||
|
||||
var entry = cachedEntries[index];
|
||||
entry.SetValueFromSource(value);
|
||||
|
@ -226,7 +226,7 @@ namespace UnityExplorer.UI.Inspectors
|
||||
|
||||
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
|
||||
new Color(0.065f, 0.065f, 0.065f));
|
||||
UIFactory.SetLayoutElement(scrollObj, minHeight: 300, flexibleWidth: 9999, flexibleHeight: 1);
|
||||
UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(Content, spacing: 3, padTop: 2, padBottom: 2, padLeft: 2, padRight: 2);
|
||||
|
||||
@ -244,10 +244,7 @@ namespace UnityExplorer.UI.Inspectors
|
||||
{
|
||||
var listHolder = UIFactory.CreateUIObject("ListHolders", UIRoot);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(listHolder, false, true, true, true, 8, 2, 2, 2, 2);
|
||||
UIFactory.SetLayoutElement(listHolder, minHeight: 350, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
//var listRect = listHolder.GetComponent<RectTransform>();
|
||||
//listRect.anchorMin = new Vector2(0, 1);
|
||||
//listRect.anchorMax = new Vector2(1, 1);
|
||||
UIFactory.SetLayoutElement(listHolder, minHeight: 150, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
|
||||
// Left group (Children)
|
||||
|
||||
|
@ -64,13 +64,12 @@ namespace UnityExplorer.UI.Inspectors
|
||||
internal static Camera MainCamera;
|
||||
internal static GraphicRaycaster[] graphicRaycasters;
|
||||
|
||||
|
||||
public void StartInspect(MouseInspectMode mode)
|
||||
{
|
||||
MainCamera = Camera.main;
|
||||
if (!MainCamera)
|
||||
return;
|
||||
|
||||
return;
|
||||
|
||||
PanelDragger.ForceEnd();
|
||||
|
||||
Mode = mode;
|
||||
@ -94,8 +93,14 @@ namespace UnityExplorer.UI.Inspectors
|
||||
public void StopInspect()
|
||||
{
|
||||
Inspecting = false;
|
||||
|
||||
UIManager.NavBarRect.gameObject.SetActive(true);
|
||||
UIManager.PanelHolder.SetActive(true);
|
||||
UIManager.PanelHolder.SetActive(true);
|
||||
|
||||
var drop = UIManager.MouseInspectDropdown;
|
||||
if (drop.transform.Find("Dropdown List") is Transform list)
|
||||
drop.DestroyDropdownList(list.gameObject);
|
||||
|
||||
UIRoot.SetActive(false);
|
||||
|
||||
if (Mode == MouseInspectMode.UI)
|
||||
@ -155,8 +160,8 @@ namespace UnityExplorer.UI.Inspectors
|
||||
mousePos.x = 350;
|
||||
if (mousePos.x > Screen.width - 350)
|
||||
mousePos.x = Screen.width - 350;
|
||||
if (mousePos.y < mainPanelRect.rect.height)
|
||||
mousePos.y += mainPanelRect.rect.height + 10;
|
||||
if (mousePos.y < Rect.rect.height)
|
||||
mousePos.y += Rect.rect.height + 10;
|
||||
else
|
||||
mousePos.y -= 10;
|
||||
|
||||
@ -341,10 +346,10 @@ namespace UnityExplorer.UI.Inspectors
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.anchorMin = Vector2.zero;
|
||||
mainPanelRect.anchorMax = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0.5f, 1);
|
||||
mainPanelRect.sizeDelta = new Vector2(700, 150);
|
||||
Rect.anchorMin = Vector2.zero;
|
||||
Rect.anchorMax = Vector2.zero;
|
||||
Rect.pivot = new Vector2(0.5f, 1);
|
||||
Rect.sizeDelta = new Vector2(700, 150);
|
||||
}
|
||||
|
||||
public override void ConstructPanelContent()
|
||||
|
@ -16,8 +16,11 @@ namespace UnityExplorer.UI
|
||||
{
|
||||
if (inputsPendingUpdate.Any())
|
||||
{
|
||||
foreach (var entry in inputsPendingUpdate)
|
||||
var array = inputsPendingUpdate.ToArray();
|
||||
|
||||
for (int i = array.Length - 1; i >= 0; i--)
|
||||
{
|
||||
var entry = array[i];
|
||||
LayoutRebuilder.MarkLayoutForRebuild(entry.Rect);
|
||||
entry.OnValueChanged?.Invoke(entry.Component.text);
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ namespace UnityExplorer.UI.ObjectExplorer
|
||||
case SceneFilter.DontDestroyOnLoad:
|
||||
return scene == SceneHandler.DontDestroyScene;
|
||||
case SceneFilter.HideAndDontSave:
|
||||
return scene == SceneHandler.AssetScene;
|
||||
return scene == default;
|
||||
case SceneFilter.ActivelyLoaded:
|
||||
return scene != SceneHandler.DontDestroyScene && scene != SceneHandler.AssetScene;
|
||||
return scene != SceneHandler.DontDestroyScene && scene != default;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -58,39 +58,22 @@ namespace UnityExplorer.UI.ObjectExplorer
|
||||
{
|
||||
var results = new List<object>();
|
||||
|
||||
Type searchType;
|
||||
switch (context)
|
||||
Type searchType = null;
|
||||
if (!string.IsNullOrEmpty(customTypeInput))
|
||||
{
|
||||
//case SearchContext.GameObject:
|
||||
// searchType = typeof(GameObject);
|
||||
// break;
|
||||
|
||||
case SearchContext.UnityObject:
|
||||
default:
|
||||
|
||||
if (!string.IsNullOrEmpty(customTypeInput))
|
||||
{
|
||||
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
|
||||
{
|
||||
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
|
||||
{
|
||||
searchType = customType;
|
||||
break;
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
|
||||
}
|
||||
|
||||
searchType = typeof(UnityEngine.Object);
|
||||
break;
|
||||
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
|
||||
{
|
||||
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
|
||||
searchType = customType;
|
||||
else
|
||||
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
|
||||
}
|
||||
|
||||
|
||||
if (searchType == null)
|
||||
return results;
|
||||
searchType = typeof(UnityEngine.Object);
|
||||
|
||||
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
|
||||
|
||||
@ -100,7 +83,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input;
|
||||
|
||||
bool canGetGameObject = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
|
||||
bool shouldFilterGOs = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
|
||||
|
||||
foreach (var obj in allObjects)
|
||||
{
|
||||
@ -108,31 +91,39 @@ namespace UnityExplorer.UI.ObjectExplorer
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
|
||||
continue;
|
||||
|
||||
if (canGetGameObject)
|
||||
var type = obj.GetActualType();
|
||||
if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type))
|
||||
{
|
||||
var go = searchType == typeof(GameObject)
|
||||
? obj.TryCast<GameObject>()
|
||||
: obj.TryCast<Component>().gameObject;
|
||||
GameObject go = type == typeof(GameObject)
|
||||
? obj.TryCast<GameObject>()
|
||||
: obj.TryCast<Component>()?.gameObject;
|
||||
|
||||
if (go)
|
||||
{
|
||||
// scene check
|
||||
if (sceneFilter != SceneFilter.Any)
|
||||
{
|
||||
if (!Filter(go.scene, sceneFilter))
|
||||
continue;
|
||||
}
|
||||
// hide unityexplorer objects
|
||||
if (go.transform.root.name == "ExplorerCanvas")
|
||||
continue;
|
||||
|
||||
if (childFilter != ChildFilter.Any)
|
||||
if (shouldFilterGOs)
|
||||
{
|
||||
if (!go)
|
||||
continue;
|
||||
// scene check
|
||||
if (sceneFilter != SceneFilter.Any)
|
||||
{
|
||||
if (!Filter(go.scene, sceneFilter))
|
||||
continue;
|
||||
}
|
||||
|
||||
// root object check (no parent)
|
||||
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
|
||||
continue;
|
||||
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
|
||||
continue;
|
||||
if (childFilter != ChildFilter.Any)
|
||||
{
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
// root object check (no parent)
|
||||
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
|
||||
continue;
|
||||
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,10 +62,10 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.localPosition = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.4f, 0.175f);
|
||||
mainPanelRect.anchorMax = new Vector2(0.85f, 0.925f);
|
||||
Rect.localPosition = Vector2.zero;
|
||||
Rect.pivot = new Vector2(0f, 1f);
|
||||
Rect.anchorMin = new Vector2(0.4f, 0.175f);
|
||||
Rect.anchorMax = new Vector2(0.85f, 0.925f);
|
||||
}
|
||||
|
||||
public override void ConstructPanelContent()
|
||||
|
@ -26,8 +26,8 @@ namespace UnityExplorer.UI.Panels
|
||||
public GameObject ContentHolder;
|
||||
public RectTransform ContentRect;
|
||||
|
||||
public static float CurrentPanelWidth => Instance.mainPanelRect.rect.width;
|
||||
public static float CurrentPanelHeight => Instance.mainPanelRect.rect.height;
|
||||
public static float CurrentPanelWidth => Instance.Rect.rect.width;
|
||||
public static float CurrentPanelHeight => Instance.Rect.rect.height;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
@ -38,7 +38,7 @@ namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
base.OnFinishResize(panel);
|
||||
|
||||
InspectorManager.PanelWidth = this.mainPanelRect.rect.width;
|
||||
InspectorManager.PanelWidth = this.Rect.rect.width;
|
||||
InspectorManager.OnPanelResized(panel.rect.width);
|
||||
}
|
||||
|
||||
@ -51,10 +51,10 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.localPosition = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.35f, 0.175f);
|
||||
mainPanelRect.anchorMax = new Vector2(0.8f, 0.925f);
|
||||
Rect.localPosition = Vector2.zero;
|
||||
Rect.pivot = new Vector2(0f, 1f);
|
||||
Rect.anchorMin = new Vector2(0.35f, 0.175f);
|
||||
Rect.anchorMax = new Vector2(0.8f, 0.925f);
|
||||
}
|
||||
|
||||
public override void ConstructPanelContent()
|
||||
|
@ -51,7 +51,7 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
if (active && !DoneScrollPoolInit)
|
||||
{
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(this.mainPanelRect);
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
|
||||
logScrollPool.Initialize(this);
|
||||
DoneScrollPoolInit = true;
|
||||
}
|
||||
@ -158,10 +158,10 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.localPosition = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.5f, 0.03f);
|
||||
mainPanelRect.anchorMax = new Vector2(0.9f, 0.2f);
|
||||
Rect.localPosition = Vector2.zero;
|
||||
Rect.pivot = new Vector2(0f, 1f);
|
||||
Rect.anchorMin = new Vector2(0.5f, 0.03f);
|
||||
Rect.anchorMax = new Vector2(0.9f, 0.2f);
|
||||
}
|
||||
|
||||
// UI Construction
|
||||
|
@ -99,10 +99,10 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.localPosition = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.125f, 0.175f);
|
||||
mainPanelRect.anchorMax = new Vector2(0.325f, 0.925f);
|
||||
Rect.localPosition = Vector2.zero;
|
||||
Rect.pivot = new Vector2(0f, 1f);
|
||||
Rect.anchorMin = new Vector2(0.125f, 0.175f);
|
||||
Rect.anchorMax = new Vector2(0.325f, 0.925f);
|
||||
//mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 350);
|
||||
}
|
||||
|
||||
|
@ -69,11 +69,11 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.localPosition = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.5f, 0.1f);
|
||||
mainPanelRect.anchorMax = new Vector2(0.5f, 0.85f);
|
||||
mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 600f);
|
||||
Rect.localPosition = Vector2.zero;
|
||||
Rect.pivot = new Vector2(0f, 1f);
|
||||
Rect.anchorMin = new Vector2(0.5f, 0.1f);
|
||||
Rect.anchorMax = new Vector2(0.5f, 0.85f);
|
||||
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 600f);
|
||||
}
|
||||
|
||||
// UI Construction
|
||||
|
@ -250,15 +250,9 @@ namespace UnityExplorer.UI.Panels
|
||||
Vector2 diff = (Vector2)mousePos - m_lastDragPosition;
|
||||
m_lastDragPosition = mousePos;
|
||||
|
||||
var pos = Panel.localPosition + (Vector3)diff;
|
||||
Panel.localPosition = Panel.localPosition + (Vector3)diff;
|
||||
|
||||
// Prevent panel going oustide screen bounds
|
||||
var halfW = Screen.width * 0.5f;
|
||||
var halfH = Screen.height * 0.5f;
|
||||
pos.x = Math.Max(-halfW, Math.Min(pos.x, halfW - Panel.rect.width));
|
||||
pos.y = Math.Max(-halfH + Panel.rect.height, Math.Min(pos.y, halfH));
|
||||
|
||||
Panel.localPosition = pos;
|
||||
UIPanel.EnsureValidPosition(Panel);
|
||||
}
|
||||
|
||||
public void OnEndDrag()
|
||||
@ -425,6 +419,9 @@ namespace UnityExplorer.UI.Panels
|
||||
if ((Vector2)mousePos == m_lastResizePos)
|
||||
return;
|
||||
|
||||
if (mousePos.x < 0 || mousePos.y < 0 || mousePos.x > Screen.width || mousePos.y > Screen.height)
|
||||
return;
|
||||
|
||||
m_lastResizePos = mousePos;
|
||||
|
||||
float diffX = (float)((decimal)diff.x / Screen.width);
|
||||
|
@ -44,8 +44,8 @@ namespace UnityExplorer.UI.Panels
|
||||
continue;
|
||||
|
||||
// check if our mouse is clicking inside the panel
|
||||
var pos = panel.mainPanelRect.InverseTransformPoint(mousePos);
|
||||
if (!panel.Enabled || !panel.mainPanelRect.rect.Contains(pos))
|
||||
var pos = panel.Rect.InverseTransformPoint(mousePos);
|
||||
if (!panel.Enabled || !panel.Rect.rect.Contains(pos))
|
||||
continue;
|
||||
|
||||
// if this is not the top panel, reorder and invoke the onchanged event
|
||||
@ -88,9 +88,11 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
public override GameObject UIRoot => uiRoot;
|
||||
protected GameObject uiRoot;
|
||||
protected RectTransform mainPanelRect;
|
||||
public RectTransform Rect;
|
||||
public GameObject content;
|
||||
|
||||
public GameObject titleBar;
|
||||
|
||||
public abstract void ConstructPanelContent();
|
||||
|
||||
public virtual void OnFinishResize(RectTransform panel)
|
||||
@ -136,14 +138,85 @@ namespace UnityExplorer.UI.Panels
|
||||
public void SetTransformDefaults()
|
||||
{
|
||||
DoSetDefaultPosAndAnchors();
|
||||
|
||||
if (mainPanelRect.rect.width < MinWidth)
|
||||
mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, MinWidth);
|
||||
if (mainPanelRect.rect.height < MinHeight)
|
||||
mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
|
||||
}
|
||||
|
||||
public GameObject titleBar;
|
||||
public void EnsureValidSize()
|
||||
{
|
||||
if (Rect.rect.width < MinWidth)
|
||||
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, MinWidth);
|
||||
if (Rect.rect.height < MinHeight)
|
||||
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
|
||||
}
|
||||
|
||||
public static void EnsureValidPosition(RectTransform panel)
|
||||
{
|
||||
var pos = panel.localPosition;
|
||||
|
||||
// Prevent panel going oustide screen bounds
|
||||
var halfW = Screen.width * 0.5f;
|
||||
var halfH = Screen.height * 0.5f;
|
||||
pos.x = Math.Max(-halfW, Math.Min(pos.x, halfW - panel.rect.width));
|
||||
pos.y = Math.Max(-halfH + panel.rect.height, Math.Min(pos.y, halfH));
|
||||
|
||||
panel.localPosition = pos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Save Data
|
||||
|
||||
public abstract void DoSaveToConfigElement();
|
||||
|
||||
public void SaveToConfigManager()
|
||||
{
|
||||
if (UIManager.Initializing)
|
||||
return;
|
||||
|
||||
DoSaveToConfigElement();
|
||||
}
|
||||
|
||||
public abstract string GetSaveDataFromConfigManager();
|
||||
|
||||
public bool ApplyingSaveData { get; set; }
|
||||
|
||||
public virtual string ToSaveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
return $"{ShouldSaveActiveState && Enabled}" +
|
||||
$"|{Rect.RectAnchorsToString()}" +
|
||||
$"|{Rect.RectPositionToString()}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Exception generating Panel save data: {ex}");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ApplySaveData(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
return;
|
||||
|
||||
var split = data.Split('|');
|
||||
|
||||
try
|
||||
{
|
||||
Rect.SetAnchorsFromString(split[1]);
|
||||
Rect.SetPositionFromString(split[2]);
|
||||
UIManager.SetPanelActive(this.PanelType, bool.Parse(split[0]));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default.");
|
||||
SetTransformDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// UI Construction
|
||||
|
||||
public void ConstructUI()
|
||||
{
|
||||
@ -168,7 +241,7 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
// create core canvas
|
||||
uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent);
|
||||
mainPanelRect = this.uiRoot.GetComponent<RectTransform>();
|
||||
Rect = this.uiRoot.GetComponent<RectTransform>();
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);
|
||||
|
||||
int id = this.uiRoot.transform.GetInstanceID();
|
||||
@ -177,9 +250,6 @@ namespace UnityExplorer.UI.Panels
|
||||
content = panelContent;
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, false, false, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
|
||||
|
||||
// always apply default pos and anchors (save data may only be partial)
|
||||
SetTransformDefaults();
|
||||
|
||||
// Title bar
|
||||
titleBar = UIFactory.CreateHorizontalGroup(content, "TitleBar", false, true, true, true, 2,
|
||||
new Vector4(2, 2, 2, 2), new Color(0.06f, 0.06f, 0.06f));
|
||||
@ -210,7 +280,7 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
// Panel dragger
|
||||
|
||||
Dragger = new PanelDragger(titleBar.GetComponent<RectTransform>(), mainPanelRect, this);
|
||||
Dragger = new PanelDragger(titleBar.GetComponent<RectTransform>(), Rect, this);
|
||||
Dragger.OnFinishResize += OnFinishResize;
|
||||
Dragger.OnFinishDrag += OnFinishDrag;
|
||||
|
||||
@ -223,6 +293,7 @@ namespace UnityExplorer.UI.Panels
|
||||
UIManager.SetPanelActive(this.PanelType, ShowByDefault);
|
||||
|
||||
ApplyingSaveData = true;
|
||||
SetTransformDefaults();
|
||||
// apply panel save data or revert to default
|
||||
try
|
||||
{
|
||||
@ -234,6 +305,13 @@ namespace UnityExplorer.UI.Panels
|
||||
SetTransformDefaults();
|
||||
}
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
|
||||
|
||||
// ensure initialized position is valid
|
||||
EnsureValidSize();
|
||||
EnsureValidPosition(this.Rect);
|
||||
|
||||
// update dragger and save data
|
||||
Dragger.OnEndResize();
|
||||
|
||||
// simple listener for saving enabled state
|
||||
@ -241,61 +319,11 @@ namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
SaveToConfigManager();
|
||||
};
|
||||
|
||||
ApplyingSaveData = false;
|
||||
}
|
||||
|
||||
public override void ConstructUI(GameObject parent) => ConstructUI();
|
||||
|
||||
// SAVE DATA
|
||||
|
||||
public abstract void DoSaveToConfigElement();
|
||||
|
||||
public void SaveToConfigManager()
|
||||
{
|
||||
if (UIManager.Initializing)
|
||||
return;
|
||||
|
||||
DoSaveToConfigElement();
|
||||
}
|
||||
|
||||
public abstract string GetSaveDataFromConfigManager();
|
||||
|
||||
public bool ApplyingSaveData { get; set; }
|
||||
|
||||
public virtual string ToSaveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
return $"{ShouldSaveActiveState && Enabled}" +
|
||||
$"|{mainPanelRect.RectAnchorsToString()}" +
|
||||
$"|{mainPanelRect.RectPositionToString()}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Exception generating Panel save data: {ex}");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ApplySaveData(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
return;
|
||||
|
||||
var split = data.Split('|');
|
||||
|
||||
try
|
||||
{
|
||||
mainPanelRect.SetAnchorsFromString(split[1]);
|
||||
mainPanelRect.SetPositionFromString(split[2]);
|
||||
UIManager.SetPanelActive(this.PanelType, bool.Parse(split[0]));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default.");
|
||||
SetTransformDefaults();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region WINDOW ANCHORS / POSITION HELPERS
|
||||
|
@ -123,6 +123,9 @@ namespace UnityExplorer.UI
|
||||
|
||||
// Main UI Update loop
|
||||
|
||||
private static int lastScreenWidth;
|
||||
private static int lastScreenHeight;
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
if (!CanvasRoot || Initializing)
|
||||
@ -150,6 +153,19 @@ namespace UnityExplorer.UI
|
||||
PanelDragger.UpdateInstances();
|
||||
InputFieldRef.UpdateInstances();
|
||||
UIBehaviourModel.UpdateInstances();
|
||||
|
||||
if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
|
||||
{
|
||||
lastScreenWidth = Screen.width;
|
||||
lastScreenHeight = Screen.height;
|
||||
|
||||
foreach (var panel in UIPanels)
|
||||
{
|
||||
panel.Value.EnsureValidSize();
|
||||
UIPanel.EnsureValidPosition(panel.Value.Rect);
|
||||
panel.Value.Dragger.OnEndResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization and UI Construction
|
||||
@ -184,6 +200,9 @@ namespace UnityExplorer.UI
|
||||
|
||||
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
||||
|
||||
lastScreenWidth = Screen.width;
|
||||
lastScreenHeight = Screen.height;
|
||||
|
||||
Initializing = false;
|
||||
}
|
||||
|
||||
|
@ -292,9 +292,9 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.42f, 0.4f);
|
||||
mainPanelRect.anchorMax = new Vector2(0.68f, 0.6f);
|
||||
Rect.pivot = new Vector2(0f, 1f);
|
||||
Rect.anchorMin = new Vector2(0.42f, 0.4f);
|
||||
Rect.anchorMax = new Vector2(0.68f, 0.6f);
|
||||
}
|
||||
|
||||
public override void ConstructPanelContent()
|
||||
|
@ -6,13 +6,29 @@ using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public class DataViewInfo
|
||||
public struct DataViewInfo
|
||||
{
|
||||
public int dataIndex;
|
||||
public float height, startPosition;
|
||||
public int normalizedSpread;
|
||||
// static
|
||||
public static DataViewInfo None => s_default;
|
||||
private static DataViewInfo s_default = default;
|
||||
|
||||
public static implicit operator float(DataViewInfo it) => it.height;
|
||||
|
||||
// instance
|
||||
public int dataIndex, normalizedSpread;
|
||||
public float height, startPosition;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = (DataViewInfo)obj;
|
||||
|
||||
return this.dataIndex == other.dataIndex
|
||||
&& this.height == other.height
|
||||
&& this.startPosition == other.startPosition
|
||||
&& this.normalizedSpread == other.normalizedSpread;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
}
|
||||
|
||||
public class DataHeightCache<T> where T : ICell
|
||||
@ -53,14 +69,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
|
||||
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
|
||||
|
||||
/// <summary>Get the data index at the specified position of the total height cache.</summary>
|
||||
public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _);
|
||||
|
||||
/// <summary>Get the data index and DataViewInfo at the specified position of the total height cache.</summary>
|
||||
public int GetFirstDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
|
||||
public int GetFirstDataIndexAtPosition(float desiredHeight)
|
||||
{
|
||||
cache = default;
|
||||
|
||||
if (!heightCache.Any())
|
||||
return 0;
|
||||
|
||||
@ -72,12 +82,11 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (rangeIndex >= rangeCache.Count)
|
||||
{
|
||||
int idx = ScrollPool.DataSource.ItemCount - 1;
|
||||
cache = heightCache[idx];
|
||||
return idx;
|
||||
}
|
||||
|
||||
int dataIndex = rangeCache[rangeIndex];
|
||||
cache = heightCache[dataIndex];
|
||||
var cache = heightCache[dataIndex];
|
||||
|
||||
// if the DataViewInfo is outdated, need to rebuild
|
||||
int expectedMin = GetRangeCeilingOfPosition(cache.startPosition);
|
||||
@ -88,7 +97,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
rangeIndex = GetRangeFloorOfPosition(desiredHeight);
|
||||
dataIndex = rangeCache[rangeIndex];
|
||||
cache = heightCache[dataIndex];
|
||||
//cache = heightCache[dataIndex];
|
||||
}
|
||||
|
||||
return dataIndex;
|
||||
@ -141,8 +150,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (!heightCache.Any())
|
||||
return;
|
||||
|
||||
var val = heightCache[heightCache.Count - 1];
|
||||
totalHeight -= val;
|
||||
totalHeight -= heightCache[heightCache.Count - 1];
|
||||
heightCache.RemoveAt(heightCache.Count - 1);
|
||||
|
||||
int idx = heightCache.Count;
|
||||
@ -214,6 +222,9 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
SetSpread(dataIndex, rangeIndex, spreadDiff);
|
||||
}
|
||||
|
||||
// set the struct back to the array (TODO necessary?)
|
||||
heightCache[dataIndex] = cache;
|
||||
}
|
||||
|
||||
private void SetSpread(int dataIndex, int rangeIndex, int spreadDiff)
|
||||
@ -244,12 +255,12 @@ namespace UnityExplorer.UI.Widgets
|
||||
return;
|
||||
|
||||
DataViewInfo cache;
|
||||
DataViewInfo prev = null;
|
||||
DataViewInfo prev = DataViewInfo.None;
|
||||
for (int i = 0; i <= toIndex && i < heightCache.Count; i++)
|
||||
{
|
||||
cache = heightCache[i];
|
||||
|
||||
if (prev != null)
|
||||
if (prev != DataViewInfo.None)
|
||||
cache.startPosition = prev.startPosition + prev.height;
|
||||
else
|
||||
cache.startPosition = 0;
|
||||
@ -262,19 +273,5 @@ namespace UnityExplorer.UI.Widgets
|
||||
prev = cache;
|
||||
}
|
||||
}
|
||||
|
||||
//private void HardRebuildRanges()
|
||||
//{
|
||||
// var tempList = new List<float>();
|
||||
// for (int i = 0; i < heightCache.Count; i++)
|
||||
// tempList.Add(heightCache[i]);
|
||||
//
|
||||
// heightCache.Clear();
|
||||
// rangeCache.Clear();
|
||||
// totalHeight = 0;
|
||||
//
|
||||
// for (int i = 0; i < tempList.Count; i++)
|
||||
// SetIndex(i, tempList[i]);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (CellPool.Count > 1)
|
||||
{
|
||||
int index = CellPool.Count - 1 - (topPoolIndex % (CellPool.Count - 1));
|
||||
cell.Rect.SetSiblingIndex(index);
|
||||
cell.Rect.SetSiblingIndex(index + 1);
|
||||
|
||||
if (bottomPoolIndex == index - 1)
|
||||
bottomPoolIndex++;
|
||||
|
@ -93,6 +93,24 @@
|
||||
<IsCpp>true</IsCpp>
|
||||
<IsStandalone>true</IsStandalone>
|
||||
</PropertyGroup>
|
||||
<!-- ML 0.3.0 CPP -->
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_MLLegacy_Cpp|AnyCPU'">
|
||||
<OutputPath>..\Release\UnityExplorer.MelonLoader_Legacy.Il2Cpp\</OutputPath>
|
||||
<DefineConstants>CPP,ML,ML_LEGACY</DefineConstants>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<AssemblyName>UnityExplorer.MLLEGACY.IL2CPP</AssemblyName>
|
||||
<IsCpp>true</IsCpp>
|
||||
<IsMelonLoaderLegacy>true</IsMelonLoaderLegacy>
|
||||
</PropertyGroup>
|
||||
<!-- ML 0.3.0 Mono -->
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_MLLegacy_Mono|AnyCPU'">
|
||||
<OutputPath>..\Release\UnityExplorer.MelonLoader_Legacy.Mono\</OutputPath>
|
||||
<DefineConstants>MONO,ML,ML_LEGACY</DefineConstants>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<AssemblyName>UnityExplorer.MLLEGACY.Mono</AssemblyName>
|
||||
<IsCpp>false</IsCpp>
|
||||
<IsMelonLoaderLegacy>true</IsMelonLoaderLegacy>
|
||||
</PropertyGroup>
|
||||
<!-- Global refs, Mono and Il2Cpp -->
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@ -117,6 +135,13 @@
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<!-- MelonLoader Legacy refs -->
|
||||
<ItemGroup Condition="'$(IsMelonLoaderLegacy)'=='true'">
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>..\lib\MelonLoader_Legacy\MelonLoader.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<!-- BepInEx universal refs -->
|
||||
<ItemGroup Condition="'$(IsBepInEx)'=='true'">
|
||||
<Reference Include="0Harmony">
|
||||
|
@ -18,6 +18,8 @@ Global
|
||||
Release_BIE6_Mono|Any CPU = Release_BIE6_Mono|Any CPU
|
||||
Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU
|
||||
Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU
|
||||
Release_MLLegacy_Cpp|Any CPU = Release_MLLegacy_Cpp|Any CPU
|
||||
Release_MLLegacy_Mono|Any CPU = Release_MLLegacy_Mono|Any CPU
|
||||
Release_STANDALONE_Cpp|Any CPU = Release_STANDALONE_Cpp|Any CPU
|
||||
Release_STANDALONE_Mono|Any CPU = Release_STANDALONE_Mono|Any CPU
|
||||
EndGlobalSection
|
||||
@ -32,6 +34,10 @@ Global
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_MLLegacy_Cpp|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -46,6 +52,10 @@ Global
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Cpp|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -60,6 +70,10 @@ Global
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Cpp|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -74,6 +88,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_MLLegacy_Cpp|Any CPU.ActiveCfg = Release_MLLegacy_Cpp|Any CPU
|
||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release_MLLegacy_Cpp|Any CPU
|
||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release_MLLegacy_Mono|Any CPU
|
||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release_MLLegacy_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
|
||||
|
Reference in New Issue
Block a user