Compare commits

..

27 Commits
4.0.0 ... 4.0.5

Author SHA1 Message Date
c8899be3ae Bump version 2021-05-26 03:59:50 +10:00
cd5c69c965 Add timer debug to deobfuscation cache 2021-05-26 03:59:45 +10:00
5427312f18 Filter UnityExplorer objects from search results 2021-05-26 03:59:17 +10:00
eb7e80d910 Make sure Mouse Inspect dropdown list gets destroyed after option chosen 2021-05-26 03:59:08 +10:00
a54888ae3a Make DataViewInfo a struct instead of class 2021-05-25 15:46:30 +10:00
4f0553d293 Remove formatting from ToStringUtility 2021-05-23 19:34:32 +10:00
9f0f7f9b57 Prevent some niche exceptions with EventSystem 2021-05-23 18:33:19 +10:00
383c6f19e8 Update README.md 2021-05-23 16:20:09 +10:00
428fab28f9 Cleanup HideAndDontSave detection and support 2021-05-23 16:16:32 +10:00
760b2981ad Update README.md 2021-05-23 13:59:29 +10:00
eee7d6bcc4 Add ML 0.3.0 build 2021-05-23 13:58:26 +10:00
e270f205a1 Fix scroll pool inserting off-by-one 2021-05-20 20:22:45 +10:00
084aee617c Prevent null reference if Canvas isn't created yet 2021-05-19 22:21:08 +10:00
d0e508727a Prevent deobfuscation crash from using Il2CppType.From, and use faster method anyway 2021-05-19 21:26:44 +10:00
a9a53ba924 Force load all Unhollowed DLLs, use Assembly.LoadFile instead of .Load, blacklist some more types 2021-05-19 20:48:34 +10:00
3cd9819790 remove backup libs 2021-05-19 19:47:13 +10:00
5abfa3da67 Better EntryType checking for enumerables and dicts 2021-05-19 19:24:33 +10:00
e5d2d29a47 Update README.md 2021-05-19 19:24:06 +10:00
6a47e542e5 Update Extensions.cs 2021-05-19 18:46:01 +10:00
f1b83e7c9e Cleanup 2021-05-19 18:45:53 +10:00
44c6503ae2 Update unhollowed libs 2021-05-19 18:45:19 +10:00
6970dcbbc7 Merge pull request #68 from sinai-dev/4.0.0-alpha
4.0 hotfixes
2021-05-18 21:20:09 +10:00
ac9c2d5286 Use recursive GetGenericArguments to catch unusual type structures, cleanup InteractiveList value caching 2021-05-18 20:55:18 +10:00
496a5de55e Update README.md 2021-05-18 20:44:42 +10:00
b062924af7 Add support for writing to IList<T>'s which don't implement IList 2021-05-18 20:43:51 +10:00
5aef8ddc99 Fix UIPanels being broken after resolution changes, better checks on size/position 2021-05-18 19:55:27 +10:00
c134c1752e Update README.md 2021-05-18 01:25:57 +10:00
44 changed files with 769 additions and 383 deletions

View File

@ -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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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;

View File

@ -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
{

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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}");

View File

@ -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)
{

View File

@ -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";

View File

@ -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"

View File

@ -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)
{

View File

@ -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

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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()

View File

@ -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);
}

View File

@ -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;
}
}
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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()

View File

@ -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]);
//}
}
}

View File

@ -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++;

View File

@ -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">

View File

@ -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