Compare commits

...

18 Commits
4.2.0 ... 4.2.1

25 changed files with 201 additions and 93 deletions

Binary file not shown.

View File

@ -121,16 +121,15 @@ namespace UnityExplorer.CSConsole
{
while (input.Length - 1 >= matchEndIdx)
{
matchEndIdx++;
if (IsNewLine(input[matchEndIdx]))
break;
matchEndIdx++;
}
}
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= matchEndIdx || (caretIdx >= input.Length && matchEndIdx >= input.Length - 1)))
if (caretIdx >= match.startIndex && (caretIdx <= (matchEndIdx+1) || (caretIdx >= input.Length && matchEndIdx >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
}
// Append trailing unhighlighted input

View File

@ -12,11 +12,11 @@ namespace UnityExplorer.CacheObject
public CacheConfigEntry(IConfigElement configElement)
{
this.RefConfigElement = configElement;
this.FallbackType = configElement.ElementType;
this.NameLabelText = $"<color=cyan>{configElement.Name}</color>" +
$"\r\n<color=grey><i>{configElement.Description}</i></color>";
this.FallbackType = configElement.ElementType;
this.NameLabelTextRaw = string.Empty;
configElement.OnValueChangedNotify += UpdateValueFromSource;
}

View File

@ -61,6 +61,7 @@ namespace UnityExplorer.CacheObject
var kvpCell = cell as CacheKeyValuePairCell;
kvpCell.NameLabel.text = $"{DictIndex}:";
kvpCell.HiddenNameLabel.Text = "";
kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
if (KeyInputWanted)

View File

@ -28,6 +28,7 @@ namespace UnityExplorer.CacheObject
var listCell = cell as CacheListEntryCell;
listCell.NameLabel.text = $"{ListIndex}:";
listCell.HiddenNameLabel.Text = "";
listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
}

View File

@ -34,6 +34,7 @@ namespace UnityExplorer.CacheObject
this.Owner = inspector;
this.NameLabelText = SignatureHighlighter.Parse(member.DeclaringType, false, member);
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
this.NameLabelTextRaw = NameForFiltering;
}
public override void ReleasePooledObjects()

View File

