mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-01-07 10:03:38 +08:00
IL2CPP reflection fixes and improvements
This commit is contained in:
parent
2fc9657560
commit
7dbf694642
@ -440,12 +440,6 @@ namespace UnityExplorer
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!moduleName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
|
||||
@ -458,7 +452,41 @@ namespace UnityExplorer
|
||||
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))
|
||||
return false;
|
||||
@ -470,16 +498,17 @@ namespace UnityExplorer
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.GetType() + ", " + e.Message);
|
||||
if (!suppressWarning)
|
||||
Console.WriteLine($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
|
||||
#region Il2cpp reflection blacklist
|
||||
#region Il2cpp reflection blacklist
|
||||
|
||||
public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist);
|
||||
|
||||
@ -625,191 +654,238 @@ namespace UnityExplorer
|
||||
"UnityEngine.XR.InputDevice.SendHapticImpulse",
|
||||
};
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
|
||||
// (Disabled)
|
||||
#region Temp il2cpp list/dictionary fixes
|
||||
#region Temp il2cpp list/dictionary fixes
|
||||
|
||||
//// Temp fix until Unhollower interface support improves
|
||||
//
|
||||
//internal static IntPtr s_cppEnumerableClassPtr;
|
||||
//internal static IntPtr s_cppDictionaryClassPtr;
|
||||
//
|
||||
//public override bool Internal_IsEnumerable(Type type)
|
||||
//{
|
||||
// if (base.Internal_IsEnumerable(type))
|
||||
// return true;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// if (s_cppEnumerableClassPtr == IntPtr.Zero)
|
||||
// Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr);
|
||||
//
|
||||
// if (s_cppEnumerableClassPtr != IntPtr.Zero
|
||||
// && Il2CppTypeNotNull(type, out IntPtr assignFromPtr)
|
||||
// && il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, assignFromPtr))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// catch { }
|
||||
//
|
||||
// return false;
|
||||
//}
|
||||
//
|
||||
//// Lists
|
||||
//
|
||||
//internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
|
||||
//
|
||||
//internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
|
||||
//
|
||||
//internal class EnumeratorInfo
|
||||
//{
|
||||
// internal MethodInfo moveNext;
|
||||
// internal PropertyInfo current;
|
||||
//}
|
||||
//
|
||||
//internal static IEnumerator EnumerateCppList(object list)
|
||||
//{
|
||||
// if (list == null)
|
||||
// yield break;
|
||||
//
|
||||
// var cppEnumerable = list.TryCast<Il2CppSystem.Collections.IEnumerable>();
|
||||
// if (cppEnumerable == null)
|
||||
// {
|
||||
// ExplorerCore.LogWarning("Failed to cast an IEnumerable to the interface!");
|
||||
// yield break;
|
||||
// }
|
||||
//
|
||||
// // Some ugly reflection to use the il2cpp interface for the instance type
|
||||
//
|
||||
// var type = cppEnumerable.GetActualType();
|
||||
// if (!s_getEnumeratorMethods.ContainsKey(type))
|
||||
// s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
|
||||
//
|
||||
// var enumerator = s_getEnumeratorMethods[type].Invoke(cppEnumerable.TryCast(type), null);
|
||||
// var enumeratorType = enumerator.GetType();
|
||||
//
|
||||
// if (!s_enumeratorInfos.ContainsKey(enumeratorType))
|
||||
// {
|
||||
// s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
|
||||
// {
|
||||
// current = enumeratorType.GetProperty("Current"),
|
||||
// moveNext = enumeratorType.GetMethod("MoveNext"),
|
||||
// });
|
||||
// }
|
||||
// var info = s_enumeratorInfos[enumeratorType];
|
||||
//
|
||||
// // Yield and return the actual entries
|
||||
// while ((bool)info.moveNext.Invoke(enumerator, null))
|
||||
// yield return info.current.GetValue(enumerator);
|
||||
//}
|
||||
//
|
||||
//// Dicts todo
|
||||
//
|
||||
//public override bool Internal_IsDictionary(Type type)
|
||||
//{
|
||||
// if (base.Internal_IsDictionary(type))
|
||||
// return true;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// if (s_cppDictionaryClassPtr == IntPtr.Zero)
|
||||
// if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr))
|
||||
// return false;
|
||||
//
|
||||
// if (Il2CppTypeNotNull(type, out IntPtr classPtr))
|
||||
// {
|
||||
// if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr))
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// catch { }
|
||||
//
|
||||
// return false;
|
||||
//}
|
||||
//
|
||||
//internal static IEnumerator<KeyValuePair<object, object>> EnumerateCppDictionary(object dictionary)
|
||||
//{
|
||||
// var cppDict = dictionary?.TryCast<Il2CppSystem.Collections.IDictionary>();
|
||||
// if (cppDict == null)
|
||||
// yield break;
|
||||
//
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
////public IDictionary EnumerateDictionary(object value)
|
||||
////{
|
||||
//// var valueType = value.GetActualType();
|
||||
////
|
||||
//// Type typeOfKeys, typeOfValues;
|
||||
//// if (valueType.IsGenericType && valueType.GetGenericArguments() is ParameterInfo[] args && args.Length == 2)
|
||||
//// {
|
||||
//// typeOfKeys = args[0];
|
||||
//// typeOfValues = args[1];
|
||||
//// }
|
||||
//// else
|
||||
//// typeOfKeys = typeOfValues = typeof(object);
|
||||
////
|
||||
//// var keyList = new List<object>();
|
||||
//// var valueList = new List<object>();
|
||||
////
|
||||
//// var hashtable = value.TryCast<Il2CppSystem.Collections.Hashtable>();
|
||||
////
|
||||
//// if (hashtable != null)
|
||||
//// {
|
||||
//// EnumerateCppHashtable(hashtable, keyList, valueList);
|
||||
//// }
|
||||
//// else
|
||||
//// {
|
||||
//// var keys = valueType.GetProperty("Keys").GetValue(value, null);
|
||||
//// EnumerateCppCollection(keys, keyList);
|
||||
////
|
||||
//// var values = valueType.GetProperty("Values").GetValue(value, null);
|
||||
//// EnumerateCppCollection(values, valueList);
|
||||
//// }
|
||||
////
|
||||
//// var dict = Activator.CreateInstance(typeof(Dictionary<,>)
|
||||
//// .MakeGenericType(typeOfKeys, typeOfValues))
|
||||
//// as IDictionary;
|
||||
////
|
||||
//// for (int i = 0; i < keyList.Count; i++)
|
||||
//// dict.Add(keyList[i], valueList[i]);
|
||||
////
|
||||
//// return dict;
|
||||
////}
|
||||
////
|
||||
////private void EnumerateCppCollection(object collection, List<object> list)
|
||||
////{
|
||||
//// // invoke GetEnumerator
|
||||
//// var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
|
||||
//// // get the type of it
|
||||
//// var enumeratorType = enumerator.GetType();
|
||||
//// // reflect MoveNext and Current
|
||||
//// var moveNext = enumeratorType.GetMethod("MoveNext");
|
||||
//// var current = enumeratorType.GetProperty("Current");
|
||||
//// // iterate
|
||||
//// while ((bool)moveNext.Invoke(enumerator, null))
|
||||
//// {
|
||||
//// list.Add(current.GetValue(enumerator, null));
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////private void EnumerateCppHashtable(Il2CppSystem.Collections.Hashtable hashtable, List<object> keys, List<object> values)
|
||||
////{
|
||||
//// for (int i = 0; i < hashtable.buckets.Count; i++)
|
||||
//// {
|
||||
//// var bucket = hashtable.buckets[i];
|
||||
//// if (bucket == null || bucket.key == null)
|
||||
//// continue;
|
||||
//// keys.Add(bucket.key);
|
||||
//// values.Add(bucket.val);
|
||||
//// }
|
||||
////}
|
||||
// Temp fix until Unhollower interface support improves
|
||||
|
||||
#endregion
|
||||
internal static readonly Dictionary<string, MethodInfo> getEnumeratorMethods = new Dictionary<string, MethodInfo>();
|
||||
internal static readonly Dictionary<string, EnumeratorInfo> enumeratorInfos = new Dictionary<string, EnumeratorInfo>();
|
||||
internal static readonly HashSet<string> notSupportedTypes = new HashSet<string>();
|
||||
|
||||
// IEnumerables
|
||||
|
||||
internal static IntPtr cppIEnumerablePointer;
|
||||
|
||||
protected override bool Internal_IsEnumerable(Type type)
|
||||
{
|
||||
if (base.Internal_IsEnumerable(type))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
if (cppIEnumerablePointer == IntPtr.Zero)
|
||||
Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out cppIEnumerablePointer);
|
||||
|
||||
if (cppIEnumerablePointer != IntPtr.Zero
|
||||
&& Il2CppTypeNotNull(type, out IntPtr assignFromPtr)
|
||||
&& il2cpp_class_is_assignable_from(cppIEnumerablePointer, assignFromPtr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal class EnumeratorInfo
|
||||
{
|
||||
internal MethodInfo moveNext;
|
||||
internal PropertyInfo current;
|
||||
}
|
||||
|
||||
protected override bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
|
||||
{
|
||||
if (list is IEnumerable)
|
||||
return base.Internal_TryGetEnumerator(list, out enumerator);
|
||||
|
||||
try
|
||||
{
|
||||
PrepareCppEnumerator(list, out object cppEnumerator, out EnumeratorInfo info);
|
||||
enumerator = EnumerateCppList(info, cppEnumerator);
|
||||
return true;
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//ExplorerCore.LogWarning($"Exception enumerating IEnumerable: {ex.ReflectionExToString()}");
|
||||
enumerator = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrepareCppEnumerator(object list, out object cppEnumerator, out EnumeratorInfo info)
|
||||
{
|
||||
info = null;
|
||||
cppEnumerator = null;
|
||||
if (list == null)
|
||||
throw new ArgumentNullException("list");
|
||||
|
||||
// Some ugly reflection to use the il2cpp interface for the instance type
|
||||
|
||||
var type = list.GetType();
|
||||
var key = type.AssemblyQualifiedName;
|
||||
|
||||
if (!getEnumeratorMethods.ContainsKey(key))
|
||||
{
|
||||
getEnumeratorMethods.Add(key, type.GetMethod("GetEnumerator"));
|
||||
|
||||
// ensure the enumerator type is supported
|
||||
try
|
||||
{
|
||||
var test = getEnumeratorMethods[key].Invoke(list, null);
|
||||
test.GetType().GetMethod("MoveNext").Invoke(test, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
notSupportedTypes.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (notSupportedTypes.Contains(key))
|
||||
throw new NotSupportedException($"The IEnumerable type '{type.FullName}' does not support MoveNext.");
|
||||
|
||||
cppEnumerator = getEnumeratorMethods[key].Invoke(list, null);
|
||||
var enumeratorType = cppEnumerator.GetType();
|
||||
|
||||
var enumInfoKey = enumeratorType.AssemblyQualifiedName;
|
||||
|
||||
if (!enumeratorInfos.ContainsKey(enumInfoKey))
|
||||
{
|
||||
enumeratorInfos.Add(enumInfoKey, new EnumeratorInfo
|
||||
{
|
||||
current = enumeratorType.GetProperty("Current"),
|
||||
moveNext = enumeratorType.GetMethod("MoveNext"),
|
||||
});
|
||||
}
|
||||
|
||||
info = enumeratorInfos[enumInfoKey];
|
||||
}
|
||||
|
||||
internal static IEnumerator EnumerateCppList(EnumeratorInfo info, object enumerator)
|
||||
{
|
||||
// Yield and return the actual entries
|
||||
while ((bool)info.moveNext.Invoke(enumerator, null))
|
||||
yield return info.current.GetValue(enumerator);
|
||||
}
|
||||
|
||||
// IDictionary
|
||||
|
||||
internal static IntPtr cppIDictionaryPointer;
|
||||
|
||||
protected override bool Internal_IsDictionary(Type type)
|
||||
{
|
||||
if (base.Internal_IsDictionary(type))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
if (cppIDictionaryPointer == IntPtr.Zero)
|
||||
if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out cppIDictionaryPointer))
|
||||
return false;
|
||||
|
||||
if (Il2CppTypeNotNull(type, out IntPtr classPtr)
|
||||
&& il2cpp_class_is_assignable_from(cppIDictionaryPointer, classPtr))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
|
||||
{
|
||||
if (dictionary is IDictionary)
|
||||
return base.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
|
||||
|
||||
try
|
||||
{
|
||||
var type = dictionary.GetType();
|
||||
|
||||
if (typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(type))
|
||||
{
|
||||
dictEnumerator = EnumerateCppHashTable(dictionary.TryCast<Il2CppSystem.Collections.Hashtable>());
|
||||
return true;
|
||||
}
|
||||
|
||||
var keys = type.GetProperty("Keys").GetValue(dictionary, null);
|
||||
|
||||
var keyCollType = keys.GetType();
|
||||
var cacheKey = keys.GetType().AssemblyQualifiedName;
|
||||
if (!getEnumeratorMethods.ContainsKey(cacheKey))
|
||||
{
|
||||
getEnumeratorMethods.Add(cacheKey, keyCollType.GetMethod("GetEnumerator"));
|
||||
|
||||
// test support
|
||||
try
|
||||
{
|
||||
var test = getEnumeratorMethods[cacheKey].Invoke(keys, null);
|
||||
test.GetType().GetMethod("MoveNext").Invoke(test, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
notSupportedTypes.Add(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (notSupportedTypes.Contains(cacheKey))
|
||||
throw new Exception($"The IDictionary type '{type.FullName}' does not support MoveNext.");
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -458,28 +458,55 @@ namespace UnityExplorer
|
||||
#endregion
|
||||
|
||||
|
||||
// This is NOT working properly. Fails on IL2CPP IList, probably more stuff too.
|
||||
// Temp fix for IL2CPP until interface support improves
|
||||
|
||||
// IsEnumerable
|
||||
|
||||
//// Temp fix for IL2CPP until interface support improves
|
||||
//
|
||||
//public static bool IsEnumerable(Type type)
|
||||
//{
|
||||
// return Instance.Internal_IsEnumerable(type);
|
||||
//}
|
||||
//
|
||||
//public virtual bool Internal_IsEnumerable(Type type)
|
||||
//{
|
||||
// return typeof(IEnumerable).IsAssignableFrom(type);
|
||||
//}
|
||||
//
|
||||
//public static bool IsDictionary(Type type)
|
||||
//{
|
||||
// return Instance.Internal_IsDictionary(type);
|
||||
//}
|
||||
//
|
||||
//public virtual bool Internal_IsDictionary(Type type)
|
||||
//{
|
||||
// return typeof(IDictionary).IsAssignableFrom(type);
|
||||
//}
|
||||
public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type);
|
||||
|
||||
protected virtual bool Internal_IsEnumerable(Type type)
|
||||
{
|
||||
return typeof(IEnumerable).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
// TryGetEnumerator (list)
|
||||
|
||||
public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
|
||||
=> Instance.Internal_TryGetEnumerator(list, out enumerator);
|
||||
|
||||
protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
|
||||
{
|
||||
enumerator = (list as IEnumerable).GetEnumerator();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user