IL2CPP reflection fixes and improvements

This commit is contained in:
Sinai 2021-05-17 21:48:10 +10:00
parent 2fc9657560
commit 7dbf694642
2 changed files with 316 additions and 213 deletions

View File

@ -440,12 +440,6 @@ namespace UnityExplorer
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded. // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
internal void TryLoadGameModules()
{
Internal_LoadModule("Assembly-CSharp");
Internal_LoadModule("Assembly-CSharp-firstpass");
}
internal override bool Internal_LoadModule(string moduleName) internal override bool Internal_LoadModule(string moduleName)
{ {
if (!moduleName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) if (!moduleName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
@ -458,7 +452,41 @@ namespace UnityExplorer
return DoLoadModule(path); return DoLoadModule(path);
} }
internal bool DoLoadModule(string fullPath) // 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))
{
var files = Directory.GetFiles(dirpath);
foreach (var filePath in files)
{
var name = Path.GetFileName(filePath);
if (!name.StartsWith("Unity") && !name.StartsWith("Assembly-CSharp"))
continue;
try
{
DoLoadModule(filePath, true);
}
catch //(Exception ex)
{
//ExplorerCore.LogWarning($"Failed to force-load module '{name}': {ex.ReflectionExToString()}");
}
}
}
}
internal bool DoLoadModule(string fullPath, bool suppressWarning = false)
{ {
if (!File.Exists(fullPath)) if (!File.Exists(fullPath))
return false; return false;
@ -470,7 +498,8 @@ namespace UnityExplorer
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.GetType() + ", " + e.Message); if (!suppressWarning)
Console.WriteLine($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}");
} }
return false; return false;
@ -628,186 +657,233 @@ namespace UnityExplorer
#endregion #endregion
// (Disabled)
#region Temp il2cpp list/dictionary fixes #region Temp il2cpp list/dictionary fixes
//// Temp fix until Unhollower interface support improves // Temp fix until Unhollower interface support improves
//
//internal static IntPtr s_cppEnumerableClassPtr; internal static readonly Dictionary<string, MethodInfo> getEnumeratorMethods = new Dictionary<string, MethodInfo>();
//internal static IntPtr s_cppDictionaryClassPtr; internal static readonly Dictionary<string, EnumeratorInfo> enumeratorInfos = new Dictionary<string, EnumeratorInfo>();
// internal static readonly HashSet<string> notSupportedTypes = new HashSet<string>();
//public override bool Internal_IsEnumerable(Type type)
//{ // IEnumerables
// if (base.Internal_IsEnumerable(type))
// return true; internal static IntPtr cppIEnumerablePointer;
//
// try protected override bool Internal_IsEnumerable(Type type)
// { {
// if (s_cppEnumerableClassPtr == IntPtr.Zero) if (base.Internal_IsEnumerable(type))
// Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr); return true;
//
// if (s_cppEnumerableClassPtr != IntPtr.Zero try
// && Il2CppTypeNotNull(type, out IntPtr assignFromPtr) {
// && il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, assignFromPtr)) if (cppIEnumerablePointer == IntPtr.Zero)
// { Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out cppIEnumerablePointer);
// return true;
// } if (cppIEnumerablePointer != IntPtr.Zero
// } && Il2CppTypeNotNull(type, out IntPtr assignFromPtr)
// catch { } && il2cpp_class_is_assignable_from(cppIEnumerablePointer, assignFromPtr))
// {
// return false; return true;
//} }
// }
//// Lists catch { }
//
//internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>(); return false;
// }
//internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
// internal class EnumeratorInfo
//internal class EnumeratorInfo {
//{ internal MethodInfo moveNext;
// internal MethodInfo moveNext; internal PropertyInfo current;
// internal PropertyInfo current; }
//}
// protected override bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
//internal static IEnumerator EnumerateCppList(object list) {
//{ if (list is IEnumerable)
// if (list == null) return base.Internal_TryGetEnumerator(list, out enumerator);
// yield break;
// try
// var cppEnumerable = list.TryCast<Il2CppSystem.Collections.IEnumerable>(); {
// if (cppEnumerable == null) PrepareCppEnumerator(list, out object cppEnumerator, out EnumeratorInfo info);
// { enumerator = EnumerateCppList(info, cppEnumerator);
// ExplorerCore.LogWarning("Failed to cast an IEnumerable to the interface!"); return true;
// yield break; }
// } catch //(Exception ex)
// {
// // Some ugly reflection to use the il2cpp interface for the instance type //ExplorerCore.LogWarning($"Exception enumerating IEnumerable: {ex.ReflectionExToString()}");
// enumerator = null;
// var type = cppEnumerable.GetActualType(); return false;
// if (!s_getEnumeratorMethods.ContainsKey(type)) }
// s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator")); }
//
// var enumerator = s_getEnumeratorMethods[type].Invoke(cppEnumerable.TryCast(type), null); private static void PrepareCppEnumerator(object list, out object cppEnumerator, out EnumeratorInfo info)
// var enumeratorType = enumerator.GetType(); {
// info = null;
// if (!s_enumeratorInfos.ContainsKey(enumeratorType)) cppEnumerator = null;
// { if (list == null)
// s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo throw new ArgumentNullException("list");
// {
// current = enumeratorType.GetProperty("Current"), // Some ugly reflection to use the il2cpp interface for the instance type
// moveNext = enumeratorType.GetMethod("MoveNext"),
// }); var type = list.GetType();
// } var key = type.AssemblyQualifiedName;
// var info = s_enumeratorInfos[enumeratorType];
// if (!getEnumeratorMethods.ContainsKey(key))
// // Yield and return the actual entries {
// while ((bool)info.moveNext.Invoke(enumerator, null)) getEnumeratorMethods.Add(key, type.GetMethod("GetEnumerator"));
// yield return info.current.GetValue(enumerator);
//} // ensure the enumerator type is supported
// try
//// Dicts todo {
// var test = getEnumeratorMethods[key].Invoke(list, null);
//public override bool Internal_IsDictionary(Type type) test.GetType().GetMethod("MoveNext").Invoke(test, null);
//{ }
// if (base.Internal_IsDictionary(type)) catch
// return true; {
// notSupportedTypes.Add(key);
// try }
// { }
// if (s_cppDictionaryClassPtr == IntPtr.Zero)
// if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr)) if (notSupportedTypes.Contains(key))
// return false; throw new NotSupportedException($"The IEnumerable type '{type.FullName}' does not support MoveNext.");
//
// if (Il2CppTypeNotNull(type, out IntPtr classPtr)) cppEnumerator = getEnumeratorMethods[key].Invoke(list, null);
// { var enumeratorType = cppEnumerator.GetType();
// if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr))
// return true; var enumInfoKey = enumeratorType.AssemblyQualifiedName;
// }
// } if (!enumeratorInfos.ContainsKey(enumInfoKey))
// catch { } {
// enumeratorInfos.Add(enumInfoKey, new EnumeratorInfo
// return false; {
//} current = enumeratorType.GetProperty("Current"),
// moveNext = enumeratorType.GetMethod("MoveNext"),
//internal static IEnumerator<KeyValuePair<object, object>> EnumerateCppDictionary(object dictionary) });
//{ }
// var cppDict = dictionary?.TryCast<Il2CppSystem.Collections.IDictionary>();
// if (cppDict == null) info = enumeratorInfos[enumInfoKey];
// yield break; }
//
//} internal static IEnumerator EnumerateCppList(EnumeratorInfo info, object enumerator)
// {
// // Yield and return the actual entries
// while ((bool)info.moveNext.Invoke(enumerator, null))
////public IDictionary EnumerateDictionary(object value) yield return info.current.GetValue(enumerator);
////{ }
//// var valueType = value.GetActualType();
//// // IDictionary
//// Type typeOfKeys, typeOfValues;
//// if (valueType.IsGenericType && valueType.GetGenericArguments() is ParameterInfo[] args && args.Length == 2) internal static IntPtr cppIDictionaryPointer;
//// {
//// typeOfKeys = args[0]; protected override bool Internal_IsDictionary(Type type)
//// typeOfValues = args[1]; {
//// } if (base.Internal_IsDictionary(type))
//// else return true;
//// typeOfKeys = typeOfValues = typeof(object);
//// try
//// var keyList = new List<object>(); {
//// var valueList = new List<object>(); if (cppIDictionaryPointer == IntPtr.Zero)
//// if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out cppIDictionaryPointer))
//// var hashtable = value.TryCast<Il2CppSystem.Collections.Hashtable>(); return false;
////
//// if (hashtable != null) if (Il2CppTypeNotNull(type, out IntPtr classPtr)
//// { && il2cpp_class_is_assignable_from(cppIDictionaryPointer, classPtr))
//// EnumerateCppHashtable(hashtable, keyList, valueList); return true;
//// } }
//// else catch { }
//// {
//// var keys = valueType.GetProperty("Keys").GetValue(value, null); return false;
//// EnumerateCppCollection(keys, keyList); }
////
//// var values = valueType.GetProperty("Values").GetValue(value, null); protected override bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
//// EnumerateCppCollection(values, valueList); {
//// } if (dictionary is IDictionary)
//// return base.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
//// var dict = Activator.CreateInstance(typeof(Dictionary<,>)
//// .MakeGenericType(typeOfKeys, typeOfValues)) try
//// as IDictionary; {
//// var type = dictionary.GetType();
//// for (int i = 0; i < keyList.Count; i++)
//// dict.Add(keyList[i], valueList[i]); if (typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(type))
//// {
//// return dict; dictEnumerator = EnumerateCppHashTable(dictionary.TryCast<Il2CppSystem.Collections.Hashtable>());
////} return true;
//// }
////private void EnumerateCppCollection(object collection, List<object> list)
////{ var keys = type.GetProperty("Keys").GetValue(dictionary, null);
//// // invoke GetEnumerator
//// var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null); var keyCollType = keys.GetType();
//// // get the type of it var cacheKey = keys.GetType().AssemblyQualifiedName;
//// var enumeratorType = enumerator.GetType(); if (!getEnumeratorMethods.ContainsKey(cacheKey))
//// // reflect MoveNext and Current {
//// var moveNext = enumeratorType.GetMethod("MoveNext"); getEnumeratorMethods.Add(cacheKey, keyCollType.GetMethod("GetEnumerator"));
//// var current = enumeratorType.GetProperty("Current");
//// // iterate // test support
//// while ((bool)moveNext.Invoke(enumerator, null)) try
//// { {
//// list.Add(current.GetValue(enumerator, null)); var test = getEnumeratorMethods[cacheKey].Invoke(keys, null);
//// } test.GetType().GetMethod("MoveNext").Invoke(test, null);
////} }
//// catch
////private void EnumerateCppHashtable(Il2CppSystem.Collections.Hashtable hashtable, List<object> keys, List<object> values) {
////{ notSupportedTypes.Add(cacheKey);
//// for (int i = 0; i < hashtable.buckets.Count; i++) }
//// { }
//// var bucket = hashtable.buckets[i];
//// if (bucket == null || bucket.key == null) if (notSupportedTypes.Contains(cacheKey))
//// continue; throw new Exception($"The IDictionary type '{type.FullName}' does not support MoveNext.");
//// keys.Add(bucket.key);
//// values.Add(bucket.val); var keyEnumerator = getEnumeratorMethods[cacheKey].Invoke(keys, null);
//// } var keyInfo = new EnumeratorInfo
////} {
current = keyEnumerator.GetType().GetProperty("Current"),
moveNext = keyEnumerator.GetType().GetMethod("MoveNext"),
};
var values = type.GetProperty("Values").GetValue(dictionary, null);
var valueEnumerator = values.GetType().GetMethod("GetEnumerator").Invoke(values, null);
var valueInfo = new EnumeratorInfo
{
current = valueEnumerator.GetType().GetProperty("Current"),
moveNext = valueEnumerator.GetType().GetMethod("MoveNext"),
};
dictEnumerator = EnumerateCppDict(keyInfo, keyEnumerator, valueInfo, valueEnumerator);
return true;
}
catch //(Exception ex)
{
//ExplorerCore.LogWarning($"Exception enumerating IDictionary: {ex.ReflectionExToString()}");
dictEnumerator = null;
return false;
}
}
internal static IEnumerator<DictionaryEntry> EnumerateCppDict(EnumeratorInfo keyInfo, object keyEnumerator,
EnumeratorInfo valueInfo, object valueEnumerator)
{
while ((bool)keyInfo.moveNext.Invoke(keyEnumerator, null))
{
valueInfo.moveNext.Invoke(valueEnumerator, null);
var key = keyInfo.current.GetValue(keyEnumerator, null);
var value = valueInfo.current.GetValue(valueEnumerator, null);
yield return new DictionaryEntry(key, value);
}
}
internal static IEnumerator<DictionaryEntry> EnumerateCppHashTable(Il2CppSystem.Collections.Hashtable hashtable)
{
for (int i = 0; i < hashtable.buckets.Count; i++)
{
var bucket = hashtable.buckets[i];
if (bucket == null || bucket.key == null)
continue;
yield return new DictionaryEntry(bucket.key, bucket.val);
}
}
#endregion #endregion