@ -30,15 +30,14 @@ namespace UnityExplorer.CacheObject
try
{
var methodInfo = MethodInfo;
if (methodInfo.IsGenericMethod)
methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
if (Arguments.Length > 0)
return methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
var ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
object ret;
if (HasArguments)
ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
else
ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
HadException = false;
LastException = null;
return ret;

View File

@ -47,6 +47,7 @@ namespace UnityExplorer.CacheObject
public bool SubContentShowWanted { get; private set; }
public string NameLabelText { get; protected set; }
public string NameLabelTextRaw { get; protected set; }
public string ValueLabelText { get; protected set; }
public abstract bool ShouldAutoEvaluate { get; }
@ -260,6 +261,8 @@ namespace UnityExplorer.CacheObject
public virtual void SetDataToCell(CacheObjectCell cell)
{
cell.NameLabel.text = NameLabelText;
if (cell.HiddenNameLabel != null)
cell.HiddenNameLabel.Text = NameLabelTextRaw;
cell.ValueLabel.gameObject.SetActive(true);
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);

View File

@ -45,6 +45,7 @@ namespace UnityExplorer.CacheObject.Views
public LayoutElement RightGroupLayout;
public Text NameLabel;
public InputFieldRef HiddenNameLabel;
public Text TypeLabel;
public Text ValueLabel;
public Toggle Toggle;
@ -116,8 +117,19 @@ namespace UnityExplorer.CacheObject.Views
NameLabel = UIFactory.CreateLabel(horiRow, "NameLabel", "<notset>", TextAnchor.MiddleLeft);
NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
NameLayout = NameLabel.GetComponent<LayoutElement>();
NameLayout = UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(NameLabel.gameObject, true, true, true, true);
HiddenNameLabel = UIFactory.CreateInputField(NameLabel.gameObject, "HiddenNameLabel", "");
var hiddenRect = HiddenNameLabel.Component.GetComponent<RectTransform>();
hiddenRect.anchorMin = Vector2.zero;
hiddenRect.anchorMax = Vector2.one;
HiddenNameLabel.Component.readOnly = true;
HiddenNameLabel.Component.lineType = UnityEngine.UI.InputField.LineType.MultiLineNewline;
HiddenNameLabel.Component.textComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
HiddenNameLabel.Component.gameObject.GetComponent<Image>().color = Color.clear;
HiddenNameLabel.Component.textComponent.color = Color.clear;
UIFactory.SetLayoutElement(HiddenNameLabel.Component.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
// Right vertical group

View File

@ -134,6 +134,9 @@ namespace UnityExplorer
var type = obj.GetType();
try
{
if (type.IsGenericType)
return type;
if (IsString(obj))
return typeof(string);
@ -178,7 +181,8 @@ namespace UnityExplorer
if (fullname.StartsWith("System."))
fullname = $"Il2Cpp{fullname}";
AllTypes.TryGetValue(fullname, out Type monoType);
if (!AllTypes.TryGetValue(fullname, out Type monoType))
ExplorerCore.LogWarning($"Failed to get type by name '{fullname}'!");
return monoType;
}
@ -477,59 +481,38 @@ namespace UnityExplorer
#region Force-loading game modules
internal static string UnhollowedFolderPath => Path.GetFullPath(
#if ML
Path.Combine("MelonLoader", "Managed")
#elif BIE
Path.Combine(BepInEx.Paths.BepInExRootPath, "unhollowed")
#else
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules")
#endif
);
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
// Force loading all il2cpp modules
internal void TryLoadGameModules()
{
if (Directory.Exists(UnhollowedFolderPath))
var dir = ExplorerCore.Loader.UnhollowedModulesFolder;
if (Directory.Exists(dir))
{
var files = Directory.GetFiles(UnhollowedFolderPath);
foreach (var filePath in files)
{
try
{
DoLoadModule(filePath, true);
}
catch //(Exception ex)
{
//ExplorerCore.LogWarning($"Failed to force-load module '{name}': {ex.ReflectionExToString()}");
}
}
foreach (var filePath in Directory.GetFiles(dir, "*.dll"))
DoLoadModule(filePath);
}
else
ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{UnhollowedFolderPath}'");
ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{dir}'. " +
$"If you are using the standalone release, you can specify the Unhollowed modules path when you call CreateInstance().");
}
internal bool DoLoadModule(string fullPath, bool suppressWarning = false)
internal bool DoLoadModule(string fullPath)
{
if (!File.Exists(fullPath))
if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath))
return false;
try
{
Assembly.LoadFile(fullPath);
//Assembly.Load(File.ReadAllBytes(fullPath));
return true;
}
catch (Exception e)
catch //(Exception e)
{
if (!suppressWarning)
Console.WriteLine($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}");
//ExplorerCore.LogWarning($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}");
return false;
}
return false;
}
#endregion

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
namespace UnityExplorer
{
public static class ReflectionPatches
{
public static void Init()
{
try
{
var method = typeof(Assembly).GetMethod(nameof(Assembly.GetTypes), new Type[0]);
var processor = ExplorerCore.Harmony.CreateProcessor(method);
processor.AddFinalizer(typeof(ReflectionPatches).GetMethod(nameof(Assembly_GetTypes)));
processor.Patch();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up Reflection patch: {ex}");
}
}
private static readonly Type[] emptyTypes = new Type[0];
public static Exception Assembly_GetTypes(Assembly __instance, Exception __exception, ref Type[] __result)
{
if (__exception != null)
{
try
{
__result = __instance.GetExportedTypes();
}
catch (ReflectionTypeLoadException e)
{
try
{
__result = e.Types.Where(it => it != null).ToArray();
}
catch
{
__result = emptyTypes;
}
}
catch
{
__result = emptyTypes;
}
}
return null;
}
}
}

View File

@ -27,6 +27,8 @@ namespace UnityExplorer
new ReflectionUtility();
#endif
Instance.Initialize();
ReflectionPatches.Init();
}
protected virtual void Initialize()

View File

@ -98,15 +98,17 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type);
internal static readonly string[] findObjectsOfTypeAllSignatures = new[]
{
"UnityEngine.Resources::FindObjectsOfTypeAll",
"UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this
};
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
{
var iCall = ICallManager.GetICallUnreliable<d_FindObjectsOfTypeAll>(new[]
{
"UnityEngine.Resources::FindObjectsOfTypeAll",
"UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this
});
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(Il2CppType.From(type).Pointer));
return new Il2CppReferenceArray<UnityEngine.Object>(
ICallManager.GetICallUnreliable<d_FindObjectsOfTypeAll>(findObjectsOfTypeAllSignatures)
.Invoke(Il2CppType.From(type).Pointer));
}
// Scene.GetRootGameObjects();

View File

@ -118,7 +118,7 @@ namespace UnityExplorer.Tests
ExplorerCore.Log($"5: Big list");
for (int i = 0; i < ABigList.Capacity; i++)
ABigList[i] = (short)UnityEngine.Random.Range(0, short.MaxValue);
ABigList.Add((short)UnityEngine.Random.Range(0, short.MaxValue));
ExplorerCore.Log("Finished TestClass Init_Mono");
}

View File

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

View File

@ -8,20 +8,10 @@
<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="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Mdb.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Pdb.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Rocks.dll" />
<InputAssemblies Include="packages\MonoMod.RuntimeDetour.20.1.1.4\lib\net35\MonoMod.RuntimeDetour.dll" />
<InputAssemblies Include="packages\MonoMod.Utils.20.1.1.4\lib\net35\MonoMod.Utils.dll" />
</ItemGroup>
<!-- Required references for ILRepack -->
<ItemGroup>
<ReferenceFolders Include="packages\HarmonyX.2.4.2\lib\net35\" />
<ReferenceFolders Include="packages\HarmonyX.2.5.2\lib\net35\" />
<ReferenceFolders Include="..\lib\BepInEx.6.IL2CPP\" />
<ReferenceFolders Include="..\lib\BepInEx.6.Mono\" />
<ReferenceFolders Include="..\lib\BepInEx.5\" />

View File

@ -47,6 +47,7 @@ namespace UnityExplorer.Inspectors
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
public InputFieldRef HiddenNameText;
public Text NameText;
public Text AssemblyText;
private Toggle autoUpdateToggle;
@ -125,6 +126,7 @@ namespace UnityExplorer.Inspectors
currentBaseTabText = $"{prefix} {SignatureHighlighter.Parse(TargetType, false)}";
Tab.TabText.text = currentBaseTabText;
NameText.text = SignatureHighlighter.Parse(TargetType, true);
HiddenNameText.Text = TargetType.FullName;
string asmText;
if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location))
@ -332,8 +334,27 @@ namespace UnityExplorer.Inspectors
// Class name, assembly
NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 17);
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0);
var titleHolder = UIFactory.CreateUIObject("TitleHolder", UIRoot);
UIFactory.SetLayoutElement(titleHolder, minHeight: 35, flexibleHeight: 0, flexibleWidth: 9999);
NameText = UIFactory.CreateLabel(titleHolder, "VisibleTitle", "NotSet", TextAnchor.MiddleLeft);
var namerect = NameText.GetComponent<RectTransform>();
namerect.anchorMin = new Vector2(0, 0);
namerect.anchorMax = new Vector2(1, 1);
NameText.fontSize = 17;
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 35, flexibleHeight: 0, minWidth: 300, flexibleWidth: 9999);
HiddenNameText = UIFactory.CreateInputField(titleHolder, "Title", "not set");
var hiddenrect = HiddenNameText.Component.gameObject.GetComponent<RectTransform>();
hiddenrect.anchorMin = new Vector2(0, 0);
hiddenrect.anchorMax = new Vector2(1, 1);
HiddenNameText.Component.readOnly = true;
HiddenNameText.Component.lineType = InputField.LineType.MultiLineNewline;
HiddenNameText.Component.gameObject.GetComponent<Image>().color = Color.clear;
HiddenNameText.Component.textComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
HiddenNameText.Component.textComponent.fontSize = 17;
HiddenNameText.Component.textComponent.color = Color.clear;
UIFactory.SetLayoutElement(HiddenNameText.Component.gameObject, minHeight: 35, flexibleHeight: 0, flexibleWidth: 9999);
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);

View File

@ -39,6 +39,8 @@ namespace UnityExplorer
=> Log;
#endif
public string UnhollowedModulesFolder => Path.Combine(Paths.BepInExRootPath, "unhollowed");
public ConfigHandler ConfigHandler => _configHandler;
private BepInExConfigHandler _configHandler;

View File

@ -9,6 +9,7 @@ namespace UnityExplorer
public interface IExplorerLoader
{
string ExplorerFolder { get; }
string UnhollowedModulesFolder { get; }
ConfigHandler ConfigHandler { get; }

View File

@ -24,6 +24,10 @@ namespace UnityExplorer
public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME);
public string UnhollowedModulesFolder => Path.Combine(
Path.GetDirectoryName(MelonHandler.ModsDirectory),
Path.Combine("MelonLoader", "Managed"));
public ConfigHandler ConfigHandler => _configHandler;
public MelonLoaderConfigHandler _configHandler;

View File

@ -34,17 +34,34 @@ namespace UnityExplorer.Loader.ML
}
}
public override void RegisterConfigElement<T>(ConfigElement<T> config)
// This wrapper exists to handle the arbitrary "LemonAction" delegates which ML now uses in 0.4.4+.
// Reflection is required since the delegate type changed between 0.4.3 and 0.4.4.
// A wrapper class is required to link the MelonPreferences_Entry and the delegate instance.
public class EntryDelegateWrapper<T>
{
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.Description, config.IsInternal, false);
entry.OnValueChangedUntyped += () =>
public MelonPreferences_Entry<T> entry;
public ConfigElement<T> config;
public EntryDelegateWrapper(MelonPreferences_Entry<T> entry, ConfigElement<T> config)
{
this.entry = entry;
this.config = config;
var evt = entry.GetType().GetEvent("OnValueChangedUntyped");
evt.AddEventHandler(entry, Delegate.CreateDelegate(evt.EventHandlerType, this, GetType().GetMethod("OnChanged")));
}
public void OnChanged()
{
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
return;
config.Value = entry.Value;
};
}
}
public override void RegisterConfigElement<T>(ConfigElement<T> config)
{
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.Description, config.IsInternal, false);
new EntryDelegateWrapper<T>(entry, config);
}
public override void SetConfigValue<T>(ConfigElement<T> config, T value)