View File

@ -458,28 +458,55 @@ namespace UnityExplorer
#endregion #endregion
// This is NOT working properly. Fails on IL2CPP IList, probably more stuff too. // Temp fix for IL2CPP until interface support improves
//// Temp fix for IL2CPP until interface support improves // IsEnumerable
//
//public static bool IsEnumerable(Type type) public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type);
//{
// return Instance.Internal_IsEnumerable(type); protected virtual bool Internal_IsEnumerable(Type type)
//} {
// return typeof(IEnumerable).IsAssignableFrom(type);
//public virtual bool Internal_IsEnumerable(Type type) }
//{
// return typeof(IEnumerable).IsAssignableFrom(type); // TryGetEnumerator (list)
//}
// public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
//public static bool IsDictionary(Type type) => Instance.Internal_TryGetEnumerator(list, out enumerator);
//{
// return Instance.Internal_IsDictionary(type); protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
//} {
// enumerator = (list as IEnumerable).GetEnumerator();
//public virtual bool Internal_IsDictionary(Type type) return true;
//{ }
// return typeof(IDictionary).IsAssignableFrom(type);
//} // IsDictionary
public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type);
protected virtual bool Internal_IsDictionary(Type type)
{
return typeof(IDictionary).IsAssignableFrom(type);
}
// TryGetEnumerator (dictionary)
public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
=> Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
{
dictEnumerator = EnumerateDictionary((IDictionary)dictionary);
return true;
}
private IEnumerator<DictionaryEntry> EnumerateDictionary(IDictionary dict)
{
var enumerator = dict.GetEnumerator();
while (enumerator.MoveNext())
{
yield return new DictionaryEntry(enumerator.Key, enumerator.Value);
}
}
} }
} }