View File

@ -18,26 +18,43 @@ namespace UnityExplorer
public class ExplorerStandalone : IExplorerLoader
{
/// <summary>
/// Call this to initialize UnityExplorer without adding a log listener.
/// Call this to initialize UnityExplorer without adding a log listener or Unhollowed modules path.
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
/// </summary>
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
public static ExplorerStandalone CreateInstance() => CreateInstance(null);
public static ExplorerStandalone CreateInstance() => CreateInstance(null, null);
/// <summary>
/// Call this to initialize UnityExplorer and add a listener for UnityExplorer's log messages.
/// Call this to initialize UnityExplorer and add a listener for UnityExplorer's log messages, without specifying an Unhollowed modules path.
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
/// </summary>
/// <param name="logListener">Your log listener to handle UnityExplorer logs.</param>
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener)
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener) => CreateInstance(logListener, null);
/// <summary>
/// Call this to initialize UnityExplorer with the provided log listener and Unhollowed modules path.
/// </summary>
/// <param name="logListener">Your log listener to handle UnityExplorer logs.</param>
/// <param name="unhollowedModulesPath">The path of the Unhollowed modules, either relative or absolute.</param>
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener, string unhollowedModulesPath)
{
if (Instance != null)
return Instance;
var instance = new ExplorerStandalone();
instance.Init();
instance.CheckExplorerFolder();
if (logListener != null)
OnLog += logListener;
var instance = new ExplorerStandalone();
instance.Init();
if (string.IsNullOrEmpty(unhollowedModulesPath) || !Directory.Exists(unhollowedModulesPath))
instance._unhollowedPath = Path.Combine(instance.ExplorerFolder, "Modules");
else
instance._unhollowedPath = unhollowedModulesPath;
return instance;
}
@ -48,6 +65,9 @@ namespace UnityExplorer
/// </summary>
public static event Action<string, LogType> OnLog;
public string UnhollowedModulesFolder => _unhollowedPath;
private string _unhollowedPath;
public ConfigHandler ConfigHandler => _configHandler;
private StandaloneConfigHandler _configHandler;

View File

@ -56,13 +56,7 @@ namespace UnityExplorer.ObjectExplorer
else if (m_context == SearchContext.Class)
currentResults = SearchProvider.ClassSearch(nameInputField.Text);
else
{
string compType = "";
if (m_context == SearchContext.UnityObject)
compType = this.desiredTypeInput;
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, compType, m_context, m_childFilter, m_sceneFilter);
}
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, desiredTypeInput, m_context, m_childFilter, m_sceneFilter);
dataHandler.RefreshData();
resultsScrollPool.Refresh(true);

View File

@ -40,14 +40,12 @@ namespace UnityExplorer.ObjectExplorer
/// <summary>
/// The names of all scenes in the build settings, if they could be retrieved.
/// </summary>
public static ReadOnlyCollection<string> AllSceneNames => new ReadOnlyCollection<string>(allScenesInBuild);
private static readonly List<string> allScenesInBuild = new List<string>();
public static readonly List<string> AllSceneNames = new List<string>();
/// <summary>
/// Whether or not we successfuly retrieved the names of the scenes in the build settings.
/// </summary>
public static bool WasAbleToGetScenesInBuild => gotAllScenesInBuild;
private static bool gotAllScenesInBuild = true;
public static bool WasAbleToGetScenesInBuild { get; private set; }
/// <summary>
/// Invoked when the currently inspected Scene changes. The argument is the new scene.
@ -97,12 +95,12 @@ namespace UnityExplorer.ObjectExplorer
for (int i = 0; i < sceneCount; i++)
{
var scenePath = (string)method.Invoke(null, new object[] { i });
allScenesInBuild.Add(scenePath);
AllSceneNames.Add(scenePath);
}
}
catch (Exception ex)
{
gotAllScenesInBuild = false;
WasAbleToGetScenesInBuild = false;
ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}");
}
}

View File

@ -218,6 +218,7 @@
<Compile Include="Core\Config\InternalConfigHandler.cs" />
<Compile Include="CacheObject\CacheConfigEntry.cs" />
<Compile Include="CacheObject\Views\CacheConfigCell.cs" />
<Compile Include="Core\Reflection\Patches.cs" />
<Compile Include="CSConsole\CSAutoCompleter.cs" />
<Compile Include="CSConsole\LexerBuilder.cs" />
<Compile Include="CSConsole\Lexers\CommentLexer.cs" />